Adjust metrics to the new specifications (#5937)
* Add `AnalyticsTransactions` with a set of `TransactionDefinition` items matching those in the user story * Use that for `AnalyticsLongRunningTransactions`, make sure we send the right fields (name, operation, description) * Add `AnalyticsSendMessageWatcher` to track how long it takes for an event to be sent and for us to get a call back for that from sync * Add `Noop` implementation for enterprise
This commit is contained in:
committed by
GitHub
parent
bc62d4c8ba
commit
71031008dd
@@ -7,15 +7,24 @@
|
||||
|
||||
package io.element.android.services.analytics.api
|
||||
|
||||
import io.element.android.services.analyticsproviders.api.AnalyticsTransactions
|
||||
import io.element.android.services.analyticsproviders.api.TransactionDefinition
|
||||
|
||||
sealed class AnalyticsLongRunningTransaction(
|
||||
val name: String,
|
||||
val operation: String?,
|
||||
val operation: String? = null,
|
||||
val description: String? = null,
|
||||
) {
|
||||
data object ColdStartUntilCachedRoomList : AnalyticsLongRunningTransaction("Cold start until cached room list is displayed", null)
|
||||
data object FirstRoomsDisplayed : AnalyticsLongRunningTransaction("First rooms displayed after login or restoration", null)
|
||||
data object ResumeAppUntilNewRoomsReceived : AnalyticsLongRunningTransaction("App was resumed and new room list items arrived", null)
|
||||
data object NotificationTapOpensTimeline : AnalyticsLongRunningTransaction("A notification was tapped and it opened a timeline", null)
|
||||
data object OpenRoom : AnalyticsLongRunningTransaction("Open a room and see loaded items in the timeline", null)
|
||||
constructor(definition: TransactionDefinition) : this(definition.name, definition.operation, definition.description)
|
||||
|
||||
// UX flows
|
||||
data object ColdStart : AnalyticsLongRunningTransaction(AnalyticsTransactions.coldStart)
|
||||
data object CatchUp : AnalyticsLongRunningTransaction(AnalyticsTransactions.catchUp)
|
||||
data object NotificationToMessage : AnalyticsLongRunningTransaction(AnalyticsTransactions.notificationToMessage)
|
||||
data object OpenRoom : AnalyticsLongRunningTransaction(AnalyticsTransactions.openRoom)
|
||||
|
||||
// Technical flows
|
||||
data object FirstRoomsDisplayed : AnalyticsLongRunningTransaction("First rooms displayed after login or restoration", null, null)
|
||||
data object LoadJoinedRoomFlow : AnalyticsLongRunningTransaction("Load joined room UI", "ui.load")
|
||||
data object LoadMessagesUi : AnalyticsLongRunningTransaction("Load messages UI", "ui.load")
|
||||
data object DisplayFirstTimelineItems : AnalyticsLongRunningTransaction("Get and display first timeline items", null)
|
||||
|
||||
@@ -53,7 +53,7 @@ interface AnalyticsService : AnalyticsTracker, ErrorTracker {
|
||||
/**
|
||||
* Starts a transaction to measure the performance of an operation.
|
||||
*/
|
||||
fun startTransaction(name: String, operation: String? = null): AnalyticsTransaction
|
||||
fun startTransaction(name: String, operation: String? = null, description: String? = null): AnalyticsTransaction
|
||||
|
||||
/**
|
||||
* Starts an [AnalyticsLongRunningTransaction], that can be shared with other components.
|
||||
@@ -80,11 +80,12 @@ interface AnalyticsService : AnalyticsTracker, ErrorTracker {
|
||||
inline fun <T> AnalyticsService.recordTransaction(
|
||||
name: String,
|
||||
operation: String,
|
||||
description: String? = null,
|
||||
parentTransaction: AnalyticsTransaction? = null,
|
||||
block: (AnalyticsTransaction) -> T
|
||||
): T {
|
||||
val transaction = parentTransaction?.startChild(name, operation)
|
||||
?: startTransaction(name, operation)
|
||||
val transaction = parentTransaction?.startChild(operation, description)
|
||||
?: startTransaction(name, operation, description)
|
||||
try {
|
||||
val result = block(transaction)
|
||||
return result
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.services.analytics.api.watchers
|
||||
|
||||
/**
|
||||
* An analytics watcher tracking the time it took the client to send a message.
|
||||
*/
|
||||
interface AnalyticsSendMessageWatcher {
|
||||
/**
|
||||
* Start listening to send queue updates and tracking the sending states of the events.
|
||||
*/
|
||||
fun start()
|
||||
|
||||
/**
|
||||
* Stop observing the sending states of the events.
|
||||
*/
|
||||
fun stop()
|
||||
}
|
||||
@@ -161,9 +161,9 @@ class DefaultAnalyticsService(
|
||||
}
|
||||
}
|
||||
|
||||
override fun startTransaction(name: String, operation: String?): AnalyticsTransaction {
|
||||
override fun startTransaction(name: String, operation: String?, description: String?): AnalyticsTransaction {
|
||||
return if (userConsent.get()) {
|
||||
analyticsProviders.firstNotNullOfOrNull { it.startTransaction(name, operation) }
|
||||
analyticsProviders.firstNotNullOfOrNull { it.startTransaction(name, operation, description) }
|
||||
} else {
|
||||
null
|
||||
} ?: NoopAnalyticsTransaction
|
||||
@@ -173,8 +173,8 @@ class DefaultAnalyticsService(
|
||||
longRunningTransaction: AnalyticsLongRunningTransaction,
|
||||
parentTransaction: AnalyticsTransaction?,
|
||||
): AnalyticsTransaction {
|
||||
val transaction = parentTransaction?.startChild(longRunningTransaction.name, longRunningTransaction.operation)
|
||||
?: startTransaction(longRunningTransaction.name, longRunningTransaction.operation)
|
||||
val transaction = parentTransaction?.startChild(longRunningTransaction.operation.orEmpty(), longRunningTransaction.name)
|
||||
?: startTransaction(longRunningTransaction.name, longRunningTransaction.operation, longRunningTransaction.description)
|
||||
|
||||
pendingLongRunningTransactions[longRunningTransaction] = transaction
|
||||
return transaction
|
||||
|
||||
@@ -37,7 +37,7 @@ class DefaultAnalyticsColdStartWatcher(
|
||||
if (hasConsent) {
|
||||
if (isColdStart.get()) {
|
||||
Timber.d("Starting cold start check")
|
||||
analyticsService.startLongRunningTransaction(AnalyticsLongRunningTransaction.ColdStartUntilCachedRoomList)
|
||||
analyticsService.startLongRunningTransaction(AnalyticsLongRunningTransaction.ColdStart)
|
||||
} else {
|
||||
error("The app is no longer in a cold start state")
|
||||
}
|
||||
@@ -49,7 +49,7 @@ class DefaultAnalyticsColdStartWatcher(
|
||||
|
||||
override fun whenLoggingIn() {
|
||||
if (isColdStart.getAndSet(false)) {
|
||||
analyticsService.cancelLongRunningTransaction(AnalyticsLongRunningTransaction.ColdStartUntilCachedRoomList)
|
||||
analyticsService.cancelLongRunningTransaction(AnalyticsLongRunningTransaction.ColdStart)
|
||||
Timber.d("Canceled cold start check: user is logging in")
|
||||
}
|
||||
}
|
||||
@@ -57,7 +57,7 @@ class DefaultAnalyticsColdStartWatcher(
|
||||
override fun onRoomListVisible() {
|
||||
if (isColdStart.getAndSet(false)) {
|
||||
Timber.d("Room list is visible, finishing cold start check")
|
||||
analyticsService.finishLongRunningTransaction(AnalyticsLongRunningTransaction.ColdStartUntilCachedRoomList)
|
||||
analyticsService.finishLongRunningTransaction(AnalyticsLongRunningTransaction.ColdStart)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ class DefaultAnalyticsRoomListStateWatcher(
|
||||
.withPreviousValue()
|
||||
.onEach { (wasInForeground, isInForeground) ->
|
||||
if (isInForeground && roomListService.state.value != RoomListService.State.Running) {
|
||||
analyticsService.startLongRunningTransaction(AnalyticsLongRunningTransaction.ResumeAppUntilNewRoomsReceived)
|
||||
analyticsService.startLongRunningTransaction(AnalyticsLongRunningTransaction.CatchUp)
|
||||
}
|
||||
|
||||
if (wasInForeground == false && isInForeground) {
|
||||
@@ -64,7 +64,7 @@ class DefaultAnalyticsRoomListStateWatcher(
|
||||
roomListService.state
|
||||
.onEach { state ->
|
||||
if (state == RoomListService.State.Running && isWarmState.get()) {
|
||||
analyticsService.finishLongRunningTransaction(AnalyticsLongRunningTransaction.ResumeAppUntilNewRoomsReceived)
|
||||
analyticsService.finishLongRunningTransaction(AnalyticsLongRunningTransaction.CatchUp)
|
||||
}
|
||||
}
|
||||
.launchIn(coroutineScope)
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.services.analytics.impl.watchers
|
||||
|
||||
import dev.zacsweers.metro.ContributesBinding
|
||||
import dev.zacsweers.metro.SingleIn
|
||||
import io.element.android.libraries.di.RoomScope
|
||||
import io.element.android.libraries.di.annotations.RoomCoroutineScope
|
||||
import io.element.android.libraries.matrix.api.core.TransactionId
|
||||
import io.element.android.libraries.matrix.api.room.JoinedRoom
|
||||
import io.element.android.libraries.matrix.api.room.SendQueueUpdate
|
||||
import io.element.android.services.analytics.api.AnalyticsService
|
||||
import io.element.android.services.analytics.api.watchers.AnalyticsSendMessageWatcher
|
||||
import io.element.android.services.analyticsproviders.api.AnalyticsTransaction
|
||||
import io.element.android.services.analyticsproviders.api.AnalyticsTransactions
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import timber.log.Timber
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
|
||||
private const val TAG = "SendMessageWatcher"
|
||||
|
||||
@SingleIn(RoomScope::class)
|
||||
@ContributesBinding(RoomScope::class)
|
||||
class DefaultAnalyticsSendMessageWatcher(
|
||||
private val room: JoinedRoom,
|
||||
private val analyticsService: AnalyticsService,
|
||||
@RoomCoroutineScope private val coroutineScope: CoroutineScope,
|
||||
) : AnalyticsSendMessageWatcher {
|
||||
private val pendingEvents = ConcurrentHashMap<TransactionId, AnalyticsTransaction>()
|
||||
private var sendQueueWatchJob: Job? = null
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
override fun start() {
|
||||
Timber.tag(TAG).d("Starting SendMessageWatcher")
|
||||
sendQueueWatchJob?.cancel()
|
||||
sendQueueWatchJob = room.subscribeToSendQueueUpdates()
|
||||
.onEach { update ->
|
||||
// We received a new local event
|
||||
when (update) {
|
||||
is SendQueueUpdate.NewLocalEvent -> {
|
||||
Timber.tag(TAG).d("Event with transaction id ${update.transactionId} sent")
|
||||
watch(update.transactionId)
|
||||
}
|
||||
is SendQueueUpdate.SentEvent -> {
|
||||
val pendingTransaction = pendingEvents.remove(update.transactionId)
|
||||
if (pendingTransaction != null) {
|
||||
Timber.tag(TAG).d("Sent event with transaction id ${update.transactionId} received in sync")
|
||||
pendingTransaction.finish()
|
||||
}
|
||||
}
|
||||
else -> Unit
|
||||
}
|
||||
}
|
||||
.launchIn(coroutineScope)
|
||||
}
|
||||
|
||||
override fun stop() {
|
||||
Timber.tag(TAG).d("Stopping SendMessageWatcher")
|
||||
sendQueueWatchJob?.cancel()
|
||||
sendQueueWatchJob = null
|
||||
pendingEvents.clear()
|
||||
}
|
||||
|
||||
private fun watch(transactionId: TransactionId) {
|
||||
pendingEvents[transactionId] = with(AnalyticsTransactions.sendMessage) {
|
||||
analyticsService.startTransaction(
|
||||
name = name,
|
||||
operation = operation,
|
||||
description = description,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,7 @@
|
||||
package io.element.android.services.analytics.impl.watchers
|
||||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.services.analytics.api.AnalyticsLongRunningTransaction.ColdStartUntilCachedRoomList
|
||||
import io.element.android.services.analytics.api.AnalyticsLongRunningTransaction.ColdStart
|
||||
import io.element.android.services.analytics.test.FakeAnalyticsService
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.test.TestScope
|
||||
@@ -31,14 +31,14 @@ class DefaultAnalyticsColdStartWatcherTest {
|
||||
runCurrent()
|
||||
|
||||
// The transaction is running
|
||||
assertThat(analyticsService.getLongRunningTransaction(ColdStartUntilCachedRoomList)).isNotNull()
|
||||
assertThat(analyticsService.getLongRunningTransaction(ColdStart)).isNotNull()
|
||||
|
||||
// As soon as the room list is visible
|
||||
watcher.onRoomListVisible()
|
||||
runCurrent()
|
||||
|
||||
// The transaction is now finished
|
||||
assertThat(analyticsService.getLongRunningTransaction(ColdStartUntilCachedRoomList)).isNull()
|
||||
assertThat(analyticsService.getLongRunningTransaction(ColdStart)).isNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -54,14 +54,14 @@ class DefaultAnalyticsColdStartWatcherTest {
|
||||
runCurrent()
|
||||
|
||||
// The transaction is running
|
||||
assertThat(analyticsService.getLongRunningTransaction(ColdStartUntilCachedRoomList)).isNotNull()
|
||||
assertThat(analyticsService.getLongRunningTransaction(ColdStart)).isNotNull()
|
||||
|
||||
// If the user starts a login flow
|
||||
watcher.whenLoggingIn()
|
||||
runCurrent()
|
||||
|
||||
// The transaction is gone
|
||||
assertThat(analyticsService.getLongRunningTransaction(ColdStartUntilCachedRoomList)).isNull()
|
||||
assertThat(analyticsService.getLongRunningTransaction(ColdStart)).isNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -80,7 +80,7 @@ class DefaultAnalyticsColdStartWatcherTest {
|
||||
runCurrent()
|
||||
|
||||
// The transaction never starts
|
||||
assertThat(analyticsService.getLongRunningTransaction(ColdStartUntilCachedRoomList)).isNull()
|
||||
assertThat(analyticsService.getLongRunningTransaction(ColdStart)).isNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -95,7 +95,7 @@ class DefaultAnalyticsColdStartWatcherTest {
|
||||
runCurrent()
|
||||
|
||||
// The transaction is not running in that case
|
||||
assertThat(analyticsService.getLongRunningTransaction(ColdStartUntilCachedRoomList)).isNull()
|
||||
assertThat(analyticsService.getLongRunningTransaction(ColdStart)).isNull()
|
||||
}
|
||||
|
||||
private fun TestScope.createAnalyticsColdStartWatcher(
|
||||
|
||||
@@ -10,7 +10,7 @@ package io.element.android.services.analytics.impl.watchers
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.libraries.matrix.api.roomlist.RoomListService
|
||||
import io.element.android.libraries.matrix.test.roomlist.FakeRoomListService
|
||||
import io.element.android.services.analytics.api.AnalyticsLongRunningTransaction.ResumeAppUntilNewRoomsReceived
|
||||
import io.element.android.services.analytics.api.AnalyticsLongRunningTransaction.CatchUp
|
||||
import io.element.android.services.analytics.test.FakeAnalyticsService
|
||||
import io.element.android.services.appnavstate.api.AppNavigationState
|
||||
import io.element.android.services.appnavstate.api.NavigationState
|
||||
@@ -49,14 +49,14 @@ class DefaultAnalyticsRoomListStateWatcherTest {
|
||||
runCurrent()
|
||||
|
||||
// The transaction should be present now
|
||||
assertThat(analyticsService.getLongRunningTransaction(ResumeAppUntilNewRoomsReceived)).isNotNull()
|
||||
assertThat(analyticsService.getLongRunningTransaction(CatchUp)).isNotNull()
|
||||
|
||||
// And now the room list service running
|
||||
roomListService.postState(RoomListService.State.Running)
|
||||
runCurrent()
|
||||
|
||||
// And the transaction should now be gone
|
||||
assertThat(analyticsService.getLongRunningTransaction(ResumeAppUntilNewRoomsReceived)).isNull()
|
||||
assertThat(analyticsService.getLongRunningTransaction(CatchUp)).isNull()
|
||||
|
||||
watcher.stop()
|
||||
}
|
||||
@@ -86,7 +86,7 @@ class DefaultAnalyticsRoomListStateWatcherTest {
|
||||
runCurrent()
|
||||
|
||||
// The transaction was never present
|
||||
assertThat(analyticsService.getLongRunningTransaction(ResumeAppUntilNewRoomsReceived)).isNull()
|
||||
assertThat(analyticsService.getLongRunningTransaction(CatchUp)).isNull()
|
||||
|
||||
watcher.stop()
|
||||
}
|
||||
@@ -116,12 +116,12 @@ class DefaultAnalyticsRoomListStateWatcherTest {
|
||||
runCurrent()
|
||||
|
||||
// The transaction should be present now
|
||||
assertThat(analyticsService.getLongRunningTransaction(ResumeAppUntilNewRoomsReceived)).isNotNull()
|
||||
assertThat(analyticsService.getLongRunningTransaction(CatchUp)).isNotNull()
|
||||
|
||||
runCurrent()
|
||||
|
||||
// But without the room list syncing, it never finishes
|
||||
assertThat(analyticsService.getLongRunningTransaction(ResumeAppUntilNewRoomsReceived)).isNotNull()
|
||||
assertThat(analyticsService.getLongRunningTransaction(CatchUp)).isNotNull()
|
||||
|
||||
watcher.stop()
|
||||
}
|
||||
@@ -151,7 +151,7 @@ class DefaultAnalyticsRoomListStateWatcherTest {
|
||||
runCurrent()
|
||||
|
||||
// The transaction was never added
|
||||
assertThat(analyticsService.getLongRunningTransaction(ResumeAppUntilNewRoomsReceived)).isNull()
|
||||
assertThat(analyticsService.getLongRunningTransaction(CatchUp)).isNull()
|
||||
|
||||
watcher.stop()
|
||||
}
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.services.analytics.impl.watchers
|
||||
|
||||
import io.element.android.libraries.matrix.api.room.SendQueueUpdate
|
||||
import io.element.android.libraries.matrix.test.AN_EVENT_ID
|
||||
import io.element.android.libraries.matrix.test.A_TRANSACTION_ID
|
||||
import io.element.android.libraries.matrix.test.room.FakeJoinedRoom
|
||||
import io.element.android.services.analytics.api.NoopAnalyticsTransaction
|
||||
import io.element.android.services.analytics.test.FakeAnalyticsService
|
||||
import io.element.android.tests.testutils.lambda.lambdaRecorder
|
||||
import io.mockk.mockk
|
||||
import io.mockk.verify
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.test.TestScope
|
||||
import kotlinx.coroutines.test.runCurrent
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Test
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
class DefaultAnalyticsSendMessageWatcherTest {
|
||||
@Test
|
||||
fun `test start listens to send queue updates`() = runTest {
|
||||
val mockedTransaction = mockk<NoopAnalyticsTransaction>(relaxed = true)
|
||||
val startTransactionRecorder = lambdaRecorder { _: String, _: String?, _: String? -> mockedTransaction }
|
||||
val room = FakeJoinedRoom()
|
||||
val analyticsService = FakeAnalyticsService(startTransactionLambda = startTransactionRecorder)
|
||||
|
||||
val watcher = createDefaultAnalyticsSendMessageWatcher(room = room, analyticsService = analyticsService)
|
||||
|
||||
// When we start listening, we don't trigger any analyticsService.startTransaction calls
|
||||
watcher.start()
|
||||
runCurrent()
|
||||
|
||||
startTransactionRecorder.assertions().isNeverCalled()
|
||||
|
||||
// When we receive a new local event, we start a new transaction for it
|
||||
room.givenSendQueueUpdate(SendQueueUpdate.NewLocalEvent(A_TRANSACTION_ID))
|
||||
runCurrent()
|
||||
|
||||
startTransactionRecorder.assertions().isCalledOnce()
|
||||
|
||||
// And we receive an 'event sent' update with the event's id, we finish the transaction
|
||||
room.givenSendQueueUpdate(SendQueueUpdate.SentEvent(A_TRANSACTION_ID, AN_EVENT_ID))
|
||||
runCurrent()
|
||||
|
||||
verify { mockedTransaction.finish() }
|
||||
|
||||
// We also stop the watcher for cleanup
|
||||
watcher.stop()
|
||||
}
|
||||
|
||||
private fun TestScope.createDefaultAnalyticsSendMessageWatcher(
|
||||
room: FakeJoinedRoom = FakeJoinedRoom(),
|
||||
analyticsService: FakeAnalyticsService = FakeAnalyticsService(),
|
||||
) = DefaultAnalyticsSendMessageWatcher(
|
||||
room = room,
|
||||
analyticsService = analyticsService,
|
||||
coroutineScope = backgroundScope,
|
||||
)
|
||||
}
|
||||
@@ -40,7 +40,7 @@ class NoopAnalyticsService : AnalyticsService {
|
||||
override fun updateUserProperties(userProperties: UserProperties) = Unit
|
||||
override fun trackError(throwable: Throwable) = Unit
|
||||
override fun updateSuperProperties(updatedProperties: SuperProperties) = Unit
|
||||
override fun startTransaction(name: String, operation: String?): AnalyticsTransaction = NoopAnalyticsTransaction
|
||||
override fun startTransaction(name: String, operation: String?, description: String?): AnalyticsTransaction = NoopAnalyticsTransaction
|
||||
override fun startLongRunningTransaction(
|
||||
longRunningTransaction: AnalyticsLongRunningTransaction,
|
||||
parentTransaction: AnalyticsTransaction?,
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.services.analytics.noop.watchers
|
||||
|
||||
import dev.zacsweers.metro.ContributesBinding
|
||||
import io.element.android.libraries.di.RoomScope
|
||||
import io.element.android.services.analytics.api.watchers.AnalyticsSendMessageWatcher
|
||||
|
||||
@ContributesBinding(RoomScope::class)
|
||||
class NoopAnalyticsSendMessageWatcher : AnalyticsSendMessageWatcher {
|
||||
override fun start() = Unit
|
||||
override fun stop() = Unit
|
||||
}
|
||||
@@ -26,6 +26,7 @@ import kotlinx.coroutines.flow.asStateFlow
|
||||
class FakeAnalyticsService(
|
||||
isEnabled: Boolean = false,
|
||||
didAskUserConsent: Boolean = false,
|
||||
private val startTransactionLambda: (String, String?, String?) -> AnalyticsTransaction = { _, _, _ -> NoopAnalyticsTransaction },
|
||||
) : AnalyticsService {
|
||||
private val isEnabledFlow = MutableStateFlow(isEnabled)
|
||||
override val didAskUserConsentFlow = MutableStateFlow(didAskUserConsent)
|
||||
@@ -72,7 +73,11 @@ class FakeAnalyticsService(
|
||||
// No op
|
||||
}
|
||||
|
||||
override fun startTransaction(name: String, operation: String?): AnalyticsTransaction = NoopAnalyticsTransaction
|
||||
override fun startTransaction(name: String, operation: String?, description: String?): AnalyticsTransaction = startTransactionLambda(
|
||||
name,
|
||||
operation,
|
||||
description
|
||||
)
|
||||
override fun startLongRunningTransaction(
|
||||
longRunningTransaction: AnalyticsLongRunningTransaction,
|
||||
parentTransaction: AnalyticsTransaction?
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Element Creations Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.services.analytics.test.watchers
|
||||
|
||||
import io.element.android.services.analytics.api.watchers.AnalyticsSendMessageWatcher
|
||||
|
||||
class FakeAnalyticsSendMessageWatcher(
|
||||
private val startLambda: () -> Unit = {},
|
||||
private val stopLambda: () -> Unit = {},
|
||||
) : AnalyticsSendMessageWatcher {
|
||||
override fun start() = startLambda()
|
||||
override fun stop() = stopLambda()
|
||||
}
|
||||
Reference in New Issue
Block a user