Browse Source

Add analytics for voice messages (#1706)

pull/1723/head
jonnyandrew 11 months ago committed by GitHub
parent
commit
dab5e0d0ca
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 10
      features/location/impl/src/test/kotlin/io/element/android/features/location/impl/send/SendLocationPresenterTest.kt
  2. 1
      features/messages/impl/build.gradle.kts
  3. 14
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/composer/VoiceMessageComposerPresenter.kt
  4. 2
      features/messages/impl/src/test/kotlin/io/element/android/features/messages/MessagesPresenterTest.kt
  5. 49
      features/messages/impl/src/test/kotlin/io/element/android/features/messages/voicemessages/composer/VoiceMessageComposerPresenterTest.kt
  6. 2
      features/messages/test/src/main/kotlin/io/element/android/features/messages/test/FakeMessageComposerContext.kt
  7. 6
      features/poll/impl/src/test/kotlin/io/element/android/features/poll/impl/create/CreatePollPresenterTest.kt
  8. 2
      gradle/libs.versions.toml

10
features/location/impl/src/test/kotlin/io/element/android/features/location/impl/send/SendLocationPresenterTest.kt

@ -28,7 +28,7 @@ import io.element.android.features.location.impl.common.permissions.PermissionsE @@ -28,7 +28,7 @@ import io.element.android.features.location.impl.common.permissions.PermissionsE
import io.element.android.features.location.impl.common.permissions.PermissionsPresenter
import io.element.android.features.location.impl.common.permissions.PermissionsPresenterFake
import io.element.android.features.location.impl.common.permissions.PermissionsState
import io.element.android.features.messages.test.MessageComposerContextFake
import io.element.android.features.messages.test.FakeMessageComposerContext
import io.element.android.libraries.matrix.api.room.location.AssetType
import io.element.android.libraries.matrix.test.core.aBuildMeta
import io.element.android.libraries.matrix.test.room.FakeMatrixRoom
@ -49,7 +49,7 @@ class SendLocationPresenterTest { @@ -49,7 +49,7 @@ class SendLocationPresenterTest {
private val permissionsPresenterFake = PermissionsPresenterFake()
private val fakeMatrixRoom = FakeMatrixRoom()
private val fakeAnalyticsService = FakeAnalyticsService()
private val messageComposerContextFake = MessageComposerContextFake()
private val fakeMessageComposerContext = FakeMessageComposerContext()
private val fakeLocationActions = FakeLocationActions()
private val fakeBuildMeta = aBuildMeta(applicationName = "app name")
private val sendLocationPresenter: SendLocationPresenter = SendLocationPresenter(
@ -58,7 +58,7 @@ class SendLocationPresenterTest { @@ -58,7 +58,7 @@ class SendLocationPresenterTest {
},
room = fakeMatrixRoom,
analyticsService = fakeAnalyticsService,
messageComposerContext = messageComposerContextFake,
messageComposerContext = fakeMessageComposerContext,
locationActions = fakeLocationActions,
buildMeta = fakeBuildMeta,
)
@ -379,7 +379,7 @@ class SendLocationPresenterTest { @@ -379,7 +379,7 @@ class SendLocationPresenterTest {
shouldShowRationale = false,
)
)
messageComposerContextFake.apply {
fakeMessageComposerContext.apply {
composerMode = MessageComposerMode.Edit(
eventId = null, defaultContent = "", transactionId = null
)
@ -425,7 +425,7 @@ class SendLocationPresenterTest { @@ -425,7 +425,7 @@ class SendLocationPresenterTest {
shouldShowRationale = false,
)
)
messageComposerContextFake.apply {
fakeMessageComposerContext.apply {
composerMode = MessageComposerMode.Edit(
eventId = null, defaultContent = "", transactionId = null
)

1
features/messages/impl/build.gradle.kts

@ -75,6 +75,7 @@ dependencies { @@ -75,6 +75,7 @@ dependencies {
testImplementation(projects.libraries.matrix.test)
testImplementation(projects.libraries.dateformatter.test)
testImplementation(projects.features.networkmonitor.test)
testImplementation(projects.features.messages.test)
testImplementation(projects.services.analytics.test)
testImplementation(projects.tests.testutils)
testImplementation(projects.libraries.featureflag.test)

14
features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/composer/VoiceMessageComposerPresenter.kt

@ -27,6 +27,8 @@ import androidx.compose.runtime.rememberCoroutineScope @@ -27,6 +27,8 @@ import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.core.net.toUri
import androidx.lifecycle.Lifecycle
import im.vector.app.features.analytics.plan.Composer
import io.element.android.features.messages.api.MessageComposerContext
import io.element.android.features.messages.impl.voicemessages.VoiceMessageException
import io.element.android.libraries.architecture.Presenter
import io.element.android.libraries.di.RoomScope
@ -56,6 +58,7 @@ class VoiceMessageComposerPresenter @Inject constructor( @@ -56,6 +58,7 @@ class VoiceMessageComposerPresenter @Inject constructor(
private val analyticsService: AnalyticsService,
private val mediaSender: MediaSender,
private val player: VoiceMessageComposerPlayer,
private val messageComposerContext: MessageComposerContext,
permissionsPresenterFactory: PermissionsPresenter.Factory
) : Presenter<VoiceMessageComposerState> {
private val permissionsPresenter = permissionsPresenterFactory.create(Manifest.permission.RECORD_AUDIO)
@ -151,6 +154,7 @@ class VoiceMessageComposerPresenter @Inject constructor( @@ -151,6 +154,7 @@ class VoiceMessageComposerPresenter @Inject constructor(
}
isSending = true
player.pause()
analyticsService.captureComposerEvent()
appCoroutineScope.sendMessage(
file = finishedState.file,
mimeType = finishedState.mimeType,
@ -236,6 +240,16 @@ class VoiceMessageComposerPresenter @Inject constructor( @@ -236,6 +240,16 @@ class VoiceMessageComposerPresenter @Inject constructor(
voiceRecorder.deleteRecording()
}
private fun AnalyticsService.captureComposerEvent() =
analyticsService.capture(
Composer(
inThread = messageComposerContext.composerMode.inThread,
isEditing = messageComposerContext.composerMode.isEditing,
isReply = messageComposerContext.composerMode.isReply,
messageType = Composer.MessageType.VoiceMessage,
)
)
}
private fun VoiceRecorderState.finishedWaveform(): ImmutableList<Float> =

2
features/messages/impl/src/test/kotlin/io/element/android/features/messages/MessagesPresenterTest.kt

@ -43,6 +43,7 @@ import io.element.android.features.messages.impl.timeline.model.event.TimelineIt @@ -43,6 +43,7 @@ import io.element.android.features.messages.impl.timeline.model.event.TimelineIt
import io.element.android.features.messages.impl.voicemessages.composer.VoiceMessageComposerPlayer
import io.element.android.features.messages.impl.voicemessages.composer.VoiceMessageComposerPresenter
import io.element.android.features.messages.media.FakeLocalMediaFactory
import io.element.android.features.messages.test.FakeMessageComposerContext
import io.element.android.features.messages.textcomposer.TestRichTextEditorStateFactory
import io.element.android.features.messages.timeline.components.customreaction.FakeEmojibaseProvider
import io.element.android.features.messages.utils.messagesummary.FakeMessageSummaryFormatter
@ -641,6 +642,7 @@ class MessagesPresenterTest { @@ -641,6 +642,7 @@ class MessagesPresenterTest {
analyticsService,
mediaSender,
player = VoiceMessageComposerPlayer(FakeMediaPlayer()),
messageComposerContext = FakeMessageComposerContext(),
permissionsPresenterFactory,
)
val timelinePresenter = TimelinePresenter(

49
features/messages/impl/src/test/kotlin/io/element/android/features/messages/voicemessages/composer/VoiceMessageComposerPresenterTest.kt

@ -25,11 +25,16 @@ import app.cash.molecule.moleculeFlow @@ -25,11 +25,16 @@ import app.cash.molecule.moleculeFlow
import app.cash.turbine.TurbineTestContext
import app.cash.turbine.test
import com.google.common.truth.Truth.assertThat
import im.vector.app.features.analytics.plan.Composer
import io.element.android.features.messages.impl.voicemessages.VoiceMessageException
import io.element.android.features.messages.impl.voicemessages.composer.VoiceMessageComposerEvents
import io.element.android.features.messages.impl.voicemessages.composer.VoiceMessageComposerPlayer
import io.element.android.features.messages.impl.voicemessages.composer.VoiceMessageComposerPresenter
import io.element.android.features.messages.impl.voicemessages.composer.VoiceMessageComposerState
import io.element.android.features.messages.test.FakeMessageComposerContext
import io.element.android.libraries.matrix.test.AN_EVENT_ID
import io.element.android.libraries.matrix.test.A_MESSAGE
import io.element.android.libraries.matrix.test.A_USER_NAME
import io.element.android.libraries.mediaplayer.test.FakeMediaPlayer
import io.element.android.libraries.matrix.test.room.FakeMatrixRoom
import io.element.android.libraries.mediaupload.api.MediaSender
@ -38,6 +43,7 @@ import io.element.android.libraries.permissions.api.PermissionsPresenter @@ -38,6 +43,7 @@ import io.element.android.libraries.permissions.api.PermissionsPresenter
import io.element.android.libraries.permissions.api.aPermissionsState
import io.element.android.libraries.permissions.test.FakePermissionsPresenter
import io.element.android.libraries.permissions.test.FakePermissionsPresenterFactory
import io.element.android.libraries.textcomposer.model.MessageComposerMode
import io.element.android.libraries.textcomposer.model.PressEvent
import io.element.android.libraries.textcomposer.model.VoiceMessagePlayerEvent
import io.element.android.libraries.textcomposer.model.VoiceMessageState
@ -65,6 +71,7 @@ class VoiceMessageComposerPresenterTest { @@ -65,6 +71,7 @@ class VoiceMessageComposerPresenterTest {
private val matrixRoom = FakeMatrixRoom()
private val mediaPreProcessor = FakeMediaPreProcessor().apply { givenAudioResult() }
private val mediaSender = MediaSender(mediaPreProcessor, matrixRoom)
private val messageComposerContext = FakeMessageComposerContext()
companion object {
private val RECORDING_DURATION = 1.seconds
@ -275,6 +282,35 @@ class VoiceMessageComposerPresenterTest { @@ -275,6 +282,35 @@ class VoiceMessageComposerPresenterTest {
}
}
@Test
fun `present - sending is tracked`() = runTest {
val presenter = createVoiceMessageComposerPresenter()
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
// Send a normal voice message
messageComposerContext.composerMode = MessageComposerMode.Normal
awaitItem().eventSink(VoiceMessageComposerEvents.RecordButtonEvent(PressEvent.PressStart))
awaitItem().eventSink(VoiceMessageComposerEvents.RecordButtonEvent(PressEvent.LongPressEnd))
awaitItem().eventSink(VoiceMessageComposerEvents.SendVoiceMessage)
skipItems(1) // Sending state
// Now reply with a voice message
messageComposerContext.composerMode = aReplyMode()
awaitItem().eventSink(VoiceMessageComposerEvents.RecordButtonEvent(PressEvent.PressStart))
awaitItem().eventSink(VoiceMessageComposerEvents.RecordButtonEvent(PressEvent.LongPressEnd))
awaitItem().eventSink(VoiceMessageComposerEvents.SendVoiceMessage)
val finalState = awaitItem() // Sending state
assertThat(analyticsService.capturedEvents).containsExactly(
aVoiceMessageComposerEvent(isReply = false),
aVoiceMessageComposerEvent(isReply = true)
)
testPauseAndDestroy(finalState)
}
}
@Test
fun `present - send while playing`() = runTest {
val presenter = createVoiceMessageComposerPresenter()
@ -565,6 +601,7 @@ class VoiceMessageComposerPresenterTest { @@ -565,6 +601,7 @@ class VoiceMessageComposerPresenterTest {
analyticsService,
mediaSender,
player = VoiceMessageComposerPlayer(FakeMediaPlayer()),
messageComposerContext = messageComposerContext,
FakePermissionsPresenterFactory(permissionsPresenter),
)
}
@ -595,3 +632,15 @@ class VoiceMessageComposerPresenterTest { @@ -595,3 +632,15 @@ class VoiceMessageComposerPresenterTest {
waveform = waveform.toImmutableList(),
)
}
private fun aReplyMode() = MessageComposerMode.Reply(A_USER_NAME, null, false, AN_EVENT_ID, A_MESSAGE)
private fun aVoiceMessageComposerEvent(
isReply: Boolean = false
) = Composer(
inThread = false,
isEditing = false,
isReply = isReply,
messageType = Composer.MessageType.VoiceMessage,
startsThread = null
)

2
features/messages/test/src/main/kotlin/io/element/android/features/messages/test/MessageComposerContextFake.kt → features/messages/test/src/main/kotlin/io/element/android/features/messages/test/FakeMessageComposerContext.kt

@ -19,6 +19,6 @@ package io.element.android.features.messages.test @@ -19,6 +19,6 @@ package io.element.android.features.messages.test
import io.element.android.features.messages.api.MessageComposerContext
import io.element.android.libraries.textcomposer.model.MessageComposerMode
class MessageComposerContextFake(
class FakeMessageComposerContext(
override var composerMode: MessageComposerMode = MessageComposerMode.Normal
) : MessageComposerContext

6
features/poll/impl/src/test/kotlin/io/element/android/features/poll/impl/create/CreatePollPresenterTest.kt

@ -22,7 +22,7 @@ import app.cash.turbine.test @@ -22,7 +22,7 @@ import app.cash.turbine.test
import com.google.common.truth.Truth
import im.vector.app.features.analytics.plan.Composer
import im.vector.app.features.analytics.plan.PollCreation
import io.element.android.features.messages.test.MessageComposerContextFake
import io.element.android.features.messages.test.FakeMessageComposerContext
import io.element.android.libraries.matrix.api.poll.PollKind
import io.element.android.libraries.matrix.test.room.CreatePollInvocation
import io.element.android.libraries.matrix.test.room.FakeMatrixRoom
@ -41,12 +41,12 @@ class CreatePollPresenterTest { @@ -41,12 +41,12 @@ class CreatePollPresenterTest {
private var navUpInvocationsCount = 0
private val fakeMatrixRoom = FakeMatrixRoom()
private val fakeAnalyticsService = FakeAnalyticsService()
private val messageComposerContextFake = MessageComposerContextFake()
private val fakeMessageComposerContext = FakeMessageComposerContext()
private val presenter = CreatePollPresenter(
room = fakeMatrixRoom,
analyticsService = fakeAnalyticsService,
messageComposerContext = messageComposerContextFake,
messageComposerContext = fakeMessageComposerContext,
navigateUp = { navUpInvocationsCount++ },
)

2
gradle/libs.versions.toml

@ -171,7 +171,7 @@ opusencoder = "io.element.android:opusencoder:1.1.0" @@ -171,7 +171,7 @@ opusencoder = "io.element.android:opusencoder:1.1.0"
# Analytics
posthog = "com.posthog.android:posthog:2.0.3"
sentry = "io.sentry:sentry-android:6.32.0"
matrix_analytics_events = "com.github.matrix-org:matrix-analytics-events:e9cd9adaf18cec52ed851395eb84358b4f9b8d7f"
matrix_analytics_events = "com.github.matrix-org:matrix-analytics-events:aa14cbcdf81af2746d20a71779ec751f971e1d7f"
# Emojibase
matrix_emojibase_bindings = "io.element.android:emojibase-bindings:1.1.3"

Loading…
Cancel
Save