From 413ec4b5dbe6fc8d16c71fde5c909b41e2a301fa Mon Sep 17 00:00:00 2001 From: jonnyandrew Date: Fri, 3 Nov 2023 12:59:36 +0000 Subject: [PATCH] Display duration of recorded voice message (#1733) --------- Co-authored-by: ElementBot --- .../composer/VoiceMessageComposerPlayer.kt | 6 ++- .../composer/VoiceMessageComposerPresenter.kt | 29 ++++++++++-- .../VoiceMessageComposerPresenterTest.kt | 46 +++++++++++++------ .../libraries/textcomposer/TextComposer.kt | 4 ++ .../components/VoiceMessagePreview.kt | 30 ++++++++++-- .../textcomposer/model/VoiceMessageState.kt | 1 + .../voicerecorder/api/VoiceRecorderState.kt | 2 + .../voicerecorder/impl/VoiceRecorderImpl.kt | 17 ++++--- .../impl/VoiceRecorderImplTest.kt | 15 ++++-- .../voicerecorder/test/FakeVoiceRecorder.kt | 1 + ...oserVoice-Day-4_4_null,NEXUS_5,1.0,en].png | 4 +- ...erVoice-Night-4_5_null,NEXUS_5,1.0,en].png | 4 +- 12 files changed, 119 insertions(+), 40 deletions(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/composer/VoiceMessageComposerPlayer.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/composer/VoiceMessageComposerPlayer.kt index a0bca22fda..1d98fd38da 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/composer/VoiceMessageComposerPlayer.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/composer/VoiceMessageComposerPlayer.kt @@ -36,7 +36,7 @@ class VoiceMessageComposerPlayer @Inject constructor( val state: Flow = mediaPlayer.state.map { state -> if (lastPlayedMediaPath == null || lastPlayedMediaPath != state.mediaId) { - return@map State.NotPlaying + return@map State.NotLoaded } State( @@ -89,13 +89,15 @@ class VoiceMessageComposerPlayer @Inject constructor( val duration: Long, ) { companion object { - val NotPlaying = State( + val NotLoaded = State( isPlaying = false, currentPosition = 0L, duration = 0L, ) } + val isLoaded get() = this != NotLoaded + /** * The progress of this player between 0 and 1. */ diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/composer/VoiceMessageComposerPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/composer/VoiceMessageComposerPresenter.kt index 9d1a9189dd..313ebfb3f6 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/composer/VoiceMessageComposerPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/composer/VoiceMessageComposerPresenter.kt @@ -42,14 +42,15 @@ import io.element.android.libraries.textcomposer.model.VoiceMessageState import io.element.android.libraries.voicerecorder.api.VoiceRecorder import io.element.android.libraries.voicerecorder.api.VoiceRecorderState import io.element.android.services.analytics.api.AnalyticsService -import kotlinx.collections.immutable.toPersistentList import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.toImmutableList +import kotlinx.collections.immutable.toPersistentList import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch import timber.log.Timber import java.io.File import javax.inject.Inject +import kotlin.time.Duration import kotlin.time.Duration.Companion.milliseconds @SingleIn(RoomScope::class) @@ -72,8 +73,8 @@ class VoiceMessageComposerPresenter @Inject constructor( val permissionState = permissionsPresenter.present() var isSending by remember { mutableStateOf(false) } - val playerState by player.state.collectAsState(initial = VoiceMessageComposerPlayer.State.NotPlaying) - val isPlaying by remember(playerState.isPlaying) { derivedStateOf { playerState.isPlaying } } + val playerState by player.state.collectAsState(initial = VoiceMessageComposerPlayer.State.NotLoaded) + val playerTime by remember(playerState, recorderState) { derivedStateOf { displayTime(playerState, recorderState) } } val waveform by remember(recorderState) { derivedStateOf { recorderState.finishedWaveform() } } val onLifecycleEvent = { event: Lifecycle.Event -> @@ -190,9 +191,10 @@ class VoiceMessageComposerPresenter @Inject constructor( ) is VoiceRecorderState.Finished -> VoiceMessageState.Preview( isSending = isSending, - isPlaying = isPlaying, + isPlaying = playerState.isPlaying, + showCursor = playerState.isLoaded && !isSending, playbackProgress = playerState.progress, - time = playerState.currentPosition.milliseconds, + time = playerTime, waveform = waveform, ) else -> VoiceMessageState.Idle @@ -259,3 +261,20 @@ private fun VoiceRecorderState.finishedWaveform(): ImmutableList = ?.waveform .orEmpty() .toImmutableList() + +/** + * The time to display depending on the player state. + * + * Either the current position or total duration. + */ +private fun displayTime( + playerState: VoiceMessageComposerPlayer.State, + recording: VoiceRecorderState +): Duration = when { + playerState.isLoaded -> + playerState.currentPosition.milliseconds + recording is VoiceRecorderState.Finished -> + recording.duration + else -> + 0.milliseconds +} diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/voicemessages/composer/VoiceMessageComposerPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/voicemessages/composer/VoiceMessageComposerPresenterTest.kt index da33a70bcc..87606d0272 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/voicemessages/composer/VoiceMessageComposerPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/voicemessages/composer/VoiceMessageComposerPresenterTest.kt @@ -179,7 +179,7 @@ class VoiceMessageComposerPresenterTest { } // Nothing should happen - assertThat(finalState.voiceMessageState).isEqualTo(VoiceMessageState.Recording(RECORDING_DURATION, RECORDING_STATE.levels)) + assertThat(finalState.voiceMessageState).isEqualTo(RECORDING_STATE) voiceRecorder.assertCalls(started = 1, stopped = 0, deleted = 0) testPauseAndDestroy(finalState) @@ -196,7 +196,7 @@ class VoiceMessageComposerPresenterTest { awaitItem().eventSink(VoiceMessageComposerEvents.RecordButtonEvent(PressEvent.LongPressEnd)) awaitItem().eventSink(VoiceMessageComposerEvents.PlayerEvent(VoiceMessagePlayerEvent.Play)) val finalState = awaitItem().also { - assertThat(it.voiceMessageState).isEqualTo(aPreviewState(isPlaying = true, playbackProgress = 0.1f, time = RECORDING_DURATION)) + assertThat(it.voiceMessageState).isEqualTo(aPlayingState()) } voiceRecorder.assertCalls(started = 1, stopped = 1, deleted = 0) @@ -215,7 +215,7 @@ class VoiceMessageComposerPresenterTest { awaitItem().eventSink(VoiceMessageComposerEvents.PlayerEvent(VoiceMessagePlayerEvent.Play)) awaitItem().eventSink(VoiceMessageComposerEvents.PlayerEvent(VoiceMessagePlayerEvent.Pause)) val finalState = awaitItem().also { - assertThat(it.voiceMessageState).isEqualTo(aPreviewState(isPlaying = false, playbackProgress = 0.1f, time = RECORDING_DURATION)) + assertThat(it.voiceMessageState).isEqualTo(aPausedState()) } voiceRecorder.assertCalls(started = 1, stopped = 1, deleted = 0) @@ -252,7 +252,7 @@ class VoiceMessageComposerPresenterTest { awaitItem().eventSink(VoiceMessageComposerEvents.PlayerEvent(VoiceMessagePlayerEvent.Play)) awaitItem().eventSink(VoiceMessageComposerEvents.DeleteVoiceMessage) awaitItem().apply { - assertThat(voiceMessageState).isEqualTo(aPreviewState(isPlaying = false, playbackProgress = 0.1f, time = RECORDING_DURATION)) + assertThat(voiceMessageState).isEqualTo(aPausedState()) } val finalState = awaitItem() @@ -272,7 +272,7 @@ class VoiceMessageComposerPresenterTest { awaitItem().eventSink(VoiceMessageComposerEvents.RecordButtonEvent(PressEvent.PressStart)) awaitItem().eventSink(VoiceMessageComposerEvents.RecordButtonEvent(PressEvent.LongPressEnd)) awaitItem().eventSink(VoiceMessageComposerEvents.SendVoiceMessage) - assertThat(awaitItem().voiceMessageState).isEqualTo(aPreviewState(isSending = true)) + assertThat(awaitItem().voiceMessageState).isEqualTo(aPreviewState().toSendingState()) val finalState = awaitItem() assertThat(finalState.voiceMessageState).isEqualTo(VoiceMessageState.Idle) @@ -322,11 +322,7 @@ class VoiceMessageComposerPresenterTest { awaitItem().eventSink(VoiceMessageComposerEvents.RecordButtonEvent(PressEvent.LongPressEnd)) awaitItem().eventSink(VoiceMessageComposerEvents.PlayerEvent(VoiceMessagePlayerEvent.Play)) awaitItem().eventSink(VoiceMessageComposerEvents.SendVoiceMessage) - assertThat(awaitItem().voiceMessageState).isEqualTo( - aPreviewState( - isSending = true, isPlaying = false, playbackProgress = 0.1f, time = RECORDING_DURATION - ) - ) + assertThat(awaitItem().voiceMessageState).isEqualTo(aPlayingState().toSendingState()) val finalState = awaitItem() assertThat(finalState.voiceMessageState).isEqualTo(VoiceMessageState.Idle) @@ -349,7 +345,7 @@ class VoiceMessageComposerPresenterTest { eventSink(VoiceMessageComposerEvents.SendVoiceMessage) eventSink(VoiceMessageComposerEvents.SendVoiceMessage) } - assertThat(awaitItem().voiceMessageState).isEqualTo(aPreviewState(isSending = true)) + assertThat(awaitItem().voiceMessageState).isEqualTo(aPreviewState().toSendingState()) val finalState = awaitItem() assertThat(finalState.voiceMessageState).isEqualTo(VoiceMessageState.Idle) @@ -398,7 +394,7 @@ class VoiceMessageComposerPresenterTest { val previewState = awaitItem() previewState.eventSink(VoiceMessageComposerEvents.SendVoiceMessage) - assertThat(awaitItem().voiceMessageState).isEqualTo(aPreviewState(isSending = true)) + assertThat(awaitItem().voiceMessageState).isEqualTo(aPreviewState().toSendingState()) ensureAllEventsConsumed() assertThat(previewState.voiceMessageState).isEqualTo(aPreviewState()) @@ -573,7 +569,7 @@ class VoiceMessageComposerPresenterTest { is VoiceMessageState.Preview -> when (state.isPlaying) { // If the preview was playing, it pauses true -> awaitItem().apply { - assertThat(voiceMessageState).isEqualTo(aPreviewState(playbackProgress = 0.1f, time = RECORDING_DURATION)) + assertThat(voiceMessageState).isEqualTo(aPausedState()) } false -> mostRecentState } @@ -627,15 +623,37 @@ class VoiceMessageComposerPresenterTest { isPlaying: Boolean = false, playbackProgress: Float = 0f, isSending: Boolean = false, - time: Duration = 0.seconds, + time: Duration = RECORDING_DURATION, + showCursor: Boolean = false, waveform: List = voiceRecorder.waveform, ) = VoiceMessageState.Preview( isPlaying = isPlaying, playbackProgress = playbackProgress, isSending = isSending, time = time, + showCursor = showCursor, waveform = waveform.toImmutableList(), ) + + private fun aPlayingState() = + aPreviewState( + isPlaying = true, + playbackProgress = 0.1f, + showCursor = true, + time = RECORDING_DURATION, + ) + + private fun aPausedState() = + aPlayingState() + .copy(isPlaying = false) + + private fun VoiceMessageState.Preview.toSendingState() = + copy( + isPlaying = false, + isSending = true, + showCursor = false, + time = RECORDING_DURATION, + ) } private fun aReplyMode() = MessageComposerMode.Reply(A_USER_NAME, null, false, AN_EVENT_ID, A_MESSAGE) diff --git a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt index f1698a6cb3..4e383dbfda 100644 --- a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt +++ b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/TextComposer.kt @@ -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 diff --git a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/VoiceMessagePreview.kt b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/VoiceMessagePreview.kt index c44469a501..7197c07307 100644 --- a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/VoiceMessagePreview.kt +++ b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/VoiceMessagePreview.kt @@ -55,6 +55,7 @@ import kotlin.time.Duration.Companion.seconds internal fun VoiceMessagePreview( isInteractive: Boolean, isPlaying: Boolean, + showCursor: Boolean, waveform: ImmutableList, 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() + ) } } diff --git a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/model/VoiceMessageState.kt b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/model/VoiceMessageState.kt index db8d75afdd..6cf3166f68 100644 --- a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/model/VoiceMessageState.kt +++ b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/model/VoiceMessageState.kt @@ -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, diff --git a/libraries/voicerecorder/api/src/main/kotlin/io/element/android/libraries/voicerecorder/api/VoiceRecorderState.kt b/libraries/voicerecorder/api/src/main/kotlin/io/element/android/libraries/voicerecorder/api/VoiceRecorderState.kt index 6f7ac54f5e..04c532e86b 100644 --- a/libraries/voicerecorder/api/src/main/kotlin/io/element/android/libraries/voicerecorder/api/VoiceRecorderState.kt +++ b/libraries/voicerecorder/api/src/main/kotlin/io/element/android/libraries/voicerecorder/api/VoiceRecorderState.kt @@ -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, + val duration: Duration, ) : VoiceRecorderState() } diff --git a/libraries/voicerecorder/impl/src/main/kotlin/io/element/android/libraries/voicerecorder/impl/VoiceRecorderImpl.kt b/libraries/voicerecorder/impl/src/main/kotlin/io/element/android/libraries/voicerecorder/impl/VoiceRecorderImpl.kt index 89e6f9186b..042ecacfbe 100644 --- a/libraries/voicerecorder/impl/src/main/kotlin/io/element/android/libraries/voicerecorder/impl/VoiceRecorderImpl.kt +++ b/libraries/voicerecorder/impl/src/main/kotlin/io/element/android/libraries/voicerecorder/impl/VoiceRecorderImpl.kt @@ -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 + ) + } } ) } diff --git a/libraries/voicerecorder/impl/src/test/kotlin/io/element/android/libraries/voicerecorder/impl/VoiceRecorderImplTest.kt b/libraries/voicerecorder/impl/src/test/kotlin/io/element/android/libraries/voicerecorder/impl/VoiceRecorderImplTest.kt index b764dc92f7..e3b2100ccc 100644 --- a/libraries/voicerecorder/impl/src/test/kotlin/io/element/android/libraries/voicerecorder/impl/VoiceRecorderImplTest.kt +++ b/libraries/voicerecorder/impl/src/test/kotlin/io/element/android/libraries/voicerecorder/impl/VoiceRecorderImplTest.kt @@ -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) diff --git a/libraries/voicerecorder/test/src/main/kotlin/io/element/android/libraries/voicerecorder/test/FakeVoiceRecorder.kt b/libraries/voicerecorder/test/src/main/kotlin/io/element/android/libraries/voicerecorder/test/FakeVoiceRecorder.kt index d25bdd7263..bc8bda59d1 100644 --- a/libraries/voicerecorder/test/src/main/kotlin/io/element/android/libraries/voicerecorder/test/FakeVoiceRecorder.kt +++ b/libraries/voicerecorder/test/src/main/kotlin/io/element/android/libraries/voicerecorder/test/FakeVoiceRecorder.kt @@ -74,6 +74,7 @@ class FakeVoiceRecorder( else -> VoiceRecorderState.Finished( file = curRecording!!, mimeType = "audio/ogg", + duration = recordingDuration, waveform = waveform, ) } diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_null_TextComposerVoice-Day-4_4_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_null_TextComposerVoice-Day-4_4_null,NEXUS_5,1.0,en].png index e565325253..f5ba129fd3 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_null_TextComposerVoice-Day-4_4_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_null_TextComposerVoice-Day-4_4_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c692c6d70f847d37dce6c1cdbe630621cc6bdf37d48e24b4f95aa79d9422f15d -size 28039 +oid sha256:991416dcd21ebf8e2d57baac7cb42506596a1a346dd7d643b09c84c473e053b7 +size 28016 diff --git a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_null_TextComposerVoice-Night-4_5_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_null_TextComposerVoice-Night-4_5_null,NEXUS_5,1.0,en].png index e9afc557c3..8bd3ded996 100644 --- a/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_null_TextComposerVoice-Night-4_5_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/ui_S_t[l.textcomposer_null_TextComposerVoice-Night-4_5_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2f9b34504384622feed1348a00172d520123512c433973094634d159850a7653 -size 27124 +oid sha256:a8b8c60473ce1b9e7451e763c960a9242bb83ca1d3769ee921db830ff6e57e7c +size 27094