Make 'room list catch-up' analytics transaction network aware (#6233)
* Make 'room list catch-up' analytics transaction network aware. * Add `RoomListService.isInitialSyncDone`. Use this to simplify `DefaultAnalyticsRoomListStateWatcher`'s logic.
This commit is contained in:
committed by
GitHub
parent
1f69958dab
commit
70d5e1868a
@@ -35,6 +35,11 @@ interface RoomListService {
|
|||||||
data object Hide : SyncIndicator
|
data object Hide : SyncIndicator
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates whether the initial sliding sync request is done or not.
|
||||||
|
*/
|
||||||
|
val isInitialSyncDone: Boolean
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a room list that can be used to load more rooms and filter them dynamically.
|
* Creates a room list that can be used to load more rooms and filter them dynamically.
|
||||||
* @param pageSize the number of rooms to load at once.
|
* @param pageSize the number of rooms to load at once.
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import kotlinx.coroutines.flow.stateIn
|
|||||||
import org.matrix.rustcomponents.sdk.RoomListServiceState
|
import org.matrix.rustcomponents.sdk.RoomListServiceState
|
||||||
import org.matrix.rustcomponents.sdk.RoomListServiceSyncIndicator
|
import org.matrix.rustcomponents.sdk.RoomListServiceSyncIndicator
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean
|
||||||
import org.matrix.rustcomponents.sdk.RoomListService as InnerRustRoomListService
|
import org.matrix.rustcomponents.sdk.RoomListService as InnerRustRoomListService
|
||||||
|
|
||||||
internal class RustRoomListService(
|
internal class RustRoomListService(
|
||||||
@@ -33,6 +34,9 @@ internal class RustRoomListService(
|
|||||||
private val roomSyncSubscriber: RoomSyncSubscriber,
|
private val roomSyncSubscriber: RoomSyncSubscriber,
|
||||||
private val sessionCoroutineScope: CoroutineScope,
|
private val sessionCoroutineScope: CoroutineScope,
|
||||||
) : RoomListService {
|
) : RoomListService {
|
||||||
|
private val _isInitialSyncDone = AtomicBoolean(false)
|
||||||
|
override val isInitialSyncDone: Boolean get() = _isInitialSyncDone.get()
|
||||||
|
|
||||||
override fun createRoomList(
|
override fun createRoomList(
|
||||||
pageSize: Int,
|
pageSize: Int,
|
||||||
source: RoomList.Source,
|
source: RoomList.Source,
|
||||||
@@ -75,6 +79,9 @@ internal class RustRoomListService(
|
|||||||
.map { it.toRoomListState() }
|
.map { it.toRoomListState() }
|
||||||
.onEach { state ->
|
.onEach { state ->
|
||||||
Timber.d("RoomList state=$state")
|
Timber.d("RoomList state=$state")
|
||||||
|
if (state == RoomListService.State.Running) {
|
||||||
|
_isInitialSyncDone.set(true)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.distinctUntilChanged()
|
.distinctUntilChanged()
|
||||||
.stateIn(sessionCoroutineScope, SharingStarted.Eagerly, RoomListService.State.Idle)
|
.stateIn(sessionCoroutineScope, SharingStarted.Eagerly, RoomListService.State.Idle)
|
||||||
|
|||||||
@@ -20,10 +20,14 @@ class FakeRoomListService(
|
|||||||
private val subscribeToVisibleRoomsLambda: (List<RoomId>) -> Unit = {},
|
private val subscribeToVisibleRoomsLambda: (List<RoomId>) -> Unit = {},
|
||||||
private val createRoomListLambda: (pageSize: Int) -> DynamicRoomList = { pageSize -> FakeDynamicRoomList(pageSize = pageSize) },
|
private val createRoomListLambda: (pageSize: Int) -> DynamicRoomList = { pageSize -> FakeDynamicRoomList(pageSize = pageSize) },
|
||||||
override val allRooms: RoomList = createRoomListLambda(Int.MAX_VALUE),
|
override val allRooms: RoomList = createRoomListLambda(Int.MAX_VALUE),
|
||||||
|
private val isInitialSyncLambda: () -> Boolean = { true },
|
||||||
) : RoomListService {
|
) : RoomListService {
|
||||||
private val roomListStateFlow = MutableStateFlow<RoomListService.State>(RoomListService.State.Idle)
|
private val roomListStateFlow = MutableStateFlow<RoomListService.State>(RoomListService.State.Idle)
|
||||||
private val syncIndicatorStateFlow = MutableStateFlow<RoomListService.SyncIndicator>(RoomListService.SyncIndicator.Hide)
|
private val syncIndicatorStateFlow = MutableStateFlow<RoomListService.SyncIndicator>(RoomListService.SyncIndicator.Hide)
|
||||||
|
|
||||||
|
override val isInitialSyncDone: Boolean
|
||||||
|
get() = isInitialSyncLambda()
|
||||||
|
|
||||||
suspend fun postState(state: RoomListService.State) {
|
suspend fun postState(state: RoomListService.State) {
|
||||||
roomListStateFlow.emit(state)
|
roomListStateFlow.emit(state)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,8 +8,11 @@
|
|||||||
package io.element.android.services.analytics.api
|
package io.element.android.services.analytics.api
|
||||||
|
|
||||||
import io.element.android.services.analyticsproviders.api.AnalyticsTransaction
|
import io.element.android.services.analyticsproviders.api.AnalyticsTransaction
|
||||||
|
import kotlin.time.Duration
|
||||||
|
import kotlin.time.Duration.Companion.seconds
|
||||||
|
|
||||||
object NoopAnalyticsTransaction : AnalyticsTransaction {
|
object NoopAnalyticsTransaction : AnalyticsTransaction {
|
||||||
|
override val duration: Duration = 0.seconds
|
||||||
override fun startChild(operation: String, description: String?): AnalyticsTransaction = NoopAnalyticsTransaction
|
override fun startChild(operation: String, description: String?): AnalyticsTransaction = NoopAnalyticsTransaction
|
||||||
override fun putExtraData(key: String, value: String) {}
|
override fun putExtraData(key: String, value: String) {}
|
||||||
override fun putIndexableData(key: String, value: String) {}
|
override fun putIndexableData(key: String, value: String) {}
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ dependencies {
|
|||||||
implementation(projects.libraries.architecture)
|
implementation(projects.libraries.architecture)
|
||||||
implementation(projects.libraries.designsystem)
|
implementation(projects.libraries.designsystem)
|
||||||
implementation(projects.libraries.matrix.api)
|
implementation(projects.libraries.matrix.api)
|
||||||
|
implementation(projects.features.networkmonitor.api)
|
||||||
implementation(projects.libraries.preferences.api)
|
implementation(projects.libraries.preferences.api)
|
||||||
implementation(projects.libraries.sessionStorage.api)
|
implementation(projects.libraries.sessionStorage.api)
|
||||||
implementation(projects.services.appnavstate.api)
|
implementation(projects.services.appnavstate.api)
|
||||||
@@ -37,6 +38,7 @@ dependencies {
|
|||||||
testCommonDependencies(libs)
|
testCommonDependencies(libs)
|
||||||
testImplementation(projects.libraries.matrix.test)
|
testImplementation(projects.libraries.matrix.test)
|
||||||
testImplementation(projects.libraries.sessionStorage.test)
|
testImplementation(projects.libraries.sessionStorage.test)
|
||||||
|
testImplementation(projects.features.networkmonitor.test)
|
||||||
testImplementation(projects.services.analytics.test)
|
testImplementation(projects.services.analytics.test)
|
||||||
testImplementation(projects.services.analyticsproviders.test)
|
testImplementation(projects.services.analyticsproviders.test)
|
||||||
testImplementation(projects.services.appnavstate.test)
|
testImplementation(projects.services.appnavstate.test)
|
||||||
|
|||||||
@@ -8,29 +8,32 @@
|
|||||||
package io.element.android.services.analytics.impl.watchers
|
package io.element.android.services.analytics.impl.watchers
|
||||||
|
|
||||||
import dev.zacsweers.metro.ContributesBinding
|
import dev.zacsweers.metro.ContributesBinding
|
||||||
|
import io.element.android.features.networkmonitor.api.NetworkMonitor
|
||||||
|
import io.element.android.features.networkmonitor.api.NetworkStatus
|
||||||
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
|
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
|
||||||
import io.element.android.libraries.core.coroutine.childScope
|
import io.element.android.libraries.core.coroutine.childScope
|
||||||
import io.element.android.libraries.core.coroutine.withPreviousValue
|
|
||||||
import io.element.android.libraries.di.SessionScope
|
import io.element.android.libraries.di.SessionScope
|
||||||
import io.element.android.libraries.di.annotations.SessionCoroutineScope
|
import io.element.android.libraries.di.annotations.SessionCoroutineScope
|
||||||
import io.element.android.libraries.matrix.api.roomlist.RoomListService
|
import io.element.android.libraries.matrix.api.roomlist.RoomListService
|
||||||
import io.element.android.services.analytics.api.AnalyticsLongRunningTransaction
|
import io.element.android.services.analytics.api.AnalyticsLongRunningTransaction
|
||||||
import io.element.android.services.analytics.api.AnalyticsService
|
import io.element.android.services.analytics.api.AnalyticsService
|
||||||
import io.element.android.services.analytics.api.finishLongRunningTransaction
|
|
||||||
import io.element.android.services.analytics.api.watchers.AnalyticsRoomListStateWatcher
|
import io.element.android.services.analytics.api.watchers.AnalyticsRoomListStateWatcher
|
||||||
import io.element.android.services.appnavstate.api.AppNavigationStateService
|
import io.element.android.services.appnavstate.api.AppForegroundStateService
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.cancelChildren
|
import kotlinx.coroutines.cancelChildren
|
||||||
|
import kotlinx.coroutines.flow.combine
|
||||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||||
import kotlinx.coroutines.flow.launchIn
|
import kotlinx.coroutines.flow.launchIn
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
import kotlinx.coroutines.flow.onEach
|
import kotlinx.coroutines.flow.onEach
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.util.concurrent.atomic.AtomicBoolean
|
import java.util.concurrent.atomic.AtomicBoolean
|
||||||
|
import kotlin.time.Duration.Companion.minutes
|
||||||
|
|
||||||
@ContributesBinding(SessionScope::class)
|
@ContributesBinding(SessionScope::class)
|
||||||
class DefaultAnalyticsRoomListStateWatcher(
|
class DefaultAnalyticsRoomListStateWatcher(
|
||||||
private val appNavigationStateService: AppNavigationStateService,
|
private val appForegroundStateService: AppForegroundStateService,
|
||||||
|
private val networkMonitor: NetworkMonitor,
|
||||||
private val roomListService: RoomListService,
|
private val roomListService: RoomListService,
|
||||||
private val analyticsService: AnalyticsService,
|
private val analyticsService: AnalyticsService,
|
||||||
@SessionCoroutineScope sessionCoroutineScope: CoroutineScope,
|
@SessionCoroutineScope sessionCoroutineScope: CoroutineScope,
|
||||||
@@ -38,7 +41,7 @@ class DefaultAnalyticsRoomListStateWatcher(
|
|||||||
) : AnalyticsRoomListStateWatcher {
|
) : AnalyticsRoomListStateWatcher {
|
||||||
private val coroutineScope: CoroutineScope = sessionCoroutineScope.childScope(dispatchers.computation, "AnalyticsRoomListStateWatcher")
|
private val coroutineScope: CoroutineScope = sessionCoroutineScope.childScope(dispatchers.computation, "AnalyticsRoomListStateWatcher")
|
||||||
private val isStarted = AtomicBoolean(false)
|
private val isStarted = AtomicBoolean(false)
|
||||||
private val isWarmState = AtomicBoolean(false)
|
private val isNotInitialSync get() = roomListService.isInitialSyncDone
|
||||||
|
|
||||||
override fun start() {
|
override fun start() {
|
||||||
if (isStarted.getAndSet(true)) {
|
if (isStarted.getAndSet(true)) {
|
||||||
@@ -48,27 +51,40 @@ class DefaultAnalyticsRoomListStateWatcher(
|
|||||||
|
|
||||||
val longRunningTransaction = AnalyticsLongRunningTransaction.CatchUp
|
val longRunningTransaction = AnalyticsLongRunningTransaction.CatchUp
|
||||||
|
|
||||||
appNavigationStateService.appNavigationState
|
val hasNetworkConnectivityFlow = networkMonitor.connectivity
|
||||||
.map { it.isInForeground }
|
.map { it == NetworkStatus.Connected }
|
||||||
.distinctUntilChanged()
|
.distinctUntilChanged()
|
||||||
.withPreviousValue()
|
|
||||||
.onEach { (wasInForeground, isInForeground) ->
|
|
||||||
if (isInForeground && roomListService.state.value != RoomListService.State.Running) {
|
|
||||||
analyticsService.startLongRunningTransaction(longRunningTransaction)
|
|
||||||
} else if (!isInForeground) {
|
|
||||||
analyticsService.removeLongRunningTransaction(longRunningTransaction)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (wasInForeground == false && isInForeground) {
|
combine(
|
||||||
isWarmState.set(true)
|
appForegroundStateService.isInForeground,
|
||||||
|
hasNetworkConnectivityFlow,
|
||||||
|
) { isInForeground, hasNetworkConnectivity ->
|
||||||
|
val canSync = isInForeground && hasNetworkConnectivity
|
||||||
|
val isNotSyncing = roomListService.state.value != RoomListService.State.Running
|
||||||
|
if (isNotInitialSync && canSync && isNotSyncing) {
|
||||||
|
Timber.d("Catch-up transaction: starting")
|
||||||
|
analyticsService.startLongRunningTransaction(longRunningTransaction)
|
||||||
|
} else if (!isInForeground || !hasNetworkConnectivity) {
|
||||||
|
analyticsService.removeLongRunningTransaction(longRunningTransaction)?.let {
|
||||||
|
Timber.d("Catch-up transaction: stopping")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.launchIn(coroutineScope)
|
.launchIn(coroutineScope)
|
||||||
|
|
||||||
roomListService.state
|
roomListService.state
|
||||||
.onEach { state ->
|
.onEach { state ->
|
||||||
if (state == RoomListService.State.Running && isWarmState.get()) {
|
if (state == RoomListService.State.Running && isNotInitialSync) {
|
||||||
analyticsService.finishLongRunningTransaction(longRunningTransaction)
|
val transaction = analyticsService.removeLongRunningTransaction(longRunningTransaction)
|
||||||
|
if (transaction != null && !transaction.isFinished()) {
|
||||||
|
val duration = transaction.duration
|
||||||
|
if (duration > 3.minutes) {
|
||||||
|
Timber.d("Cancelling catch-up transaction, the elapsed time is too long ($duration), something probably went wrong while measuring")
|
||||||
|
} else {
|
||||||
|
Timber.d("Catch-up transaction finished in $duration")
|
||||||
|
transaction.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.launchIn(coroutineScope)
|
.launchIn(coroutineScope)
|
||||||
|
|||||||
@@ -8,13 +8,12 @@
|
|||||||
package io.element.android.services.analytics.impl.watchers
|
package io.element.android.services.analytics.impl.watchers
|
||||||
|
|
||||||
import com.google.common.truth.Truth.assertThat
|
import com.google.common.truth.Truth.assertThat
|
||||||
|
import io.element.android.features.networkmonitor.test.FakeNetworkMonitor
|
||||||
import io.element.android.libraries.matrix.api.roomlist.RoomListService
|
import io.element.android.libraries.matrix.api.roomlist.RoomListService
|
||||||
import io.element.android.libraries.matrix.test.roomlist.FakeRoomListService
|
import io.element.android.libraries.matrix.test.roomlist.FakeRoomListService
|
||||||
import io.element.android.services.analytics.api.AnalyticsLongRunningTransaction.CatchUp
|
import io.element.android.services.analytics.api.AnalyticsLongRunningTransaction.CatchUp
|
||||||
import io.element.android.services.analytics.test.FakeAnalyticsService
|
import io.element.android.services.analytics.test.FakeAnalyticsService
|
||||||
import io.element.android.services.appnavstate.api.AppNavigationState
|
import io.element.android.services.appnavstate.test.FakeAppForegroundStateService
|
||||||
import io.element.android.services.appnavstate.api.NavigationState
|
|
||||||
import io.element.android.services.appnavstate.test.FakeAppNavigationStateService
|
|
||||||
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.test.TestScope
|
import kotlinx.coroutines.test.TestScope
|
||||||
@@ -26,13 +25,13 @@ import org.junit.Test
|
|||||||
class DefaultAnalyticsRoomListStateWatcherTest {
|
class DefaultAnalyticsRoomListStateWatcherTest {
|
||||||
@Test
|
@Test
|
||||||
fun `Opening the app in a warm state tracks the time until the room list is synced`() = runTest {
|
fun `Opening the app in a warm state tracks the time until the room list is synced`() = runTest {
|
||||||
val navigationStateService = FakeAppNavigationStateService()
|
val appForegroundStateService = FakeAppForegroundStateService()
|
||||||
val roomListService = FakeRoomListService().apply {
|
val roomListService = FakeRoomListService().apply {
|
||||||
postState(RoomListService.State.Idle)
|
postState(RoomListService.State.Idle)
|
||||||
}
|
}
|
||||||
val analyticsService = FakeAnalyticsService()
|
val analyticsService = FakeAnalyticsService()
|
||||||
val watcher = createAnalyticsRoomListStateWatcher(
|
val watcher = createAnalyticsRoomListStateWatcher(
|
||||||
appNavigationStateService = navigationStateService,
|
appForegroundStateService = appForegroundStateService,
|
||||||
roomListService = roomListService,
|
roomListService = roomListService,
|
||||||
analyticsService = analyticsService,
|
analyticsService = analyticsService,
|
||||||
)
|
)
|
||||||
@@ -43,9 +42,9 @@ class DefaultAnalyticsRoomListStateWatcherTest {
|
|||||||
runCurrent()
|
runCurrent()
|
||||||
|
|
||||||
// Make sure it's warm by changing its internal state
|
// Make sure it's warm by changing its internal state
|
||||||
navigationStateService.emitNavigationState(AppNavigationState(navigationState = NavigationState.Root, isInForeground = false))
|
appForegroundStateService.givenIsInForeground(false)
|
||||||
runCurrent()
|
runCurrent()
|
||||||
navigationStateService.emitNavigationState(AppNavigationState(navigationState = NavigationState.Root, isInForeground = true))
|
appForegroundStateService.givenIsInForeground(true)
|
||||||
runCurrent()
|
runCurrent()
|
||||||
|
|
||||||
// The transaction should be present now
|
// The transaction should be present now
|
||||||
@@ -63,15 +62,15 @@ class DefaultAnalyticsRoomListStateWatcherTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `Opening the app in a cold state does nothing`() = runTest {
|
fun `Opening the app in a cold state does nothing`() = runTest {
|
||||||
val navigationStateService = FakeAppNavigationStateService(
|
val appForegroundStateService = FakeAppForegroundStateService(
|
||||||
initialAppNavigationState = AppNavigationState(NavigationState.Root, false)
|
initialForegroundValue = false
|
||||||
)
|
)
|
||||||
val roomListService = FakeRoomListService().apply {
|
val roomListService = FakeRoomListService().apply {
|
||||||
postState(RoomListService.State.Idle)
|
postState(RoomListService.State.Idle)
|
||||||
}
|
}
|
||||||
val analyticsService = FakeAnalyticsService()
|
val analyticsService = FakeAnalyticsService()
|
||||||
val watcher = createAnalyticsRoomListStateWatcher(
|
val watcher = createAnalyticsRoomListStateWatcher(
|
||||||
appNavigationStateService = navigationStateService,
|
appForegroundStateService = appForegroundStateService,
|
||||||
roomListService = roomListService,
|
roomListService = roomListService,
|
||||||
analyticsService = analyticsService,
|
analyticsService = analyticsService,
|
||||||
)
|
)
|
||||||
@@ -93,13 +92,13 @@ class DefaultAnalyticsRoomListStateWatcherTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `The transaction won't be finished until the room list is synchronised`() = runTest {
|
fun `The transaction won't be finished until the room list is synchronised`() = runTest {
|
||||||
val navigationStateService = FakeAppNavigationStateService()
|
val appForegroundStateService = FakeAppForegroundStateService()
|
||||||
val roomListService = FakeRoomListService().apply {
|
val roomListService = FakeRoomListService().apply {
|
||||||
postState(RoomListService.State.Idle)
|
postState(RoomListService.State.Idle)
|
||||||
}
|
}
|
||||||
val analyticsService = FakeAnalyticsService()
|
val analyticsService = FakeAnalyticsService()
|
||||||
val watcher = createAnalyticsRoomListStateWatcher(
|
val watcher = createAnalyticsRoomListStateWatcher(
|
||||||
appNavigationStateService = navigationStateService,
|
appForegroundStateService = appForegroundStateService,
|
||||||
roomListService = roomListService,
|
roomListService = roomListService,
|
||||||
analyticsService = analyticsService,
|
analyticsService = analyticsService,
|
||||||
)
|
)
|
||||||
@@ -110,9 +109,9 @@ class DefaultAnalyticsRoomListStateWatcherTest {
|
|||||||
runCurrent()
|
runCurrent()
|
||||||
|
|
||||||
// Make sure it's warm by changing its internal state
|
// Make sure it's warm by changing its internal state
|
||||||
navigationStateService.emitNavigationState(AppNavigationState(navigationState = NavigationState.Root, isInForeground = false))
|
appForegroundStateService.givenIsInForeground(false)
|
||||||
runCurrent()
|
runCurrent()
|
||||||
navigationStateService.emitNavigationState(AppNavigationState(navigationState = NavigationState.Root, isInForeground = true))
|
appForegroundStateService.givenIsInForeground(true)
|
||||||
runCurrent()
|
runCurrent()
|
||||||
|
|
||||||
// The transaction should be present now
|
// The transaction should be present now
|
||||||
@@ -128,13 +127,13 @@ class DefaultAnalyticsRoomListStateWatcherTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `Opening the app when the room list state was already Running does nothing`() = runTest {
|
fun `Opening the app when the room list state was already Running does nothing`() = runTest {
|
||||||
val navigationStateService = FakeAppNavigationStateService()
|
val appForegroundStateService = FakeAppForegroundStateService()
|
||||||
val roomListService = FakeRoomListService().apply {
|
val roomListService = FakeRoomListService().apply {
|
||||||
postState(RoomListService.State.Running)
|
postState(RoomListService.State.Running)
|
||||||
}
|
}
|
||||||
val analyticsService = FakeAnalyticsService()
|
val analyticsService = FakeAnalyticsService()
|
||||||
val watcher = createAnalyticsRoomListStateWatcher(
|
val watcher = createAnalyticsRoomListStateWatcher(
|
||||||
appNavigationStateService = navigationStateService,
|
appForegroundStateService = appForegroundStateService,
|
||||||
roomListService = roomListService,
|
roomListService = roomListService,
|
||||||
analyticsService = analyticsService,
|
analyticsService = analyticsService,
|
||||||
)
|
)
|
||||||
@@ -145,9 +144,9 @@ class DefaultAnalyticsRoomListStateWatcherTest {
|
|||||||
runCurrent()
|
runCurrent()
|
||||||
|
|
||||||
// Make sure it's warm by changing its internal state
|
// Make sure it's warm by changing its internal state
|
||||||
navigationStateService.emitNavigationState(AppNavigationState(navigationState = NavigationState.Root, isInForeground = false))
|
appForegroundStateService.givenIsInForeground(false)
|
||||||
runCurrent()
|
runCurrent()
|
||||||
navigationStateService.emitNavigationState(AppNavigationState(navigationState = NavigationState.Root, isInForeground = true))
|
appForegroundStateService.givenIsInForeground(true)
|
||||||
runCurrent()
|
runCurrent()
|
||||||
|
|
||||||
// The transaction was never added
|
// The transaction was never added
|
||||||
@@ -157,14 +156,16 @@ class DefaultAnalyticsRoomListStateWatcherTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun TestScope.createAnalyticsRoomListStateWatcher(
|
private fun TestScope.createAnalyticsRoomListStateWatcher(
|
||||||
appNavigationStateService: FakeAppNavigationStateService = FakeAppNavigationStateService(),
|
appForegroundStateService: FakeAppForegroundStateService = FakeAppForegroundStateService(),
|
||||||
roomListService: FakeRoomListService = FakeRoomListService(),
|
roomListService: FakeRoomListService = FakeRoomListService(),
|
||||||
analyticsService: FakeAnalyticsService = FakeAnalyticsService(),
|
analyticsService: FakeAnalyticsService = FakeAnalyticsService(),
|
||||||
|
networkMonitor: FakeNetworkMonitor = FakeNetworkMonitor(),
|
||||||
) = DefaultAnalyticsRoomListStateWatcher(
|
) = DefaultAnalyticsRoomListStateWatcher(
|
||||||
appNavigationStateService = appNavigationStateService,
|
appForegroundStateService = appForegroundStateService,
|
||||||
roomListService = roomListService,
|
roomListService = roomListService,
|
||||||
analyticsService = analyticsService,
|
analyticsService = analyticsService,
|
||||||
sessionCoroutineScope = backgroundScope,
|
sessionCoroutineScope = backgroundScope,
|
||||||
dispatchers = testCoroutineDispatchers(),
|
dispatchers = testCoroutineDispatchers(),
|
||||||
|
networkMonitor = networkMonitor,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,14 @@
|
|||||||
|
|
||||||
package io.element.android.services.analyticsproviders.api
|
package io.element.android.services.analyticsproviders.api
|
||||||
|
|
||||||
|
import kotlin.time.Duration
|
||||||
|
|
||||||
interface AnalyticsTransaction {
|
interface AnalyticsTransaction {
|
||||||
|
/**
|
||||||
|
* The time elapsed since the transaction started until now if the transaction is ongoing or the time it finished.
|
||||||
|
*/
|
||||||
|
val duration: Duration
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Start a child span from this transaction.
|
* Start a child span from this transaction.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -11,7 +11,10 @@ import io.element.android.services.analyticsproviders.api.AnalyticsTransaction
|
|||||||
import io.sentry.ISpan
|
import io.sentry.ISpan
|
||||||
import io.sentry.ITransaction
|
import io.sentry.ITransaction
|
||||||
import io.sentry.Sentry
|
import io.sentry.Sentry
|
||||||
|
import io.sentry.SentryInstantDate
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
import kotlin.time.Duration
|
||||||
|
import kotlin.time.Duration.Companion.nanoseconds
|
||||||
|
|
||||||
class SentryAnalyticsTransaction private constructor(span: ISpan) : AnalyticsTransaction {
|
class SentryAnalyticsTransaction private constructor(span: ISpan) : AnalyticsTransaction {
|
||||||
constructor(name: String, operation: String?, description: String? = null) : this(
|
constructor(name: String, operation: String?, description: String? = null) : this(
|
||||||
@@ -19,6 +22,11 @@ class SentryAnalyticsTransaction private constructor(span: ISpan) : AnalyticsTra
|
|||||||
)
|
)
|
||||||
private val inner = span
|
private val inner = span
|
||||||
|
|
||||||
|
@Suppress("UnstableApiUsage")
|
||||||
|
override val duration: Duration get() {
|
||||||
|
return (inner.finishDate ?: SentryInstantDate()).diff(inner.startDate).nanoseconds
|
||||||
|
}
|
||||||
|
|
||||||
override fun startChild(operation: String, description: String?): AnalyticsTransaction = SentryAnalyticsTransaction(
|
override fun startChild(operation: String, description: String?): AnalyticsTransaction = SentryAnalyticsTransaction(
|
||||||
inner.startChild(operation, description)
|
inner.startChild(operation, description)
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user