From 29117ce93dd89f4f956dd5da265bbdac72a8f8bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Mart=C3=ADn?= Date: Thu, 27 Nov 2025 12:27:49 +0100 Subject: [PATCH] Add `AnalyticsService.cancelLongRunningTransaction` and `AnalyticsService.finishLongRunningTransaction` --- .../room/joined/JoinedRoomLoadedFlowNode.kt | 3 ++- .../features/messages/impl/MessagesNode.kt | 3 ++- .../impl/timeline/TimelinePresenter.kt | 7 ++++--- .../matrix/impl/roomlist/RoomListFactory.kt | 3 ++- .../analytics/api/AnalyticsService.kt | 21 +++++++++++++++++++ .../DefaultAnalyticsColdStartWatcher.kt | 6 ++++-- .../DefaultAnalyticsRoomListStateWatcher.kt | 3 ++- 7 files changed, 37 insertions(+), 9 deletions(-) diff --git a/appnav/src/main/kotlin/io/element/android/appnav/room/joined/JoinedRoomLoadedFlowNode.kt b/appnav/src/main/kotlin/io/element/android/appnav/room/joined/JoinedRoomLoadedFlowNode.kt index 3ff8e16b49..5a6ef9133b 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/room/joined/JoinedRoomLoadedFlowNode.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/room/joined/JoinedRoomLoadedFlowNode.kt @@ -49,6 +49,7 @@ import io.element.android.services.analytics.api.AnalyticsLongRunningTransaction import io.element.android.services.analytics.api.AnalyticsLongRunningTransaction.LoadMessagesUi import io.element.android.services.analytics.api.AnalyticsLongRunningTransaction.OpenRoom import io.element.android.services.analytics.api.AnalyticsService +import io.element.android.services.analytics.api.finishLongRunningTransaction import io.element.android.services.appnavstate.api.ActiveRoomsHolder import io.element.android.services.appnavstate.api.AppNavigationStateService import kotlinx.coroutines.CoroutineScope @@ -107,7 +108,7 @@ class JoinedRoomLoadedFlowNode( trackVisitedRoom() }, onResume = { - analyticsService.removeLongRunningTransaction(LoadJoinedRoomFlow)?.finish() + analyticsService.finishLongRunningTransaction(LoadJoinedRoomFlow) sessionCoroutineScope.launch { inputs.room.subscribeToSync() } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNode.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNode.kt index 2032d70d64..0692a98745 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNode.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNode.kt @@ -70,6 +70,7 @@ import io.element.android.libraries.mediaplayer.api.MediaPlayer import io.element.android.libraries.ui.strings.CommonStrings import io.element.android.services.analytics.api.AnalyticsLongRunningTransaction.LoadMessagesUi import io.element.android.services.analytics.api.AnalyticsService +import io.element.android.services.analytics.api.finishLongRunningTransaction import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.CoroutineScope @@ -138,7 +139,7 @@ class MessagesNode( sessionCoroutineScope.launch { analyticsService.capture(room.toAnalyticsViewRoom()) } }, onResume = { - analyticsService.removeLongRunningTransaction(LoadMessagesUi)?.finish() + analyticsService.finishLongRunningTransaction(LoadMessagesUi) }, onDestroy = { mediaPlayer.close() diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt index 3044822256..127dab0345 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt @@ -59,6 +59,7 @@ import io.element.android.services.analytics.api.AnalyticsLongRunningTransaction import io.element.android.services.analytics.api.AnalyticsLongRunningTransaction.NotificationTapOpensTimeline import io.element.android.services.analytics.api.AnalyticsLongRunningTransaction.OpenRoom import io.element.android.services.analytics.api.AnalyticsService +import io.element.android.services.analytics.api.finishLongRunningTransaction import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf import kotlinx.coroutines.CoroutineScope @@ -239,9 +240,9 @@ class TimelinePresenter( timelineItems = newTimelineItems analyticsService.run { - removeLongRunningTransaction(DisplayFirstTimelineItems)?.finish() - removeLongRunningTransaction(OpenRoom)?.finish() - removeLongRunningTransaction(NotificationTapOpensTimeline)?.finish() + finishLongRunningTransaction(DisplayFirstTimelineItems) + finishLongRunningTransaction(OpenRoom) + finishLongRunningTransaction(NotificationTapOpensTimeline) } } .launchIn(this) diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomListFactory.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomListFactory.kt index 1fd0871019..b15411c382 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomListFactory.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomListFactory.kt @@ -14,6 +14,7 @@ import io.element.android.libraries.matrix.api.roomlist.RoomListFilter import io.element.android.libraries.matrix.api.roomlist.RoomSummary import io.element.android.services.analytics.api.AnalyticsLongRunningTransaction import io.element.android.services.analytics.api.AnalyticsService +import io.element.android.services.analytics.api.finishLongRunningTransaction import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow @@ -73,7 +74,7 @@ internal class RoomListFactory( initialFilterKind = RoomListEntriesDynamicFilterKind.All(ROOM_LIST_RUST_FILTERS), ).onEach { update -> if (!firstRoomsTransaction.isFinished()) { - analyticsService.removeLongRunningTransaction(AnalyticsLongRunningTransaction.FirstRoomsDisplayed)?.finish() + analyticsService.finishLongRunningTransaction(AnalyticsLongRunningTransaction.FirstRoomsDisplayed) firstRoomsTransaction.finish() } processor.postUpdate(update) diff --git a/services/analytics/api/src/main/kotlin/io/element/android/services/analytics/api/AnalyticsService.kt b/services/analytics/api/src/main/kotlin/io/element/android/services/analytics/api/AnalyticsService.kt index 1e95263509..08562b01ce 100644 --- a/services/analytics/api/src/main/kotlin/io/element/android/services/analytics/api/AnalyticsService.kt +++ b/services/analytics/api/src/main/kotlin/io/element/android/services/analytics/api/AnalyticsService.kt @@ -89,3 +89,24 @@ inline fun AnalyticsService.recordTransaction( transaction.finish() } } + +/** + * Cancels a long running transaction. It behaves the same as [AnalyticsService.removeLongRunningTransaction], + * but it doesn't return the transaction so we can't finish it later. + */ +fun AnalyticsService.cancelLongRunningTransaction( + longRunningTransaction: AnalyticsLongRunningTransaction +) = removeLongRunningTransaction(longRunningTransaction) + +/** + * Finishes a long running transaction if it exists. Optionally performs an [action] with the transaction before finishing it. + */ +fun AnalyticsService.finishLongRunningTransaction( + longRunningTransaction: AnalyticsLongRunningTransaction, + action: (AnalyticsTransaction) -> Unit = {}, +) { + removeLongRunningTransaction(longRunningTransaction)?.let { + action(it) + it.finish() + } +} diff --git a/services/analytics/impl/src/main/kotlin/io/element/android/services/analytics/impl/watchers/DefaultAnalyticsColdStartWatcher.kt b/services/analytics/impl/src/main/kotlin/io/element/android/services/analytics/impl/watchers/DefaultAnalyticsColdStartWatcher.kt index f5ef72ba8d..c53e28918b 100644 --- a/services/analytics/impl/src/main/kotlin/io/element/android/services/analytics/impl/watchers/DefaultAnalyticsColdStartWatcher.kt +++ b/services/analytics/impl/src/main/kotlin/io/element/android/services/analytics/impl/watchers/DefaultAnalyticsColdStartWatcher.kt @@ -13,6 +13,8 @@ import dev.zacsweers.metro.SingleIn import io.element.android.libraries.di.annotations.AppCoroutineScope import io.element.android.services.analytics.api.AnalyticsLongRunningTransaction import io.element.android.services.analytics.api.AnalyticsService +import io.element.android.services.analytics.api.cancelLongRunningTransaction +import io.element.android.services.analytics.api.finishLongRunningTransaction import io.element.android.services.analytics.api.watchers.AnalyticsColdStartWatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.catch @@ -47,7 +49,7 @@ class DefaultAnalyticsColdStartWatcher( override fun whenLoggingIn() { if (isColdStart.getAndSet(false)) { - analyticsService.removeLongRunningTransaction(AnalyticsLongRunningTransaction.ColdStartUntilCachedRoomList) + analyticsService.cancelLongRunningTransaction(AnalyticsLongRunningTransaction.ColdStartUntilCachedRoomList) Timber.d("Canceled cold start check: user is logging in") } } @@ -55,7 +57,7 @@ class DefaultAnalyticsColdStartWatcher( override fun onRoomListVisible() { if (isColdStart.getAndSet(false)) { Timber.d("Room list is visible, finishing cold start check") - analyticsService.removeLongRunningTransaction(AnalyticsLongRunningTransaction.ColdStartUntilCachedRoomList)?.finish() + analyticsService.finishLongRunningTransaction(AnalyticsLongRunningTransaction.ColdStartUntilCachedRoomList) } } } diff --git a/services/analytics/impl/src/main/kotlin/io/element/android/services/analytics/impl/watchers/DefaultAnalyticsRoomListStateWatcher.kt b/services/analytics/impl/src/main/kotlin/io/element/android/services/analytics/impl/watchers/DefaultAnalyticsRoomListStateWatcher.kt index c2a82eb49b..cd1af657b2 100644 --- a/services/analytics/impl/src/main/kotlin/io/element/android/services/analytics/impl/watchers/DefaultAnalyticsRoomListStateWatcher.kt +++ b/services/analytics/impl/src/main/kotlin/io/element/android/services/analytics/impl/watchers/DefaultAnalyticsRoomListStateWatcher.kt @@ -16,6 +16,7 @@ import io.element.android.libraries.di.annotations.SessionCoroutineScope import io.element.android.libraries.matrix.api.roomlist.RoomListService import io.element.android.services.analytics.api.AnalyticsLongRunningTransaction 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.appnavstate.api.AppNavigationStateService import kotlinx.coroutines.CoroutineScope @@ -63,7 +64,7 @@ class DefaultAnalyticsRoomListStateWatcher( roomListService.state .onEach { state -> if (state == RoomListService.State.Running && isWarmState.get()) { - analyticsService.removeLongRunningTransaction(AnalyticsLongRunningTransaction.ResumeAppUntilNewRoomsReceived)?.finish() + analyticsService.finishLongRunningTransaction(AnalyticsLongRunningTransaction.ResumeAppUntilNewRoomsReceived) } } .launchIn(coroutineScope)