Add support for Verification state analytics

This commit is contained in:
Valere 2024-05-06 19:06:54 +02:00
parent 34358035eb
commit a761215e31
8 changed files with 245 additions and 9 deletions

View file

@ -0,0 +1,59 @@
/*
* 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 io.element.android.appnav.loggedin
import im.vector.app.features.analytics.plan.CryptoSessionStateChange
import im.vector.app.features.analytics.plan.UserProperties
import io.element.android.libraries.matrix.api.encryption.RecoveryState
import io.element.android.libraries.matrix.api.verification.SessionVerifiedStatus
fun SessionVerifiedStatus.toAnalyticsUserPropertyValue(): UserProperties.VerificationState? {
return when (this) {
// we don't need to report transient states
SessionVerifiedStatus.Unknown -> null
SessionVerifiedStatus.NotVerified -> UserProperties.VerificationState.NotVerified
SessionVerifiedStatus.Verified -> UserProperties.VerificationState.Verified
}
}
fun RecoveryState.toAnalyticsUserPropertyValue(): UserProperties.RecoveryState? {
return when (this) {
RecoveryState.ENABLED -> UserProperties.RecoveryState.Enabled
RecoveryState.DISABLED -> UserProperties.RecoveryState.Disabled
RecoveryState.INCOMPLETE -> UserProperties.RecoveryState.Incomplete
// we don't need to report transient states
else -> null
}
}
fun SessionVerifiedStatus.toAnalyticsStateChangeValue(): CryptoSessionStateChange.VerificationState? {
return when (this) {
// we don't need to report transient states
SessionVerifiedStatus.Unknown -> null
SessionVerifiedStatus.NotVerified -> CryptoSessionStateChange.VerificationState.NotVerified
SessionVerifiedStatus.Verified -> CryptoSessionStateChange.VerificationState.Verified
}
}
fun RecoveryState.toAnalyticsStateChangeValue(): CryptoSessionStateChange.RecoveryState? {
return when (this) {
RecoveryState.ENABLED -> CryptoSessionStateChange.RecoveryState.Enabled
RecoveryState.DISABLED -> CryptoSessionStateChange.RecoveryState.Disabled
RecoveryState.INCOMPLETE -> CryptoSessionStateChange.RecoveryState.Incomplete
// we don't need to report transient states
else -> null
}
}

View file

@ -22,14 +22,19 @@ import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import im.vector.app.features.analytics.plan.CryptoSessionStateChange
import im.vector.app.features.analytics.plan.UserProperties
import io.element.android.features.networkmonitor.api.NetworkMonitor
import io.element.android.features.networkmonitor.api.NetworkStatus
import io.element.android.libraries.architecture.Presenter
import io.element.android.libraries.matrix.api.MatrixClient
import io.element.android.libraries.matrix.api.encryption.EncryptionService
import io.element.android.libraries.matrix.api.encryption.RecoveryState
import io.element.android.libraries.matrix.api.roomlist.RoomListService
import io.element.android.libraries.matrix.api.verification.SessionVerificationService
import io.element.android.libraries.matrix.api.verification.SessionVerifiedStatus
import io.element.android.libraries.push.api.PushService
import io.element.android.services.analytics.api.AnalyticsService
import kotlinx.coroutines.flow.map
import javax.inject.Inject
@ -38,6 +43,8 @@ class LoggedInPresenter @Inject constructor(
private val networkMonitor: NetworkMonitor,
private val pushService: PushService,
private val sessionVerificationService: SessionVerificationService,
private val analyticsService: AnalyticsService,
private val encryptionService: EncryptionService,
) : Presenter<LoggedInState> {
@Composable
override fun present(): LoggedInState {
@ -62,8 +69,34 @@ class LoggedInPresenter @Inject constructor(
networkStatus == NetworkStatus.Online && syncIndicator == RoomListService.SyncIndicator.Show
}
}
val verificationState by sessionVerificationService.sessionVerifiedStatus.collectAsState()
val recoveryState by encryptionService.recoveryStateStateFlow.collectAsState()
reportCryptoStatusToAnalytics(verificationState, recoveryState)
return LoggedInState(
showSyncSpinner = showSyncSpinner,
)
}
private fun reportCryptoStatusToAnalytics(verificationState: SessionVerifiedStatus, recoveryState: RecoveryState) {
// Update first the user property, to store the current status for that posthog user
val userVerificationState = verificationState.toAnalyticsUserPropertyValue()
val userRecoveryState = recoveryState.toAnalyticsUserPropertyValue()
if (userRecoveryState != null && userVerificationState != null) {
// we want to report when both value are known (if one is unknown we wait until we have them both)
analyticsService.updateUserProperties(
UserProperties(
verificationState = userVerificationState,
recoveryState = userRecoveryState
)
)
}
// Also report when there is a change in the state, to be able to track the changes
val changeVerificationState = verificationState.toAnalyticsStateChangeValue()
val changeRecoveryState = recoveryState.toAnalyticsStateChangeValue()
if (changeVerificationState != null && changeRecoveryState != null) {
analyticsService.capture(CryptoSessionStateChange(changeRecoveryState, changeVerificationState))
}
}
}

View file

@ -24,9 +24,11 @@ import io.element.android.features.networkmonitor.api.NetworkStatus
import io.element.android.features.networkmonitor.test.FakeNetworkMonitor
import io.element.android.libraries.matrix.api.roomlist.RoomListService
import io.element.android.libraries.matrix.test.FakeMatrixClient
import io.element.android.libraries.matrix.test.encryption.FakeEncryptionService
import io.element.android.libraries.matrix.test.roomlist.FakeRoomListService
import io.element.android.libraries.matrix.test.verification.FakeSessionVerificationService
import io.element.android.libraries.push.test.FakePushService
import io.element.android.services.analytics.test.FakeAnalyticsService
import io.element.android.tests.testutils.WarmUpRule
import io.element.android.tests.testutils.consumeItemsUntilPredicate
import kotlinx.coroutines.test.runTest
@ -73,6 +75,8 @@ class LoggedInPresenterTest {
networkMonitor = FakeNetworkMonitor(networkStatus),
pushService = FakePushService(),
sessionVerificationService = FakeSessionVerificationService(),
analyticsService = FakeAnalyticsService(),
encryptionService = FakeEncryptionService()
)
}
}