Add another performance check for cold start time until the cached room list is displayed

This commit is contained in:
Jorge Martín 2025-11-20 10:08:53 +01:00 committed by Jorge Martin Espinosa
parent e1bd189ba0
commit daf7bea39e
7 changed files with 91 additions and 0 deletions

View file

@ -24,6 +24,7 @@ import dev.zacsweers.metro.AppScope
import dev.zacsweers.metro.Assisted
import dev.zacsweers.metro.AssistedInject
import io.element.android.annotations.ContributesNode
import io.element.android.appnav.analytics.AnalyticsColdStartWatcher
import io.element.android.features.login.api.LoginEntryPoint
import io.element.android.features.login.api.LoginParams
import io.element.android.libraries.architecture.BackstackView
@ -43,6 +44,7 @@ class NotLoggedInFlowNode(
@Assisted plugins: List<Plugin>,
private val loginEntryPoint: LoginEntryPoint,
private val imageLoaderHolder: ImageLoaderHolder,
private val analyticsColdStartWatcher: AnalyticsColdStartWatcher,
) : BaseFlowNode<NotLoggedInFlowNode.NavTarget>(
backstack = BackStack(
initialElement = NavTarget.Root,
@ -65,6 +67,7 @@ class NotLoggedInFlowNode(
override fun onBuilt() {
super.onBuilt()
analyticsColdStartWatcher.whenLoggingIn()
lifecycle.subscribe(
onResume = {
SingletonImageLoader.setUnsafe(imageLoaderHolder.get())

View file

@ -29,6 +29,7 @@ import dev.zacsweers.metro.Assisted
import dev.zacsweers.metro.AssistedInject
import im.vector.app.features.analytics.plan.JoinedRoom
import io.element.android.annotations.ContributesNode
import io.element.android.appnav.analytics.AnalyticsColdStartWatcher
import io.element.android.appnav.di.MatrixSessionCache
import io.element.android.appnav.intent.IntentResolver
import io.element.android.appnav.intent.ResolvedIntent
@ -89,6 +90,7 @@ class RootFlowNode(
private val featureFlagService: FeatureFlagService,
private val announcementService: AnnouncementService,
private val analyticsService: AnalyticsService,
private val analyticsColdStartWatcher: AnalyticsColdStartWatcher,
) : BaseFlowNode<RootFlowNode.NavTarget>(
backstack = BackStack(
initialElement = NavTarget.SplashScreen,
@ -98,6 +100,7 @@ class RootFlowNode(
plugins = plugins
) {
override fun onBuilt() {
analyticsColdStartWatcher.start()
matrixSessionCache.restoreWithSavedState(buildContext.savedStateMap)
super.onBuilt()
observeNavState()

View file

@ -0,0 +1,69 @@
/*
* 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.appnav.analytics
import dev.zacsweers.metro.AppScope
import dev.zacsweers.metro.ContributesBinding
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 kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import timber.log.Timber
import java.util.concurrent.atomic.AtomicBoolean
/**
* Adds a performance check transaction measuring the time between a cold start (or, after we read the user consent after a cold start)
* until the cached room list is displayed. This check only takes place in a cold app start after the user is authenticated.
*/
interface AnalyticsColdStartWatcher {
fun start()
fun whenLoggingIn()
fun onRoomListVisible()
}
@SingleIn(AppScope::class)
@ContributesBinding(AppScope::class)
class DefaultAnalyticsColdStartWatcher(
private val analyticsService: AnalyticsService,
@AppCoroutineScope private val appCoroutineScope: CoroutineScope,
) : AnalyticsColdStartWatcher {
private val isColdStart = AtomicBoolean(true)
override fun start() {
analyticsService.userConsentFlow
.onEach { hasConsent ->
if (hasConsent) {
if (isColdStart.get()) {
Timber.d("Starting cold start check")
analyticsService.startLongRunningTransaction(AnalyticsLongRunningTransaction.ColdStartUntilCachedRoomList)
} else {
error("The app is no longer in a cold start state")
}
}
}
.catch { Timber.w(it.message) }
.launchIn(appCoroutineScope)
}
override fun whenLoggingIn() {
if (isColdStart.getAndSet(false)) {
Timber.d("Canceled cold start check: user is logging in")
}
}
override fun onRoomListVisible() {
if (isColdStart.getAndSet(false)) {
Timber.d("Room list is visible, finishing cold start check")
analyticsService.removeLongRunningTransaction(AnalyticsLongRunningTransaction.ColdStartUntilCachedRoomList)?.finish()
}
}
}