Merge pull request #2970 from element-hq/feature/bma/moreAnalytics

Track when the user starts a room call and when they enable formatting options on the message composer
This commit is contained in:
Benoit Marty
2024-06-05 09:21:28 +02:00
committed by GitHub
13 changed files with 111 additions and 20 deletions

1
changelog.d/2969.misc Normal file
View File

@@ -0,0 +1 @@
Track when the user starts a room call and when they enable formatting options on the message composer

View File

@@ -43,6 +43,7 @@ dependencies {
implementation(projects.libraries.matrix.impl) implementation(projects.libraries.matrix.impl)
implementation(projects.libraries.network) implementation(projects.libraries.network)
implementation(projects.libraries.preferences.api) implementation(projects.libraries.preferences.api)
implementation(projects.services.analytics.api)
implementation(projects.services.toolbox.api) implementation(projects.services.toolbox.api)
implementation(libs.androidx.webkit) implementation(libs.androidx.webkit)
implementation(libs.serialization.json) implementation(libs.serialization.json)
@@ -56,5 +57,6 @@ dependencies {
testImplementation(projects.libraries.featureflag.test) testImplementation(projects.libraries.featureflag.test)
testImplementation(projects.libraries.preferences.test) testImplementation(projects.libraries.preferences.test)
testImplementation(projects.libraries.matrix.test) testImplementation(projects.libraries.matrix.test)
testImplementation(projects.services.analytics.test)
testImplementation(projects.tests.testutils) testImplementation(projects.tests.testutils)
} }

View File

@@ -29,6 +29,7 @@ import androidx.compose.runtime.setValue
import dagger.assisted.Assisted import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject import dagger.assisted.AssistedInject
import im.vector.app.features.analytics.plan.MobileScreen
import io.element.android.features.call.CallType import io.element.android.features.call.CallType
import io.element.android.features.call.data.WidgetMessage import io.element.android.features.call.data.WidgetMessage
import io.element.android.features.call.utils.CallWidgetProvider import io.element.android.features.call.utils.CallWidgetProvider
@@ -42,6 +43,7 @@ import io.element.android.libraries.matrix.api.MatrixClientProvider
import io.element.android.libraries.matrix.api.sync.SyncState import io.element.android.libraries.matrix.api.sync.SyncState
import io.element.android.libraries.matrix.api.widget.MatrixWidgetDriver import io.element.android.libraries.matrix.api.widget.MatrixWidgetDriver
import io.element.android.libraries.network.useragent.UserAgentProvider import io.element.android.libraries.network.useragent.UserAgentProvider
import io.element.android.services.analytics.api.ScreenTracker
import io.element.android.services.toolbox.api.systemclock.SystemClock import io.element.android.services.toolbox.api.systemclock.SystemClock
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.collect
@@ -61,6 +63,7 @@ class CallScreenPresenter @AssistedInject constructor(
private val clock: SystemClock, private val clock: SystemClock,
private val dispatchers: CoroutineDispatchers, private val dispatchers: CoroutineDispatchers,
private val matrixClientsProvider: MatrixClientProvider, private val matrixClientsProvider: MatrixClientProvider,
private val screenTracker: ScreenTracker,
private val appCoroutineScope: CoroutineScope, private val appCoroutineScope: CoroutineScope,
) : Presenter<CallScreenState> { ) : Presenter<CallScreenState> {
@AssistedFactory @AssistedFactory
@@ -83,6 +86,15 @@ class CallScreenPresenter @AssistedInject constructor(
loadUrl(callType, urlState, callWidgetDriver) loadUrl(callType, urlState, callWidgetDriver)
} }
when (callType) {
is CallType.ExternalUrl -> {
// No analytics yet for external calls
}
is CallType.RoomCall -> {
screenTracker.TrackScreen(screen = MobileScreen.ScreenName.RoomCall)
}
}
HandleMatrixClientSyncState() HandleMatrixClientSyncState()
callWidgetDriver.value?.let { driver -> callWidgetDriver.value?.let { driver ->

View File

@@ -20,6 +20,7 @@ import app.cash.molecule.RecompositionMode
import app.cash.molecule.moleculeFlow import app.cash.molecule.moleculeFlow
import app.cash.turbine.test import app.cash.turbine.test
import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertThat
import im.vector.app.features.analytics.plan.MobileScreen
import io.element.android.features.call.CallType import io.element.android.features.call.CallType
import io.element.android.features.call.utils.FakeCallWidgetProvider import io.element.android.features.call.utils.FakeCallWidgetProvider
import io.element.android.features.call.utils.FakeWidgetMessageInterceptor import io.element.android.features.call.utils.FakeWidgetMessageInterceptor
@@ -32,9 +33,13 @@ import io.element.android.libraries.matrix.test.FakeMatrixClient
import io.element.android.libraries.matrix.test.FakeMatrixClientProvider import io.element.android.libraries.matrix.test.FakeMatrixClientProvider
import io.element.android.libraries.matrix.test.widget.FakeMatrixWidgetDriver import io.element.android.libraries.matrix.test.widget.FakeMatrixWidgetDriver
import io.element.android.libraries.network.useragent.UserAgentProvider import io.element.android.libraries.network.useragent.UserAgentProvider
import io.element.android.services.analytics.api.ScreenTracker
import io.element.android.services.analytics.test.FakeScreenTracker
import io.element.android.services.toolbox.api.systemclock.SystemClock import io.element.android.services.toolbox.api.systemclock.SystemClock
import io.element.android.tests.testutils.WarmUpRule import io.element.android.tests.testutils.WarmUpRule
import io.element.android.tests.testutils.consumeItemsUntilTimeout import io.element.android.tests.testutils.consumeItemsUntilTimeout
import io.element.android.tests.testutils.lambda.lambdaRecorder
import io.element.android.tests.testutils.lambda.value
import io.element.android.tests.testutils.testCoroutineDispatchers import io.element.android.tests.testutils.testCoroutineDispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.cancelAndJoin import kotlinx.coroutines.cancelAndJoin
@@ -53,16 +58,20 @@ class CallScreenPresenterTest {
@Test @Test
fun `present - with CallType ExternalUrl just loads the URL`() = runTest { fun `present - with CallType ExternalUrl just loads the URL`() = runTest {
val presenter = createCallScreenPresenter(CallType.ExternalUrl("https://call.element.io")) val analyticsLambda = lambdaRecorder<MobileScreen.ScreenName, Unit> { }
val presenter = createCallScreenPresenter(
callType = CallType.ExternalUrl("https://call.element.io"),
screenTracker = FakeScreenTracker(analyticsLambda)
)
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
presenter.present() presenter.present()
}.test { }.test {
// Wait until the URL is loaded // Wait until the URL is loaded
skipItems(1) skipItems(1)
val initialState = awaitItem() val initialState = awaitItem()
assertThat(initialState.urlState).isEqualTo(AsyncData.Success("https://call.element.io")) assertThat(initialState.urlState).isEqualTo(AsyncData.Success("https://call.element.io"))
assertThat(initialState.isInWidgetMode).isFalse() assertThat(initialState.isInWidgetMode).isFalse()
analyticsLambda.assertions().isNeverCalled()
} }
} }
@@ -70,22 +79,29 @@ class CallScreenPresenterTest {
fun `present - with CallType RoomCall loads URL and runs WidgetDriver`() = runTest { fun `present - with CallType RoomCall loads URL and runs WidgetDriver`() = runTest {
val widgetDriver = FakeMatrixWidgetDriver() val widgetDriver = FakeMatrixWidgetDriver()
val widgetProvider = FakeCallWidgetProvider(widgetDriver) val widgetProvider = FakeCallWidgetProvider(widgetDriver)
val analyticsLambda = lambdaRecorder<MobileScreen.ScreenName, Unit> { }
val presenter = createCallScreenPresenter( val presenter = createCallScreenPresenter(
callType = CallType.RoomCall(A_SESSION_ID, A_ROOM_ID), callType = CallType.RoomCall(A_SESSION_ID, A_ROOM_ID),
widgetDriver = widgetDriver, widgetDriver = widgetDriver,
widgetProvider = widgetProvider, widgetProvider = widgetProvider,
screenTracker = FakeScreenTracker(analyticsLambda)
) )
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
presenter.present() presenter.present()
}.test { }.test {
// Wait until the URL is loaded // Wait until the URL is loaded
skipItems(1) skipItems(1)
val initialState = awaitItem() val initialState = awaitItem()
assertThat(initialState.urlState).isInstanceOf(AsyncData.Success::class.java) assertThat(initialState.urlState).isInstanceOf(AsyncData.Success::class.java)
assertThat(initialState.isInWidgetMode).isTrue() assertThat(initialState.isInWidgetMode).isTrue()
assertThat(widgetProvider.getWidgetCalled).isTrue() assertThat(widgetProvider.getWidgetCalled).isTrue()
assertThat(widgetDriver.runCalledCount).isEqualTo(1) assertThat(widgetDriver.runCalledCount).isEqualTo(1)
// Called several times because of the recomposition
analyticsLambda.assertions().isCalledExactly(2)
.withSequence(
listOf(value(MobileScreen.ScreenName.RoomCall)),
listOf(value(MobileScreen.ScreenName.RoomCall))
)
} }
} }
@@ -95,6 +111,7 @@ class CallScreenPresenterTest {
val presenter = createCallScreenPresenter( val presenter = createCallScreenPresenter(
callType = CallType.RoomCall(A_SESSION_ID, A_ROOM_ID), callType = CallType.RoomCall(A_SESSION_ID, A_ROOM_ID),
widgetDriver = widgetDriver, widgetDriver = widgetDriver,
screenTracker = FakeScreenTracker {},
) )
val messageInterceptor = FakeWidgetMessageInterceptor() val messageInterceptor = FakeWidgetMessageInterceptor()
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
@@ -125,6 +142,7 @@ class CallScreenPresenterTest {
widgetDriver = widgetDriver, widgetDriver = widgetDriver,
navigator = navigator, navigator = navigator,
dispatchers = testCoroutineDispatchers(useUnconfinedTestDispatcher = true), dispatchers = testCoroutineDispatchers(useUnconfinedTestDispatcher = true),
screenTracker = FakeScreenTracker {},
) )
val messageInterceptor = FakeWidgetMessageInterceptor() val messageInterceptor = FakeWidgetMessageInterceptor()
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
@@ -155,6 +173,7 @@ class CallScreenPresenterTest {
widgetDriver = widgetDriver, widgetDriver = widgetDriver,
navigator = navigator, navigator = navigator,
dispatchers = testCoroutineDispatchers(useUnconfinedTestDispatcher = true), dispatchers = testCoroutineDispatchers(useUnconfinedTestDispatcher = true),
screenTracker = FakeScreenTracker {},
) )
val messageInterceptor = FakeWidgetMessageInterceptor() val messageInterceptor = FakeWidgetMessageInterceptor()
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
@@ -185,7 +204,8 @@ class CallScreenPresenterTest {
widgetDriver = widgetDriver, widgetDriver = widgetDriver,
navigator = navigator, navigator = navigator,
dispatchers = testCoroutineDispatchers(useUnconfinedTestDispatcher = true), dispatchers = testCoroutineDispatchers(useUnconfinedTestDispatcher = true),
matrixClientsProvider = FakeMatrixClientProvider(getClient = { Result.success(matrixClient) }) matrixClientsProvider = FakeMatrixClientProvider(getClient = { Result.success(matrixClient) }),
screenTracker = FakeScreenTracker {},
) )
moleculeFlow(RecompositionMode.Immediate) { moleculeFlow(RecompositionMode.Immediate) {
presenter.present() presenter.present()
@@ -208,7 +228,8 @@ class CallScreenPresenterTest {
widgetDriver = widgetDriver, widgetDriver = widgetDriver,
navigator = navigator, navigator = navigator,
dispatchers = testCoroutineDispatchers(useUnconfinedTestDispatcher = true), dispatchers = testCoroutineDispatchers(useUnconfinedTestDispatcher = true),
matrixClientsProvider = FakeMatrixClientProvider(getClient = { Result.success(matrixClient) }) matrixClientsProvider = FakeMatrixClientProvider(getClient = { Result.success(matrixClient) }),
screenTracker = FakeScreenTracker {},
) )
val hasRun = Mutex(true) val hasRun = Mutex(true)
val job = launch { val job = launch {
@@ -233,6 +254,7 @@ class CallScreenPresenterTest {
widgetProvider: FakeCallWidgetProvider = FakeCallWidgetProvider(widgetDriver), widgetProvider: FakeCallWidgetProvider = FakeCallWidgetProvider(widgetDriver),
dispatchers: CoroutineDispatchers = testCoroutineDispatchers(), dispatchers: CoroutineDispatchers = testCoroutineDispatchers(),
matrixClientsProvider: FakeMatrixClientProvider = FakeMatrixClientProvider(), matrixClientsProvider: FakeMatrixClientProvider = FakeMatrixClientProvider(),
screenTracker: ScreenTracker = FakeScreenTracker(),
): CallScreenPresenter { ): CallScreenPresenter {
val userAgentProvider = object : UserAgentProvider { val userAgentProvider = object : UserAgentProvider {
override fun provide(): String { override fun provide(): String {
@@ -241,14 +263,15 @@ class CallScreenPresenterTest {
} }
val clock = SystemClock { 0 } val clock = SystemClock { 0 }
return CallScreenPresenter( return CallScreenPresenter(
callType, callType = callType,
navigator, navigator = navigator,
widgetProvider, callWidgetProvider = widgetProvider,
userAgentProvider, userAgentProvider = userAgentProvider,
clock, clock = clock,
dispatchers, dispatchers = dispatchers,
matrixClientsProvider, matrixClientsProvider = matrixClientsProvider,
this, screenTracker = screenTracker,
appCoroutineScope = this,
) )
} }
} }

View File

@@ -29,6 +29,7 @@ import com.bumble.appyx.navmodel.backstack.BackStack
import com.bumble.appyx.navmodel.backstack.operation.push import com.bumble.appyx.navmodel.backstack.operation.push
import dagger.assisted.Assisted import dagger.assisted.Assisted
import dagger.assisted.AssistedInject import dagger.assisted.AssistedInject
import im.vector.app.features.analytics.plan.Interaction
import io.element.android.anvilannotations.ContributesNode import io.element.android.anvilannotations.ContributesNode
import io.element.android.features.call.CallType import io.element.android.features.call.CallType
import io.element.android.features.call.ui.ElementCallActivity import io.element.android.features.call.ui.ElementCallActivity
@@ -68,6 +69,8 @@ import io.element.android.libraries.matrix.api.permalink.PermalinkData
import io.element.android.libraries.matrix.api.timeline.item.TimelineItemDebugInfo import io.element.android.libraries.matrix.api.timeline.item.TimelineItemDebugInfo
import io.element.android.libraries.mediaviewer.api.local.MediaInfo import io.element.android.libraries.mediaviewer.api.local.MediaInfo
import io.element.android.libraries.mediaviewer.api.viewer.MediaViewerNode import io.element.android.libraries.mediaviewer.api.viewer.MediaViewerNode
import io.element.android.services.analytics.api.AnalyticsService
import io.element.android.services.analyticsproviders.api.trackers.captureInteraction
import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.ImmutableList
import kotlinx.parcelize.Parcelize import kotlinx.parcelize.Parcelize
@@ -80,6 +83,7 @@ class MessagesFlowNode @AssistedInject constructor(
private val sendLocationEntryPoint: SendLocationEntryPoint, private val sendLocationEntryPoint: SendLocationEntryPoint,
private val showLocationEntryPoint: ShowLocationEntryPoint, private val showLocationEntryPoint: ShowLocationEntryPoint,
private val createPollEntryPoint: CreatePollEntryPoint, private val createPollEntryPoint: CreatePollEntryPoint,
private val analyticsService: AnalyticsService,
) : BaseFlowNode<MessagesFlowNode.NavTarget>( ) : BaseFlowNode<MessagesFlowNode.NavTarget>(
backstack = BackStack( backstack = BackStack(
initialElement = NavTarget.Messages, initialElement = NavTarget.Messages,
@@ -188,6 +192,7 @@ class MessagesFlowNode @AssistedInject constructor(
sessionId = matrixClient.sessionId, sessionId = matrixClient.sessionId,
roomId = roomId, roomId = roomId,
) )
analyticsService.captureInteraction(Interaction.Name.MobileRoomCallButton)
ElementCallActivity.start(context, inputs) ElementCallActivity.start(context, inputs)
} }
} }

View File

@@ -36,6 +36,7 @@ import androidx.compose.runtime.setValue
import androidx.media3.common.MimeTypes import androidx.media3.common.MimeTypes
import androidx.media3.common.util.UnstableApi import androidx.media3.common.util.UnstableApi
import im.vector.app.features.analytics.plan.Composer import im.vector.app.features.analytics.plan.Composer
import im.vector.app.features.analytics.plan.Interaction
import io.element.android.features.messages.impl.attachments.Attachment import io.element.android.features.messages.impl.attachments.Attachment
import io.element.android.features.messages.impl.attachments.preview.error.sendAttachmentError import io.element.android.features.messages.impl.attachments.preview.error.sendAttachmentError
import io.element.android.features.messages.impl.mentions.MentionSuggestionsProcessor import io.element.android.features.messages.impl.mentions.MentionSuggestionsProcessor
@@ -68,6 +69,7 @@ import io.element.android.libraries.textcomposer.model.Suggestion
import io.element.android.libraries.textcomposer.model.TextEditorState import io.element.android.libraries.textcomposer.model.TextEditorState
import io.element.android.libraries.textcomposer.model.rememberMarkdownTextEditorState import io.element.android.libraries.textcomposer.model.rememberMarkdownTextEditorState
import io.element.android.services.analytics.api.AnalyticsService import io.element.android.services.analytics.api.AnalyticsService
import io.element.android.services.analyticsproviders.api.trackers.captureInteraction
import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toPersistentList import kotlinx.collections.immutable.toPersistentList
import kotlinx.coroutines.CancellationException import kotlinx.coroutines.CancellationException
@@ -388,6 +390,9 @@ class MessageComposerPresenter @Inject constructor(
is MessageComposerEvents.ToggleTextFormatting -> { is MessageComposerEvents.ToggleTextFormatting -> {
showAttachmentSourcePicker = false showAttachmentSourcePicker = false
showTextFormatting = event.enabled showTextFormatting = event.enabled
if (showTextFormatting) {
analyticsService.captureInteraction(Interaction.Name.MobileRoomComposerFormattingEnabled)
}
} }
is MessageComposerEvents.Error -> { is MessageComposerEvents.Error -> {
analyticsService.trackError(event.error) analyticsService.trackError(event.error)

View File

@@ -26,6 +26,7 @@ import app.cash.turbine.ReceiveTurbine
import app.cash.turbine.test import app.cash.turbine.test
import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertThat
import im.vector.app.features.analytics.plan.Composer import im.vector.app.features.analytics.plan.Composer
import im.vector.app.features.analytics.plan.Interaction
import io.element.android.features.messages.impl.messagecomposer.AttachmentsState import io.element.android.features.messages.impl.messagecomposer.AttachmentsState
import io.element.android.features.messages.impl.messagecomposer.DefaultMessageComposerContext import io.element.android.features.messages.impl.messagecomposer.DefaultMessageComposerContext
import io.element.android.features.messages.impl.messagecomposer.MessageComposerEvents import io.element.android.features.messages.impl.messagecomposer.MessageComposerEvents
@@ -768,10 +769,15 @@ class MessageComposerPresenterTest {
val showTextFormatting = awaitItem() val showTextFormatting = awaitItem()
assertThat(showTextFormatting.showAttachmentSourcePicker).isFalse() assertThat(showTextFormatting.showAttachmentSourcePicker).isFalse()
assertThat(showTextFormatting.showTextFormatting).isTrue() assertThat(showTextFormatting.showTextFormatting).isTrue()
assertThat(analyticsService.capturedEvents).containsExactly(
Interaction(index = null, interactionType = null, name = Interaction.Name.MobileRoomComposerFormattingEnabled)
)
analyticsService.capturedEvents.clear()
showTextFormatting.eventSink(MessageComposerEvents.ToggleTextFormatting(false)) showTextFormatting.eventSink(MessageComposerEvents.ToggleTextFormatting(false))
skipItems(1) skipItems(1)
val finished = awaitItem() val finished = awaitItem()
assertThat(finished.showTextFormatting).isFalse() assertThat(finished.showTextFormatting).isFalse()
assertThat(analyticsService.capturedEvents).isEmpty()
} }
} }

View File

@@ -28,6 +28,7 @@ import com.bumble.appyx.navmodel.backstack.BackStack
import com.bumble.appyx.navmodel.backstack.operation.push import com.bumble.appyx.navmodel.backstack.operation.push
import dagger.assisted.Assisted import dagger.assisted.Assisted
import dagger.assisted.AssistedInject import dagger.assisted.AssistedInject
import im.vector.app.features.analytics.plan.Interaction
import io.element.android.anvilannotations.ContributesNode import io.element.android.anvilannotations.ContributesNode
import io.element.android.features.call.CallType import io.element.android.features.call.CallType
import io.element.android.features.call.ui.ElementCallActivity import io.element.android.features.call.ui.ElementCallActivity
@@ -53,6 +54,8 @@ import io.element.android.libraries.matrix.api.media.MediaSource
import io.element.android.libraries.matrix.api.room.MatrixRoom import io.element.android.libraries.matrix.api.room.MatrixRoom
import io.element.android.libraries.mediaviewer.api.local.MediaInfo import io.element.android.libraries.mediaviewer.api.local.MediaInfo
import io.element.android.libraries.mediaviewer.api.viewer.MediaViewerNode import io.element.android.libraries.mediaviewer.api.viewer.MediaViewerNode
import io.element.android.services.analytics.api.AnalyticsService
import io.element.android.services.analyticsproviders.api.trackers.captureInteraction
import kotlinx.parcelize.Parcelize import kotlinx.parcelize.Parcelize
@ContributesNode(RoomScope::class) @ContributesNode(RoomScope::class)
@@ -62,6 +65,7 @@ class RoomDetailsFlowNode @AssistedInject constructor(
@ApplicationContext private val context: Context, @ApplicationContext private val context: Context,
private val pollHistoryEntryPoint: PollHistoryEntryPoint, private val pollHistoryEntryPoint: PollHistoryEntryPoint,
private val room: MatrixRoom, private val room: MatrixRoom,
private val analyticsService: AnalyticsService,
) : BaseFlowNode<RoomDetailsFlowNode.NavTarget>( ) : BaseFlowNode<RoomDetailsFlowNode.NavTarget>(
backstack = BackStack( backstack = BackStack(
initialElement = plugins.filterIsInstance<RoomDetailsEntryPoint.Params>().first().initialElement.toNavTarget(), initialElement = plugins.filterIsInstance<RoomDetailsEntryPoint.Params>().first().initialElement.toNavTarget(),
@@ -142,6 +146,7 @@ class RoomDetailsFlowNode @AssistedInject constructor(
sessionId = room.sessionId, sessionId = room.sessionId,
roomId = room.roomId, roomId = room.roomId,
) )
analyticsService.captureInteraction(Interaction.Name.MobileRoomCallButton)
ElementCallActivity.start(context, inputs) ElementCallActivity.start(context, inputs)
} }
} }
@@ -189,8 +194,8 @@ class RoomDetailsFlowNode @AssistedInject constructor(
plugins<RoomDetailsEntryPoint.Callback>().forEach { it.onOpenRoom(roomId) } plugins<RoomDetailsEntryPoint.Callback>().forEach { it.onOpenRoom(roomId) }
} }
override fun onStartCall(roomId: RoomId) { override fun onStartCall(dmRoomId: RoomId) {
ElementCallActivity.start(context, CallType.RoomCall(roomId = roomId, sessionId = room.sessionId)) ElementCallActivity.start(context, CallType.RoomCall(sessionId = room.sessionId, roomId = dmRoomId))
} }
} }
val plugins = listOf(RoomMemberDetailsNode.RoomMemberDetailsInput(navTarget.roomMemberId), callback) val plugins = listOf(RoomMemberDetailsNode.RoomMemberDetailsInput(navTarget.roomMemberId), callback)

View File

@@ -83,8 +83,8 @@ class UserProfileFlowNode @AssistedInject constructor(
plugins<UserProfileEntryPoint.Callback>().forEach { it.onOpenRoom(roomId) } plugins<UserProfileEntryPoint.Callback>().forEach { it.onOpenRoom(roomId) }
} }
override fun onStartCall(roomId: RoomId) { override fun onStartCall(dmRoomId: RoomId) {
ElementCallActivity.start(context, CallType.RoomCall(sessionId = sessionIdHolder.current, roomId = roomId)) ElementCallActivity.start(context, CallType.RoomCall(sessionId = sessionIdHolder.current, roomId = dmRoomId))
} }
} }
val params = UserProfileNode.UserProfileInputs(userId = inputs<UserProfileEntryPoint.Params>().userId) val params = UserProfileNode.UserProfileInputs(userId = inputs<UserProfileEntryPoint.Params>().userId)

View File

@@ -32,7 +32,7 @@ class UserProfileNodeHelper(
interface Callback : NodeInputs { interface Callback : NodeInputs {
fun openAvatarPreview(username: String, avatarUrl: String) fun openAvatarPreview(username: String, avatarUrl: String)
fun onStartDM(roomId: RoomId) fun onStartDM(roomId: RoomId)
fun onStartCall(roomId: RoomId) fun onStartCall(dmRoomId: RoomId)
} }
fun onShareUser( fun onShareUser(

View File

@@ -187,7 +187,7 @@ zxing_cpp = "io.github.zxing-cpp:android:2.2.0"
posthog = "com.posthog:posthog-android:3.3.0" posthog = "com.posthog:posthog-android:3.3.0"
sentry = "io.sentry:sentry-android:7.9.0" sentry = "io.sentry:sentry-android:7.9.0"
# main branch can be tested replacing the version with main-SNAPSHOT # main branch can be tested replacing the version with main-SNAPSHOT
matrix_analytics_events = "com.github.matrix-org:matrix-analytics-events:0.23.0" matrix_analytics_events = "com.github.matrix-org:matrix-analytics-events:0.23.1"
# Emojibase # Emojibase
matrix_emojibase_bindings = "io.element.android:emojibase-bindings:1.1.3" matrix_emojibase_bindings = "io.element.android:emojibase-bindings:1.1.3"

View File

@@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
plugins { plugins {
id("io.element.android-library") id("io.element.android-compose-library")
} }
android { android {
@@ -24,5 +24,6 @@ android {
dependencies { dependencies {
implementation(projects.services.analytics.api) implementation(projects.services.analytics.api)
implementation(projects.libraries.core) implementation(projects.libraries.core)
implementation(projects.tests.testutils)
implementation(libs.coroutines.core) implementation(libs.coroutines.core)
} }

View File

@@ -0,0 +1,31 @@
/*
* Copyright (c) 2024 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.services.analytics.test
import androidx.compose.runtime.Composable
import im.vector.app.features.analytics.plan.MobileScreen
import io.element.android.services.analytics.api.ScreenTracker
import io.element.android.tests.testutils.lambda.lambdaError
class FakeScreenTracker(
private val trackScreenLambda: (MobileScreen.ScreenName) -> Unit = { lambdaError() }
) : ScreenTracker {
@Composable
override fun TrackScreen(screen: MobileScreen.ScreenName) {
trackScreenLambda(screen)
}
}