Skip to content

Commit

Permalink
feat: enable countly performance metrics (WPB-8842) (#3481)
Browse files Browse the repository at this point in the history
  • Loading branch information
yamilmedina authored Oct 2, 2024
1 parent c5e2908 commit 59f9c3d
Show file tree
Hide file tree
Showing 14 changed files with 205 additions and 34 deletions.
2 changes: 2 additions & 0 deletions app/src/main/kotlin/com/wire/android/WireApplication.kt
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,8 @@ class WireApplication : BaseApp() {
dispatcher = Dispatchers.IO
)

AnonymousAnalyticsManagerImpl.applicationOnCreate()

// observe the app visibility state and send AppOpen event if the app goes from the background to the foreground
globalAppScope.launch {
currentScreenManager
Expand Down
12 changes: 6 additions & 6 deletions app/src/main/kotlin/com/wire/android/ui/WireActivityViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -566,15 +566,15 @@ class WireActivityViewModel @Inject constructor(
}

private fun CurrentScreen.isGlobalDialogAllowed(): Boolean = when (this) {
CurrentScreen.ImportMedia,
CurrentScreen.DeviceManager -> false
is CurrentScreen.ImportMedia,
is CurrentScreen.DeviceManager -> false

CurrentScreen.InBackground,
is CurrentScreen.InBackground,
is CurrentScreen.Conversation,
CurrentScreen.Home,
is CurrentScreen.Home,
is CurrentScreen.OtherUserProfile,
CurrentScreen.AuthRelated,
CurrentScreen.SomeOther -> true
is CurrentScreen.AuthRelated,
is CurrentScreen.SomeOther -> true
}
}

Expand Down
37 changes: 25 additions & 12 deletions app/src/main/kotlin/com/wire/android/util/CurrentScreenManager.kt
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,15 @@

package com.wire.android.util

import android.annotation.SuppressLint
import android.os.Bundle
import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.LifecycleOwner
import androidx.navigation.NavController
import androidx.navigation.NavDestination
import com.ramcosta.composedestinations.spec.DestinationSpec
import com.wire.android.appLogger
import com.wire.android.feature.analytics.AnonymousAnalyticsManagerImpl
import com.wire.android.navigation.toDestination
import com.wire.android.ui.destinations.ConversationScreenDestination
import com.wire.android.ui.destinations.CreateAccountDetailsScreenDestination
Expand All @@ -46,6 +48,7 @@ import com.wire.android.ui.destinations.RegisterDeviceScreenDestination
import com.wire.android.ui.destinations.RemoveDeviceScreenDestination
import com.wire.android.ui.destinations.SelfDevicesScreenDestination
import com.wire.android.ui.destinations.WelcomeScreenDestination
import com.wire.kalium.logger.obfuscateId
import com.wire.kalium.logic.data.id.ConversationId
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
Expand All @@ -67,7 +70,7 @@ class CurrentScreenManager @Inject constructor(
) : DefaultLifecycleObserver,
NavController.OnDestinationChangedListener {

private val currentScreenState = MutableStateFlow<CurrentScreen>(CurrentScreen.SomeOther)
private val currentScreenState = MutableStateFlow<CurrentScreen>(CurrentScreen.SomeOther())

/**
* An integer that counts up when a screen appears, and counts down when
Expand Down Expand Up @@ -120,12 +123,17 @@ class CurrentScreenManager @Inject constructor(
}

override fun onDestinationChanged(controller: NavController, destination: NavDestination, arguments: Bundle?) {
val currentView = currentScreenState.value.toString()
AnonymousAnalyticsManagerImpl.stopView(currentView)
val currentItem = destination.toDestination()
currentScreenState.value = CurrentScreen.fromDestination(
currentItem,
arguments,
isApplicationVisibleFlow.value
)

val newView = currentScreenState.value.toString()
AnonymousAnalyticsManagerImpl.recordView(newView)
}

companion object {
Expand All @@ -136,30 +144,35 @@ class CurrentScreenManager @Inject constructor(
sealed class CurrentScreen {

// Home Screen is being displayed
object Home : CurrentScreen()
data object Home : CurrentScreen()

// Some Conversation is opened
data class Conversation(val id: ConversationId) : CurrentScreen()
data class Conversation(val id: ConversationId) : CurrentScreen() {
override fun toString(): String = "Conversation(${id.toString().obfuscateId()})"
}

// Another User Profile Screen is opened
data class OtherUserProfile(val id: ConversationId) : CurrentScreen()
data class OtherUserProfile(val id: ConversationId) : CurrentScreen() {
override fun toString(): String = "OtherUserProfile(${id.toString().obfuscateId()})"
}

// Import media screen is opened
object ImportMedia : CurrentScreen()
data object ImportMedia : CurrentScreen()

// SelfDevices screen is opened
object DeviceManager : CurrentScreen()
data object DeviceManager : CurrentScreen()

// Auth related screen is opened
object AuthRelated : CurrentScreen()
data class AuthRelated(val route: String?) : CurrentScreen()

// Some other screen is opened, kinda "do nothing screen"
object SomeOther : CurrentScreen()
data class SomeOther(val route: String? = null) : CurrentScreen()

// App is in background (screen is turned off, or covered by another app), non of the screens is visible
object InBackground : CurrentScreen()
data object InBackground : CurrentScreen()

companion object {
@SuppressLint("RestrictedApi")
@Suppress("ComplexMethod")
fun fromDestination(destination: DestinationSpec<*>?, arguments: Bundle?, isAppVisible: Boolean): CurrentScreen {
if (!isAppVisible) {
Expand All @@ -171,7 +184,7 @@ sealed class CurrentScreen {
Conversation(destination.argsFrom(arguments).conversationId)

is OtherUserProfileScreenDestination ->
destination.argsFrom(arguments).conversationId?.let { OtherUserProfile(it) } ?: SomeOther
destination.argsFrom(arguments).conversationId?.let { OtherUserProfile(it) } ?: SomeOther(destination.baseRoute)

is ImportMediaScreenDestination -> ImportMedia

Expand All @@ -189,9 +202,9 @@ sealed class CurrentScreen {
is E2EIEnrollmentScreenDestination,
is E2eiCertificateDetailsScreenDestination,
is RegisterDeviceScreenDestination,
is RemoveDeviceScreenDestination -> AuthRelated
is RemoveDeviceScreenDestination -> AuthRelated(destination.baseRoute)

else -> SomeOther
else -> SomeOther(destination?.baseRoute)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ class WireNotificationManagerTest {
fun givenNotAuthenticatedUser_whenObserveCalled_thenNothingHappenAndCallNotificationHides() =
runTestWithCancellation(dispatcherProvider.main()) {
val (arrangement, manager) = Arrangement()
.withCurrentScreen(CurrentScreen.SomeOther)
.withCurrentScreen(CurrentScreen.SomeOther())
.arrange()

manager.observeNotificationsAndCallsWhileRunning(listOf(), this)
Expand All @@ -152,7 +152,7 @@ class WireNotificationManagerTest {
val (arrangement, manager) = Arrangement()
.withSpecificUserSession(userId = userId, incomingCalls = incomingCalls)
.withMessageNotifications(listOf())
.withCurrentScreen(CurrentScreen.SomeOther)
.withCurrentScreen(CurrentScreen.SomeOther())
.withCurrentUserSession(CurrentSessionResult.Success(AccountInfo.Valid(userId)))
.arrange()

Expand All @@ -174,7 +174,7 @@ class WireNotificationManagerTest {
.withSpecificUserSession(userId = user1, incomingCalls = listOf())
.withSpecificUserSession(userId = user2, incomingCalls = incomingCalls)
.withMessageNotifications(listOf())
.withCurrentScreen(CurrentScreen.SomeOther)
.withCurrentScreen(CurrentScreen.SomeOther())
.withCurrentUserSession(CurrentSessionResult.Success(provideAccountInfo(user1.value)))
.arrange()

Expand All @@ -198,7 +198,7 @@ class WireNotificationManagerTest {
)
)
)
.withCurrentScreen(CurrentScreen.SomeOther).arrange()
.withCurrentScreen(CurrentScreen.SomeOther()).arrange()

manager.observeNotificationsAndCallsWhileRunning(listOf(), this)
runCurrent()
Expand Down Expand Up @@ -249,7 +249,7 @@ class WireNotificationManagerTest {
.withMessageNotifications(messageNotifications)
.withIncomingCalls(listOf())
.withOutgoingCalls(listOf())
.withCurrentScreen(CurrentScreen.SomeOther)
.withCurrentScreen(CurrentScreen.SomeOther())
.arrange()

manager.observeNotificationsAndCallsWhileRunning(listOf(provideUserId(TestUser.SELF_USER.id.value)), this)
Expand Down Expand Up @@ -960,7 +960,7 @@ class WireNotificationManagerTest {
)
.withIncomingCalls(listOf())
.withOutgoingCalls(listOf())
.withCurrentScreen(CurrentScreen.SomeOther)
.withCurrentScreen(CurrentScreen.SomeOther())
.withObserveE2EIRequired(E2EIRequiredResult.NoGracePeriod.Create)
.arrange()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -465,7 +465,7 @@ class WireActivityViewModelTest {
val (_, viewModel) = Arrangement()
.withNoCurrentSession()
.withNewClient(NewClientResult.InCurrentAccount(listOf(TestClient.CLIENT), USER_ID))
.withCurrentScreen(MutableStateFlow<CurrentScreen>(CurrentScreen.SomeOther))
.withCurrentScreen(MutableStateFlow<CurrentScreen>(CurrentScreen.SomeOther()))
.arrange()

assertEquals(
Expand All @@ -479,7 +479,7 @@ class WireActivityViewModelTest {
val (_, viewModel) = Arrangement()
.withNoCurrentSession()
.withNewClient(NewClientResult.InOtherAccount(listOf(TestClient.CLIENT), USER_ID, "name", "handle"))
.withCurrentScreen(MutableStateFlow<CurrentScreen>(CurrentScreen.SomeOther))
.withCurrentScreen(MutableStateFlow<CurrentScreen>(CurrentScreen.SomeOther()))
.arrange()

assertEquals(
Expand All @@ -495,7 +495,7 @@ class WireActivityViewModelTest {

@Test
fun `given newClient is registered when current screen does not allow dialog, then remember NewClient dialog state`() = runTest {
val currentScreenFlow = MutableStateFlow<CurrentScreen>(CurrentScreen.SomeOther)
val currentScreenFlow = MutableStateFlow<CurrentScreen>(CurrentScreen.SomeOther())
val newClientFlow = MutableSharedFlow<NewClientResult>()
val (_, viewModel) = Arrangement()
.withNoCurrentSession()
Expand All @@ -513,7 +513,7 @@ class WireActivityViewModelTest {

@Test
fun `given newClient is registered when current screen changed to ImportMedea, then remember NewClient dialog state`() = runTest {
val currentScreenFlow = MutableStateFlow<CurrentScreen>(CurrentScreen.SomeOther)
val currentScreenFlow = MutableStateFlow<CurrentScreen>(CurrentScreen.SomeOther())
val (_, viewModel) = Arrangement()
.withNoCurrentSession()
.withNewClient(NewClientResult.InCurrentAccount(listOf(TestClient.CLIENT), USER_ID))
Expand Down Expand Up @@ -628,7 +628,7 @@ class WireActivityViewModelTest {
every { observeScreenshotCensoringConfigUseCaseProviderFactory.create(any()).observeScreenshotCensoringConfig } returns
observeScreenshotCensoringConfigUseCase
coEvery { observeScreenshotCensoringConfigUseCase() } returns flowOf(ObserveScreenshotCensoringConfigResult.Disabled)
coEvery { currentScreenManager.observeCurrentScreen(any()) } returns MutableStateFlow(CurrentScreen.SomeOther)
coEvery { currentScreenManager.observeCurrentScreen(any()) } returns MutableStateFlow(CurrentScreen.SomeOther())
coEvery { globalDataStore.selectedThemeOptionFlow() } returns flowOf(ThemeOption.LIGHT)
coEvery { observeIfE2EIRequiredDuringLoginUseCaseProviderFactory.create(any()).observeIfE2EIIsRequiredDuringLogin() } returns
flowOf(false)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ class CommonTopAppBarViewModelTest {
.withOngoingCall(isMuted = false)
.withoutOutgoingCall()
.withoutIncomingCall()
.withCurrentScreen(CurrentScreen.SomeOther)
.withCurrentScreen(CurrentScreen.SomeOther())
.withSyncState(SyncState.Waiting)
.arrange()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -391,9 +391,7 @@ class RecordAudioViewModelTest {
coEvery { generateAudioFileWithEffects(any(), any(), any()) } returns Unit

coEvery { currentScreenManager.observeCurrentScreen(any()) } returns MutableStateFlow(
CurrentScreen.Conversation(
id = DUMMY_CALL.conversationId
)
CurrentScreen.Conversation(id = DUMMY_CALL.conversationId)
)

coEvery { recordAudioMessagePlayer.audioMessageStateFlow } returns flowOf(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -170,4 +170,29 @@ object AnonymousAnalyticsManagerImpl : AnonymousAnalyticsManager {
Log.w(TAG, "Calling isAnalyticsInitialized with a null recorder.")
false
}

override fun recordView(screen: String) {
coroutineScope.launch {
mutex.withLock {
if (!isAnonymousUsageDataEnabled) return@withLock
anonymousAnalyticsRecorder?.recordView(screen)
}
}
}

override fun stopView(screen: String) {
coroutineScope.launch {
mutex.withLock {
if (!isAnonymousUsageDataEnabled) return@withLock
anonymousAnalyticsRecorder?.stopView(screen)
}
}
}

override fun applicationOnCreate() {
if (!isAnonymousUsageDataEnabled) return

anonymousAnalyticsRecorder?.applicationOnCreate()
?: Log.w(TAG, "Calling applicationOnCreate with a null recorder.")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
package com.wire.android.feature.analytics

import android.app.Activity
import android.app.Application
import android.content.Context
import com.wire.android.feature.analytics.model.AnalyticsEvent
import com.wire.android.feature.analytics.model.AnalyticsEventConstants
Expand All @@ -42,8 +43,12 @@ class AnonymousAnalyticsRecorderImpl : AnonymousAnalyticsRecorder {
)
.enableTemporaryDeviceIdMode() // Nothing is sent until a proper ID is placed
.setLoggingEnabled(analyticsSettings.enableDebugLogging)
countlyConfig.apm.enableAppStartTimeTracking()
countlyConfig.apm.enableForegroundBackgroundTracking()
countlyConfig.setApplication(context.applicationContext as Application)

Countly.sharedInstance().init(countlyConfig)
Countly.sharedInstance().consent().giveConsent(arrayOf("apm"))

val packageInfo = context.packageManager.getPackageInfo(context.packageName, 0)
val globalSegmentations = mapOf<String, Any>(
Expand Down Expand Up @@ -107,4 +112,18 @@ class AnonymousAnalyticsRecorderImpl : AnonymousAnalyticsRecorder {
}

override fun isAnalyticsInitialized(): Boolean = Countly.sharedInstance().isInitialized

override fun applicationOnCreate() {
if (isConfigured) return

Countly.applicationOnCreate()
}

override fun recordView(screen: String) {
Countly.sharedInstance().views().startAutoStoppedView(screen)
}

override fun stopView(screen: String) {
Countly.sharedInstance().views().stopViewWithName(screen)
}
}
Loading

0 comments on commit 59f9c3d

Please sign in to comment.