Update to analytics events 0.23.0 and refactor

This commit is contained in:
Valere 2024-05-29 17:39:16 +02:00
parent 09c68f3421
commit a363e392b4
6 changed files with 75 additions and 34 deletions

View file

@ -53,6 +53,7 @@ import im.vector.app.core.pushers.FcmHelper
import im.vector.app.core.resources.BuildMeta import im.vector.app.core.resources.BuildMeta
import im.vector.app.features.analytics.DecryptionFailureTracker import im.vector.app.features.analytics.DecryptionFailureTracker
import im.vector.app.features.analytics.VectorAnalytics import im.vector.app.features.analytics.VectorAnalytics
import im.vector.app.features.analytics.plan.SuperProperties
import im.vector.app.features.call.webrtc.WebRtcCallManager import im.vector.app.features.call.webrtc.WebRtcCallManager
import im.vector.app.features.configuration.VectorConfiguration import im.vector.app.features.configuration.VectorConfiguration
import im.vector.app.features.invite.InvitesAcceptor import im.vector.app.features.invite.InvitesAcceptor
@ -130,6 +131,12 @@ class VectorApplication :
appContext = this appContext = this
flipperProxy.init(matrix) flipperProxy.init(matrix)
vectorAnalytics.init() vectorAnalytics.init()
vectorAnalytics.updateSuperProperties(
SuperProperties(
appPlatform = SuperProperties.AppPlatform.EA,
cryptoSDK = SuperProperties.CryptoSDK.Rust,
)
)
invitesAcceptor.initialize() invitesAcceptor.initialize()
autoRageShaker.initialize() autoRageShaker.initialize()
decryptionFailureTracker.start() decryptionFailureTracker.start()

View file

@ -160,7 +160,7 @@ dependencies {
api 'com.facebook.stetho:stetho:1.6.0' api 'com.facebook.stetho:stetho:1.6.0'
// Analytics // Analytics
api 'com.github.matrix-org:matrix-analytics-events:0.22.0' api 'com.github.matrix-org:matrix-analytics-events:0.23.0'
api libs.google.phonenumber api libs.google.phonenumber

View file

@ -22,8 +22,6 @@ import im.vector.app.core.dispatchers.CoroutineDispatchers
import im.vector.app.core.pushers.UnregisterUnifiedPushUseCase import im.vector.app.core.pushers.UnregisterUnifiedPushUseCase
import im.vector.app.core.services.GuardServiceStarter import im.vector.app.core.services.GuardServiceStarter
import im.vector.app.core.session.ConfigureAndStartSessionUseCase import im.vector.app.core.session.ConfigureAndStartSessionUseCase
import im.vector.app.features.analytics.VectorAnalytics
import im.vector.app.features.analytics.plan.SuperProperties
import im.vector.app.features.call.webrtc.WebRtcCallManager import im.vector.app.features.call.webrtc.WebRtcCallManager
import im.vector.app.features.crypto.keysrequest.KeyRequestHandler import im.vector.app.features.crypto.keysrequest.KeyRequestHandler
import im.vector.app.features.crypto.verification.IncomingVerificationRequestHandler import im.vector.app.features.crypto.verification.IncomingVerificationRequestHandler
@ -58,7 +56,6 @@ class ActiveSessionHolder @Inject constructor(
private val unregisterUnifiedPushUseCase: UnregisterUnifiedPushUseCase, private val unregisterUnifiedPushUseCase: UnregisterUnifiedPushUseCase,
private val applicationCoroutineScope: CoroutineScope, private val applicationCoroutineScope: CoroutineScope,
private val coroutineDispatchers: CoroutineDispatchers, private val coroutineDispatchers: CoroutineDispatchers,
private val vectorAnalytics: VectorAnalytics,
) { ) {
private var activeSessionReference: AtomicReference<Session?> = AtomicReference() private var activeSessionReference: AtomicReference<Session?> = AtomicReference()
@ -75,13 +72,6 @@ class ActiveSessionHolder @Inject constructor(
session.callSignalingService().addCallListener(callManager) session.callSignalingService().addCallListener(callManager)
imageManager.onSessionStarted(session) imageManager.onSessionStarted(session)
guardServiceStarter.start() guardServiceStarter.start()
vectorAnalytics.updateSuperProperties(
SuperProperties(
platformCodeName = SuperProperties.PlatformCodeName.EA,
cryptoSDK = SuperProperties.CryptoSDK.Rust,
cryptoSDKVersion = session.cryptoService().getCryptoVersion(applicationContext, false)
)
)
} }
suspend fun clearActiveSession() { suspend fun clearActiveSession() {

View file

@ -0,0 +1,43 @@
/*
* Copyright (c) 2024 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.app.features.analytics.impl
import im.vector.app.ActiveSessionDataSource
import im.vector.app.features.analytics.plan.SuperProperties
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
import javax.inject.Inject
/**
* Gathers the super properties that are static to this platform or
* that can be automatically resolved from the current session.
*/
class AutoSuperPropertiesFlowProvider @Inject constructor(
activeSessionDataSource: ActiveSessionDataSource,
) {
val superPropertiesFlow: Flow<SuperProperties> = activeSessionDataSource.stream()
.map { session ->
SuperProperties(
appPlatform = SuperProperties.AppPlatform.EA,
cryptoSDK = SuperProperties.CryptoSDK.Rust,
cryptoSDKVersion = session.getOrNull()?.cryptoService()?.getCryptoVersion(false)
)
}
.distinctUntilChanged()
}

View file

@ -42,6 +42,7 @@ class DefaultVectorAnalytics @Inject constructor(
private val analyticsConfig: AnalyticsConfig, private val analyticsConfig: AnalyticsConfig,
private val analyticsStore: AnalyticsStore, private val analyticsStore: AnalyticsStore,
private val lateInitUserPropertiesFactory: LateInitUserPropertiesFactory, private val lateInitUserPropertiesFactory: LateInitUserPropertiesFactory,
private val autoSuperPropertiesFlowProvider: AutoSuperPropertiesFlowProvider,
@NamedGlobalScope private val globalScope: CoroutineScope @NamedGlobalScope private val globalScope: CoroutineScope
) : VectorAnalytics { ) : VectorAnalytics {
@ -69,6 +70,7 @@ class DefaultVectorAnalytics @Inject constructor(
override fun init() { override fun init() {
observeUserConsent() observeUserConsent()
observeAnalyticsId() observeAnalyticsId()
observeAutoSuperProperties()
} }
override fun getUserConsent(): Flow<Boolean> { override fun getUserConsent(): Flow<Boolean> {
@ -116,6 +118,12 @@ class DefaultVectorAnalytics @Inject constructor(
.launchIn(globalScope) .launchIn(globalScope)
} }
private fun observeAutoSuperProperties() {
autoSuperPropertiesFlowProvider.superPropertiesFlow.onEach {
updateSuperProperties(it)
}.launchIn(globalScope)
}
private suspend fun identifyPostHog() { private suspend fun identifyPostHog() {
val id = analyticsId ?: return val id = analyticsId ?: return
if (!userConsent.orFalse()) return if (!userConsent.orFalse()) return
@ -171,20 +179,14 @@ class DefaultVectorAnalytics @Inject constructor(
override fun capture(event: VectorAnalyticsEvent) { override fun capture(event: VectorAnalyticsEvent) {
Timber.tag(analyticsTag.value).d("capture($event)") Timber.tag(analyticsTag.value).d("capture($event)")
posthog posthog?.takeIf { userConsent == true }?.capture(
?.takeIf { userConsent == true } event.getName(), analyticsId, event.getProperties()?.toPostHogProperties().orEmpty().withSuperProperties()
?.capture(
event.getName(),
analyticsId,
event.getProperties()?.toPostHogProperties().orEmpty().withSuperProperties()
) )
} }
override fun screen(screen: VectorAnalyticsScreen) { override fun screen(screen: VectorAnalyticsScreen) {
Timber.tag(analyticsTag.value).d("screen($screen)") Timber.tag(analyticsTag.value).d("screen($screen)")
posthog posthog?.takeIf { userConsent == true }?.screen(screen.getName(), screen.getProperties()?.toPostHogProperties().orEmpty().withSuperProperties())
?.takeIf { userConsent == true }
?.screen(screen.getName(), screen.getProperties()?.toPostHogProperties().orEmpty().withSuperProperties())
} }
override fun updateUserProperties(userProperties: UserProperties) { override fun updateUserProperties(userProperties: UserProperties) {
@ -198,9 +200,7 @@ class DefaultVectorAnalytics @Inject constructor(
private fun doUpdateUserProperties(userProperties: UserProperties) { private fun doUpdateUserProperties(userProperties: UserProperties) {
// we need a distinct id to set user properties // we need a distinct id to set user properties
val distinctId = analyticsId ?: return val distinctId = analyticsId ?: return
posthog posthog?.takeIf { userConsent == true }?.identify(distinctId, userProperties.getProperties())
?.takeIf { userConsent == true }
?.identify(distinctId, userProperties.getProperties())
} }
private fun Map<String, Any?>?.toPostHogProperties(): Map<String, Any>? { private fun Map<String, Any?>?.toPostHogProperties(): Map<String, Any>? {
@ -233,7 +233,7 @@ class DefaultVectorAnalytics @Inject constructor(
* Adds super properties to the actual property set. * Adds super properties to the actual property set.
* If a property of the same name is already on the reported event it will not be overwritten. * If a property of the same name is already on the reported event it will not be overwritten.
*/ */
private fun Map<String, Any>.withSuperProperties(): Map<String, Any> { private fun Map<String, Any>.withSuperProperties(): Map<String, Any>? {
val withSuperProperties = this.toMutableMap() val withSuperProperties = this.toMutableMap()
val superProperties = this@DefaultVectorAnalytics.superProperties?.getProperties() val superProperties = this@DefaultVectorAnalytics.superProperties?.getProperties()
superProperties?.forEach { superProperties?.forEach {
@ -241,7 +241,7 @@ class DefaultVectorAnalytics @Inject constructor(
withSuperProperties[it.key] = it.value withSuperProperties[it.key] = it.value
} }
} }
return withSuperProperties return withSuperProperties.takeIf { it.isEmpty().not() }
} }
override fun trackError(throwable: Throwable) { override fun trackError(throwable: Throwable) {
@ -251,13 +251,7 @@ class DefaultVectorAnalytics @Inject constructor(
} }
override fun updateSuperProperties(updatedProperties: SuperProperties) { override fun updateSuperProperties(updatedProperties: SuperProperties) {
if (this.superProperties == null) {
this.superProperties = updatedProperties
return
}
this.superProperties = SuperProperties( this.superProperties = SuperProperties(
platformCodeName = updatedProperties.platformCodeName ?: this.superProperties?.platformCodeName,
cryptoSDK = updatedProperties.cryptoSDK ?: this.superProperties?.cryptoSDK, cryptoSDK = updatedProperties.cryptoSDK ?: this.superProperties?.cryptoSDK,
appPlatform = updatedProperties.appPlatform ?: this.superProperties?.appPlatform, appPlatform = updatedProperties.appPlatform ?: this.superProperties?.appPlatform,
cryptoSDKVersion = updatedProperties.cryptoSDKVersion ?: superProperties?.cryptoSDKVersion cryptoSDKVersion = updatedProperties.cryptoSDKVersion ?: superProperties?.cryptoSDKVersion

View file

@ -26,9 +26,12 @@ import im.vector.app.test.fixtures.AnalyticsConfigFixture.anAnalyticsConfig
import im.vector.app.test.fixtures.aUserProperties import im.vector.app.test.fixtures.aUserProperties
import im.vector.app.test.fixtures.aVectorAnalyticsEvent import im.vector.app.test.fixtures.aVectorAnalyticsEvent
import im.vector.app.test.fixtures.aVectorAnalyticsScreen import im.vector.app.test.fixtures.aVectorAnalyticsScreen
import io.mockk.every
import io.mockk.mockk
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.test.runTest import kotlinx.coroutines.test.runTest
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
@ -45,6 +48,9 @@ class DefaultVectorAnalyticsTest {
private val fakeAnalyticsStore = FakeAnalyticsStore() private val fakeAnalyticsStore = FakeAnalyticsStore()
private val fakeLateInitUserPropertiesFactory = FakeLateInitUserPropertiesFactory() private val fakeLateInitUserPropertiesFactory = FakeLateInitUserPropertiesFactory()
private val fakeSentryAnalytics = FakeSentryAnalytics() private val fakeSentryAnalytics = FakeSentryAnalytics()
private val mockAutoSuperPropertiesFlowProvider = mockk<AutoSuperPropertiesFlowProvider>().also {
every { it.superPropertiesFlow } returns flowOf(SuperProperties())
}
private val defaultVectorAnalytics = DefaultVectorAnalytics( private val defaultVectorAnalytics = DefaultVectorAnalytics(
postHogFactory = FakePostHogFactory(fakePostHog.instance).instance, postHogFactory = FakePostHogFactory(fakePostHog.instance).instance,
@ -52,7 +58,8 @@ class DefaultVectorAnalyticsTest {
analyticsStore = fakeAnalyticsStore.instance, analyticsStore = fakeAnalyticsStore.instance,
globalScope = CoroutineScope(Dispatchers.Unconfined), globalScope = CoroutineScope(Dispatchers.Unconfined),
analyticsConfig = anAnalyticsConfig(isEnabled = true), analyticsConfig = anAnalyticsConfig(isEnabled = true),
lateInitUserPropertiesFactory = fakeLateInitUserPropertiesFactory.instance lateInitUserPropertiesFactory = fakeLateInitUserPropertiesFactory.instance,
autoSuperPropertiesFlowProvider = mockAutoSuperPropertiesFlowProvider,
) )
@Before @Before
@ -180,7 +187,7 @@ class DefaultVectorAnalyticsTest {
fakeAnalyticsStore.givenUserContent(consent = true) fakeAnalyticsStore.givenUserContent(consent = true)
val updatedProperties = SuperProperties( val updatedProperties = SuperProperties(
platformCodeName = SuperProperties.PlatformCodeName.EA, appPlatform = SuperProperties.AppPlatform.EA,
cryptoSDKVersion = "0.0", cryptoSDKVersion = "0.0",
cryptoSDK = SuperProperties.CryptoSDK.Rust cryptoSDK = SuperProperties.CryptoSDK.Rust
) )
@ -214,7 +221,7 @@ class DefaultVectorAnalyticsTest {
fakeAnalyticsStore.givenUserContent(consent = true) fakeAnalyticsStore.givenUserContent(consent = true)
val superProperties = SuperProperties( val superProperties = SuperProperties(
platformCodeName = SuperProperties.PlatformCodeName.EA, appPlatform = SuperProperties.AppPlatform.EA,
cryptoSDKVersion = "0.0", cryptoSDKVersion = "0.0",
cryptoSDK = SuperProperties.CryptoSDK.Rust cryptoSDK = SuperProperties.CryptoSDK.Rust
) )