diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/VoiceMessageException.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/VoiceMessageException.kt index 2020b687ae..7ca95ad9a7 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/VoiceMessageException.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/VoiceMessageException.kt @@ -23,4 +23,7 @@ internal sealed class VoiceMessageException : Exception() { data class PermissionMissing( override val message: String?, override val cause: Throwable? ) : VoiceMessageException() + data class PlayMessageError( + override val message: String?, override val cause: Throwable? + ) : VoiceMessageException() } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/VoiceMessagePresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/VoiceMessagePresenter.kt index 6bff6ef26f..1bbdedc22d 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/VoiceMessagePresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/timeline/VoiceMessagePresenter.kt @@ -33,11 +33,13 @@ import dagger.multibindings.IntoMap import io.element.android.features.messages.impl.timeline.di.TimelineItemEventContentKey import io.element.android.features.messages.impl.timeline.di.TimelineItemPresenterFactory import io.element.android.features.messages.impl.timeline.model.event.TimelineItemVoiceContent +import io.element.android.features.messages.impl.voicemessages.VoiceMessageException import io.element.android.libraries.architecture.Async import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.architecture.runUpdatingState import io.element.android.libraries.di.RoomScope import io.element.android.libraries.ui.utils.time.formatShort +import io.element.android.services.analytics.api.AnalyticsService import kotlinx.coroutines.launch import kotlin.time.Duration.Companion.milliseconds @@ -52,6 +54,7 @@ interface VoiceMessagePresenterModule { class VoiceMessagePresenter @AssistedInject constructor( voiceMessagePlayerFactory: VoiceMessagePlayer.Factory, + private val analyticsService: AnalyticsService, @Assisted private val content: TimelineItemVoiceContent, ) : Presenter { @@ -102,7 +105,18 @@ class VoiceMessagePresenter @AssistedInject constructor( if (playerState.isPlaying) { player.pause() } else { - scope.launch { play.runUpdatingState { player.play() } } + scope.launch { + play.runUpdatingState( + errorTransform = { + analyticsService.trackError( + VoiceMessageException.PlayMessageError("Error while trying to play voice message", it) + ) + it + }, + ) { + player.play() + } + } } } is VoiceMessageEvents.Seek -> { diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/voicemessages/timeline/VoiceMessagePresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/voicemessages/timeline/VoiceMessagePresenterTest.kt index e735dd4f28..257174ec8d 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/voicemessages/timeline/VoiceMessagePresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/voicemessages/timeline/VoiceMessagePresenterTest.kt @@ -22,12 +22,15 @@ import app.cash.turbine.test import com.google.common.truth.Truth import io.element.android.features.messages.impl.timeline.model.event.TimelineItemVoiceContent import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemVoiceContent +import io.element.android.features.messages.impl.voicemessages.VoiceMessageException import io.element.android.features.messages.impl.voicemessages.timeline.DefaultVoiceMessagePlayer import io.element.android.features.messages.impl.voicemessages.timeline.VoiceMessageEvents import io.element.android.features.messages.impl.voicemessages.timeline.VoiceMessageMediaRepo import io.element.android.features.messages.impl.voicemessages.timeline.VoiceMessagePresenter import io.element.android.features.messages.impl.voicemessages.timeline.VoiceMessageState import io.element.android.libraries.mediaplayer.test.FakeMediaPlayer +import io.element.android.services.analytics.api.AnalyticsService +import io.element.android.services.analytics.test.FakeAnalyticsService import kotlinx.coroutines.test.runTest import org.junit.Test @@ -77,8 +80,10 @@ class VoiceMessagePresenterTest { @Test fun `pressing play downloads and fails`() = runTest { + val analyticsService = FakeAnalyticsService() val presenter = createVoiceMessagePresenter( voiceMessageMediaRepo = FakeVoiceMessageMediaRepo().apply { shouldFail = true }, + analyticsService = analyticsService, content = aTimelineItemVoiceContent(durationMs = 2_000), ) moleculeFlow(RecompositionMode.Immediate) { @@ -102,6 +107,9 @@ class VoiceMessagePresenterTest { Truth.assertThat(it.progress).isEqualTo(0f) Truth.assertThat(it.time).isEqualTo("0:02") } + analyticsService.trackedErrors.first().also { + Truth.assertThat(it).isInstanceOf(VoiceMessageException.PlayMessageError::class.java) + } } } @@ -190,6 +198,7 @@ class VoiceMessagePresenterTest { fun createVoiceMessagePresenter( voiceMessageMediaRepo: VoiceMessageMediaRepo = FakeVoiceMessageMediaRepo(), + analyticsService: AnalyticsService = FakeAnalyticsService(), content: TimelineItemVoiceContent = aTimelineItemVoiceContent(), ) = VoiceMessagePresenter( voiceMessagePlayerFactory = { eventId, mediaSource, mimeType, body -> @@ -202,5 +211,6 @@ fun createVoiceMessagePresenter( body = body ) }, + analyticsService = analyticsService, content = content, )