First Commit

This commit is contained in:
2025-12-18 16:28:50 +07:00
commit 8c3e4f491f
9974 changed files with 396488 additions and 0 deletions
+21
View File
@@ -0,0 +1,21 @@
/*
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2023-2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
plugins {
id("io.element.android-compose-library")
}
android {
namespace = "io.element.android.services.analytics.api"
}
dependencies {
api(projects.services.analyticsproviders.api)
api(projects.services.toolbox.api)
implementation(libs.coroutines.core)
implementation(projects.libraries.core)
}
@@ -0,0 +1,22 @@
/*
* 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.services.analytics.api
sealed class AnalyticsLongRunningTransaction(
val name: String,
val operation: String?,
) {
data object ColdStartUntilCachedRoomList : AnalyticsLongRunningTransaction("Cold start until cached room list is displayed", 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 NotificationTapOpensTimeline : AnalyticsLongRunningTransaction("A notification was tapped and it opened a timeline", null)
data object OpenRoom : AnalyticsLongRunningTransaction("Open a room and see loaded items in the timeline", null)
data object LoadJoinedRoomFlow : AnalyticsLongRunningTransaction("Load joined room UI", "ui.load")
data object LoadMessagesUi : AnalyticsLongRunningTransaction("Load messages UI", "ui.load")
data object DisplayFirstTimelineItems : AnalyticsLongRunningTransaction("Get and display first timeline items", null)
}
@@ -0,0 +1,112 @@
/*
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2023-2025 New Vector 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.services.analytics.api
import io.element.android.services.analyticsproviders.api.AnalyticsProvider
import io.element.android.services.analyticsproviders.api.AnalyticsTransaction
import io.element.android.services.analyticsproviders.api.trackers.AnalyticsTracker
import io.element.android.services.analyticsproviders.api.trackers.ErrorTracker
import kotlinx.coroutines.flow.Flow
interface AnalyticsService : AnalyticsTracker, ErrorTracker {
/**
* Get the available analytics providers.
*/
fun getAvailableAnalyticsProviders(): Set<AnalyticsProvider>
/**
* A Flow of Boolean, true if the user has given their consent.
*/
val userConsentFlow: Flow<Boolean>
/**
* Update the user consent value.
*/
suspend fun setUserConsent(userConsent: Boolean)
/**
* A Flow of Boolean, true if the user has been asked for their consent.
*/
val didAskUserConsentFlow: Flow<Boolean>
/**
* Store the fact that the user has been asked for their consent.
*/
suspend fun setDidAskUserConsent()
/**
* A Flow of String, used for analytics Id.
*/
val analyticsIdFlow: Flow<String>
/**
* Update analyticsId from the AccountData.
*/
suspend fun setAnalyticsId(analyticsId: String)
/**
* Starts a transaction to measure the performance of an operation.
*/
fun startTransaction(name: String, operation: String? = null): AnalyticsTransaction
/**
* Starts an [AnalyticsLongRunningTransaction], that can be shared with other components.
*/
fun startLongRunningTransaction(
longRunningTransaction: AnalyticsLongRunningTransaction,
parentTransaction: AnalyticsTransaction? = null
): AnalyticsTransaction
/**
* Gets an ongoing [AnalyticsLongRunningTransaction], if it exists.
*/
fun getLongRunningTransaction(longRunningTransaction: AnalyticsLongRunningTransaction): AnalyticsTransaction?
/**
* Removes an ongoing [AnalyticsLongRunningTransaction] so it's no longer shared.
*/
fun removeLongRunningTransaction(longRunningTransaction: AnalyticsLongRunningTransaction): AnalyticsTransaction?
}
inline fun <T> AnalyticsService.recordTransaction(
name: String,
operation: String,
parentTransaction: AnalyticsTransaction? = null,
block: (AnalyticsTransaction) -> T
): T {
val transaction = parentTransaction?.startChild(name, operation)
?: startTransaction(name, operation)
try {
val result = block(transaction)
return result
} finally {
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()
}
}
@@ -0,0 +1,17 @@
/*
* 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.services.analytics.api
import io.element.android.services.analyticsproviders.api.AnalyticsTransaction
object NoopAnalyticsTransaction : AnalyticsTransaction {
override fun startChild(operation: String, description: String?): AnalyticsTransaction = NoopAnalyticsTransaction
override fun setData(key: String, value: Any) {}
override fun isFinished(): Boolean = true
override fun finish() {}
}
@@ -0,0 +1,19 @@
/*
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2024, 2025 New Vector 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.services.analytics.api
import androidx.compose.runtime.Composable
import im.vector.app.features.analytics.plan.MobileScreen
interface ScreenTracker {
@Composable
fun TrackScreen(
screen: MobileScreen.ScreenName,
)
}
@@ -0,0 +1,18 @@
/*
* 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.services.analytics.api.watchers
/**
* 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()
}
@@ -0,0 +1,17 @@
/*
* 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.services.analytics.api.watchers
/**
* This component is used to check how long it takes for the room list to be up to date after opening the app while it's on a 'warm' state:
* the app was previously running and we just returned to it.
*/
interface AnalyticsRoomListStateWatcher {
fun start()
fun stop()
}