Skip to content

Commit 06c4b94

Browse files
authored
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
1 parent a9e095d commit 06c4b94

File tree

33 files changed

+443
-48
lines changed

33 files changed

+443
-48
lines changed

appnav/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ dependencies {
6363
testImplementation(projects.libraries.push.test)
6464
testImplementation(projects.libraries.pushproviders.test)
6565
testImplementation(projects.features.forward.test)
66+
testImplementation(projects.features.messages.test)
6667
testImplementation(projects.features.networkmonitor.test)
6768
testImplementation(projects.features.rageshake.test)
6869
testImplementation(projects.services.appnavstate.impl)

appnav/src/main/kotlin/io/element/android/appnav/RootFlowNode.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -320,7 +320,7 @@ class RootFlowNode(
320320
is ResolvedIntent.Navigation -> {
321321
val openingRoomFromNotification = intent.getBooleanExtra(ROOM_OPENED_FROM_NOTIFICATION, false)
322322
if (openingRoomFromNotification && resolvedIntent.deeplinkData is DeeplinkData.Room) {
323-
analyticsService.startLongRunningTransaction(AnalyticsLongRunningTransaction.NotificationTapOpensTimeline)
323+
analyticsService.startLongRunningTransaction(AnalyticsLongRunningTransaction.NotificationToMessage)
324324
}
325325
navigateTo(resolvedIntent.deeplinkData)
326326
}

appnav/src/main/kotlin/io/element/android/appnav/di/TimelineBindings.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,10 @@ package io.element.android.appnav.di
1010

1111
import io.element.android.features.messages.api.pinned.PinnedEventsTimelineProvider
1212
import io.element.android.libraries.matrix.api.timeline.TimelineProvider
13+
import io.element.android.services.analytics.api.watchers.AnalyticsSendMessageWatcher
1314

1415
interface TimelineBindings {
1516
val timelineProvider: TimelineProvider
1617
val pinnedEventsTimelineProvider: PinnedEventsTimelineProvider
18+
val analyticsSendMessageWatcher: AnalyticsSendMessageWatcher
1719
}

appnav/src/main/kotlin/io/element/android/appnav/room/RoomFlowNode.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ import io.element.android.libraries.matrix.api.room.RoomMembershipObserver
4949
import io.element.android.libraries.matrix.api.room.alias.ResolvedRoomAlias
5050
import io.element.android.libraries.matrix.ui.room.LoadingRoomState
5151
import io.element.android.services.analytics.api.AnalyticsLongRunningTransaction.LoadJoinedRoomFlow
52-
import io.element.android.services.analytics.api.AnalyticsLongRunningTransaction.NotificationTapOpensTimeline
52+
import io.element.android.services.analytics.api.AnalyticsLongRunningTransaction.NotificationToMessage
5353
import io.element.android.services.analytics.api.AnalyticsLongRunningTransaction.OpenRoom
5454
import io.element.android.services.analytics.api.AnalyticsService
5555
import kotlinx.coroutines.flow.SharingStarted
@@ -128,7 +128,7 @@ class RoomFlowNode(
128128

129129
override fun onBuilt() {
130130
super.onBuilt()
131-
val parentTransaction = analyticsService.getLongRunningTransaction(NotificationTapOpensTimeline)
131+
val parentTransaction = analyticsService.getLongRunningTransaction(NotificationToMessage)
132132
val openRoomTransaction = analyticsService.startLongRunningTransaction(OpenRoom, parentTransaction)
133133
analyticsService.startLongRunningTransaction(LoadJoinedRoomFlow, openRoomTransaction)
134134
resolveRoomId()

appnav/src/main/kotlin/io/element/android/appnav/room/joined/JoinedRoomLoadedFlowNode.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,8 @@ class JoinedRoomLoadedFlowNode(
9898
private val callback: Callback = callback()
9999
override val graph = roomGraphFactory.create(inputs.room)
100100

101+
private val sendMessageWatcher = (graph as? TimelineBindings)?.analyticsSendMessageWatcher
102+
101103
// This is an ugly hack to check activity recreation
102104
private var currentActivity: Activity? = null
103105

@@ -109,6 +111,7 @@ class JoinedRoomLoadedFlowNode(
109111
Timber.v("OnCreate => ${inputs.room.roomId}")
110112
appNavigationStateService.onNavigateToRoom(id, inputs.room.roomId)
111113
activeRoomsHolder.addRoom(inputs.room)
114+
sendMessageWatcher?.start()
112115
fetchRoomMembers()
113116
trackVisitedRoom()
114117
},
@@ -120,6 +123,7 @@ class JoinedRoomLoadedFlowNode(
120123
},
121124
onDestroy = {
122125
Timber.v("OnDestroy")
126+
sendMessageWatcher?.stop()
123127
// If we're just going through an activity recreation there's no need to destroy the Room object
124128
// Destroying it would actually cause an issue where its methods can no longer be called
125129
if (currentActivity?.isChangingConfigurations != true) {

appnav/src/test/kotlin/io/element/android/appnav/JoinedRoomLoadedFlowNodeTest.kt

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,22 +19,29 @@ import com.bumble.appyx.testing.junit4.util.MainDispatcherRule
1919
import com.bumble.appyx.testing.unit.common.helper.parentNodeTestHelper
2020
import com.google.common.truth.Truth.assertThat
2121
import io.element.android.appnav.di.RoomGraphFactory
22+
import io.element.android.appnav.di.TimelineBindings
2223
import io.element.android.appnav.room.RoomNavigationTarget
2324
import io.element.android.appnav.room.joined.FakeJoinedRoomLoadedFlowNodeCallback
2425
import io.element.android.appnav.room.joined.JoinedRoomLoadedFlowNode
2526
import io.element.android.features.forward.api.ForwardEntryPoint
2627
import io.element.android.features.forward.test.FakeForwardEntryPoint
2728
import io.element.android.features.messages.api.MessagesEntryPoint
29+
import io.element.android.features.messages.api.pinned.PinnedEventsTimelineProvider
30+
import io.element.android.features.messages.test.pinned.FakePinnedEventsTimelineProvider
2831
import io.element.android.features.roomdetails.api.RoomDetailsEntryPoint
2932
import io.element.android.features.space.api.SpaceEntryPoint
3033
import io.element.android.libraries.architecture.childNode
3134
import io.element.android.libraries.matrix.api.room.JoinedRoom
35+
import io.element.android.libraries.matrix.api.timeline.TimelineProvider
3236
import io.element.android.libraries.matrix.test.A_SESSION_ID
3337
import io.element.android.libraries.matrix.test.FakeMatrixClient
3438
import io.element.android.libraries.matrix.test.room.FakeBaseRoom
3539
import io.element.android.libraries.matrix.test.room.FakeJoinedRoom
3640
import io.element.android.libraries.matrix.test.room.aRoomInfo
41+
import io.element.android.libraries.matrix.test.timeline.FakeTimelineProvider
42+
import io.element.android.services.analytics.api.watchers.AnalyticsSendMessageWatcher
3743
import io.element.android.services.analytics.test.FakeAnalyticsService
44+
import io.element.android.services.analytics.test.watchers.FakeAnalyticsSendMessageWatcher
3845
import io.element.android.services.appnavstate.api.ActiveRoomsHolder
3946
import io.element.android.services.appnavstate.impl.DefaultActiveRoomsHolder
4047
import io.element.android.services.appnavstate.test.FakeAppNavigationStateService
@@ -72,9 +79,20 @@ class JoinedRoomLoadedFlowNodeTest {
7279
}
7380
}
7481

75-
private class FakeRoomGraphFactory : RoomGraphFactory {
82+
private class FakeRoomGraphFactory(
83+
private val timelineProvider: FakeTimelineProvider = FakeTimelineProvider(),
84+
private val pinnedEventsTimelineProvider: FakePinnedEventsTimelineProvider = FakePinnedEventsTimelineProvider(),
85+
private val analyticsSendMessageWatcher: FakeAnalyticsSendMessageWatcher = FakeAnalyticsSendMessageWatcher(),
86+
) : RoomGraphFactory {
7687
override fun create(room: JoinedRoom): Any {
77-
return Unit
88+
return object : TimelineBindings {
89+
override val timelineProvider: TimelineProvider
90+
get() = this@FakeRoomGraphFactory.timelineProvider
91+
override val pinnedEventsTimelineProvider: PinnedEventsTimelineProvider
92+
get() = this@FakeRoomGraphFactory.pinnedEventsTimelineProvider
93+
override val analyticsSendMessageWatcher: AnalyticsSendMessageWatcher
94+
get() = this@FakeRoomGraphFactory.analyticsSendMessageWatcher
95+
}
7896
}
7997
}
8098

features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ import io.element.android.libraries.matrix.api.timeline.item.event.MessageShield
5656
import io.element.android.libraries.matrix.api.timeline.item.event.TimelineItemEventOrigin
5757
import io.element.android.libraries.preferences.api.store.SessionPreferencesStore
5858
import io.element.android.services.analytics.api.AnalyticsLongRunningTransaction.DisplayFirstTimelineItems
59-
import io.element.android.services.analytics.api.AnalyticsLongRunningTransaction.NotificationTapOpensTimeline
59+
import io.element.android.services.analytics.api.AnalyticsLongRunningTransaction.NotificationToMessage
6060
import io.element.android.services.analytics.api.AnalyticsLongRunningTransaction.OpenRoom
6161
import io.element.android.services.analytics.api.AnalyticsService
6262
import io.element.android.services.analytics.api.finishLongRunningTransaction
@@ -205,7 +205,7 @@ class TimelinePresenter(
205205
}.start()
206206
is TimelineEvents.OnFocusEventRender -> {
207207
// If there was a pending 'notification tap opens timeline' transaction, finish it now we're focused in the required event
208-
analyticsService.finishLongRunningTransaction(NotificationTapOpensTimeline)
208+
analyticsService.finishLongRunningTransaction(NotificationToMessage)
209209

210210
focusRequestState.value = focusRequestState.value.onFocusEventRender()
211211
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
/*
2+
* Copyright (c) 2025 Element Creations Ltd.
3+
*
4+
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
5+
* Please see LICENSE files in the repository root for full details.
6+
*/
7+
8+
package io.element.android.features.messages.test.pinned
9+
10+
import io.element.android.features.messages.api.pinned.PinnedEventsTimelineProvider
11+
import io.element.android.libraries.matrix.api.timeline.Timeline
12+
import io.element.android.libraries.matrix.test.timeline.FakeTimelineProvider
13+
import kotlinx.coroutines.flow.StateFlow
14+
15+
class FakePinnedEventsTimelineProvider(
16+
private val fakeTimelineProvider: FakeTimelineProvider = FakeTimelineProvider(),
17+
) : PinnedEventsTimelineProvider {
18+
override fun activeTimelineFlow(): StateFlow<Timeline?> = fakeTimelineProvider.activeTimelineFlow()
19+
}

libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/JoinedRoom.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,4 +177,9 @@ interface JoinedRoom : BaseRoom {
177177
*
178178
*/
179179
suspend fun withdrawVerificationAndResend(userIds: List<UserId>, sendHandle: SendHandle): Result<Unit>
180+
181+
/**
182+
* Subscribe to a [Flow] of [SendQueueUpdate] related to this room.
183+
*/
184+
fun subscribeToSendQueueUpdates(): Flow<SendQueueUpdate>
180185
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/*
2+
* Copyright (c) 2025 Element Creations Ltd.
3+
*
4+
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
5+
* Please see LICENSE files in the repository root for full details.
6+
*/
7+
8+
package io.element.android.libraries.matrix.api.room
9+
10+
import io.element.android.libraries.matrix.api.core.EventId
11+
import io.element.android.libraries.matrix.api.core.TransactionId
12+
import io.element.android.libraries.matrix.api.media.MediaSource
13+
14+
sealed interface SendQueueUpdate {
15+
data class NewLocalEvent(val transactionId: TransactionId) : SendQueueUpdate
16+
data class CancelledLocalEvent(val transactionId: TransactionId) : SendQueueUpdate
17+
data class ReplacedLocalEvent(val transactionId: TransactionId) : SendQueueUpdate
18+
data class SendError(val transactionId: TransactionId) : SendQueueUpdate
19+
data class RetrySendingEvent(val transactionId: TransactionId) : SendQueueUpdate
20+
data class SentEvent(val transactionId: TransactionId, val eventId: EventId) : SendQueueUpdate
21+
data class MediaUpload(val relatedTo: EventId, val file: MediaSource?, val index: Long, val progress: Float) : SendQueueUpdate
22+
}

0 commit comments

Comments
 (0)