Add a performance check for notification tap -> populated timeline time

This commit is contained in:
Jorge Martín 2025-11-20 09:04:36 +01:00 committed by Jorge Martin Espinosa
parent 8815fe7986
commit e1bd189ba0
5 changed files with 17 additions and 1 deletions

View file

@ -36,6 +36,7 @@ class DefaultIntentProvider(
return Intent(context, MainActivity::class.java).apply { return Intent(context, MainActivity::class.java).apply {
action = Intent.ACTION_VIEW action = Intent.ACTION_VIEW
data = deepLinkCreator.create(sessionId, roomId, threadId, eventId).toUri() data = deepLinkCreator.create(sessionId, roomId, threadId, eventId).toUri()
putExtra("from_notification", true)
} }
} }
} }

View file

@ -62,6 +62,8 @@ import io.element.android.libraries.oidc.api.OidcActionFlow
import io.element.android.libraries.sessionstorage.api.LoggedInState import io.element.android.libraries.sessionstorage.api.LoggedInState
import io.element.android.libraries.sessionstorage.api.SessionStore import io.element.android.libraries.sessionstorage.api.SessionStore
import io.element.android.libraries.ui.common.nodes.emptyNode import io.element.android.libraries.ui.common.nodes.emptyNode
import io.element.android.services.analytics.api.AnalyticsLongRunningTransaction
import io.element.android.services.analytics.api.AnalyticsService
import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
@ -86,6 +88,7 @@ class RootFlowNode(
private val oidcActionFlow: OidcActionFlow, private val oidcActionFlow: OidcActionFlow,
private val featureFlagService: FeatureFlagService, private val featureFlagService: FeatureFlagService,
private val announcementService: AnnouncementService, private val announcementService: AnnouncementService,
private val analyticsService: AnalyticsService,
) : BaseFlowNode<RootFlowNode.NavTarget>( ) : BaseFlowNode<RootFlowNode.NavTarget>(
backstack = BackStack( backstack = BackStack(
initialElement = NavTarget.SplashScreen, initialElement = NavTarget.SplashScreen,
@ -310,7 +313,12 @@ class RootFlowNode(
suspend fun handleIntent(intent: Intent) { suspend fun handleIntent(intent: Intent) {
val resolvedIntent = intentResolver.resolve(intent) ?: return val resolvedIntent = intentResolver.resolve(intent) ?: return
when (resolvedIntent) { when (resolvedIntent) {
is ResolvedIntent.Navigation -> navigateTo(resolvedIntent.deeplinkData) is ResolvedIntent.Navigation -> {
if (intent.getBooleanExtra("from_notification", false) && resolvedIntent.deeplinkData is DeeplinkData.Room) {
analyticsService.startLongRunningTransaction(AnalyticsLongRunningTransaction.NotificationTapOpensTimeline)
}
navigateTo(resolvedIntent.deeplinkData)
}
is ResolvedIntent.Login -> onLoginLink(resolvedIntent.params) is ResolvedIntent.Login -> onLoginLink(resolvedIntent.params)
is ResolvedIntent.Oidc -> onOidcAction(resolvedIntent.oidcAction) is ResolvedIntent.Oidc -> onOidcAction(resolvedIntent.oidcAction)
is ResolvedIntent.Permalink -> navigateTo(resolvedIntent.permalinkData) is ResolvedIntent.Permalink -> navigateTo(resolvedIntent.permalinkData)

View file

@ -55,6 +55,8 @@ import io.element.android.libraries.matrix.api.timeline.item.event.MessageShield
import io.element.android.libraries.matrix.api.timeline.item.event.TimelineItemEventOrigin import io.element.android.libraries.matrix.api.timeline.item.event.TimelineItemEventOrigin
import io.element.android.libraries.matrix.ui.room.canSendMessageAsState import io.element.android.libraries.matrix.ui.room.canSendMessageAsState
import io.element.android.libraries.preferences.api.store.SessionPreferencesStore import io.element.android.libraries.preferences.api.store.SessionPreferencesStore
import io.element.android.services.analytics.api.AnalyticsLongRunningTransaction
import io.element.android.services.analytics.api.AnalyticsService
import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.persistentListOf
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
@ -86,6 +88,7 @@ class TimelinePresenter(
private val typingNotificationPresenter: Presenter<TypingNotificationState>, private val typingNotificationPresenter: Presenter<TypingNotificationState>,
private val roomCallStatePresenter: Presenter<RoomCallState>, private val roomCallStatePresenter: Presenter<RoomCallState>,
private val featureFlagService: FeatureFlagService, private val featureFlagService: FeatureFlagService,
private val analyticsService: AnalyticsService,
) : Presenter<TimelineState> { ) : Presenter<TimelineState> {
private val tag = "TimelinePresenter" private val tag = "TimelinePresenter"
@AssistedFactory @AssistedFactory
@ -225,6 +228,7 @@ class TimelinePresenter(
LaunchedEffect(Unit) { LaunchedEffect(Unit) {
timelineItemsFactory.timelineItems timelineItemsFactory.timelineItems
.onEach { newTimelineItems -> .onEach { newTimelineItems ->
analyticsService.removeLongRunningTransaction(AnalyticsLongRunningTransaction.NotificationTapOpensTimeline)?.finish()
timelineItemIndexer.process(newTimelineItems) timelineItemIndexer.process(newTimelineItems)
timelineItems = newTimelineItems timelineItems = newTimelineItems
} }

View file

@ -60,6 +60,7 @@ import io.element.android.libraries.matrix.test.timeline.aMessageContent
import io.element.android.libraries.matrix.test.timeline.anEventTimelineItem import io.element.android.libraries.matrix.test.timeline.anEventTimelineItem
import io.element.android.libraries.matrix.ui.components.aMatrixUserList import io.element.android.libraries.matrix.ui.components.aMatrixUserList
import io.element.android.libraries.preferences.test.InMemorySessionPreferencesStore import io.element.android.libraries.preferences.test.InMemorySessionPreferencesStore
import io.element.android.services.analytics.test.FakeAnalyticsService
import io.element.android.tests.testutils.WarmUpRule import io.element.android.tests.testutils.WarmUpRule
import io.element.android.tests.testutils.awaitLastSequentialItem import io.element.android.tests.testutils.awaitLastSequentialItem
import io.element.android.tests.testutils.consumeItemsUntilPredicate import io.element.android.tests.testutils.consumeItemsUntilPredicate
@ -1054,6 +1055,7 @@ class TimelinePresenterTest {
typingNotificationPresenter = { aTypingNotificationState() }, typingNotificationPresenter = { aTypingNotificationState() },
roomCallStatePresenter = { aStandByCallState() }, roomCallStatePresenter = { aStandByCallState() },
featureFlagService = featureFlagService, featureFlagService = featureFlagService,
analyticsService = FakeAnalyticsService(),
) )
} }
} }

View file

@ -13,4 +13,5 @@ sealed class AnalyticsLongRunningTransaction(
) { ) {
data object FirstRoomsDisplayed : AnalyticsLongRunningTransaction("First rooms displayed after login or restoration", 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 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)
} }