Skip to content

Commit

Permalink
fix: Conversations and Profile Views are reported individually (WPB-1…
Browse files Browse the repository at this point in the history
…4942) (#3792)
  • Loading branch information
ohassine authored Jan 28, 2025
1 parent 65de869 commit 0a8b0c6
Show file tree
Hide file tree
Showing 4 changed files with 110 additions and 9 deletions.
39 changes: 32 additions & 7 deletions app/src/main/kotlin/com/wire/android/util/CurrentScreenManager.kt
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ 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.getBaseRoute
import com.wire.android.navigation.toDestination
import com.wire.android.ui.destinations.ConversationScreenDestination
import com.wire.android.ui.destinations.CreateAccountDetailsScreenDestination
Expand Down Expand Up @@ -123,17 +124,31 @@ class CurrentScreenManager @Inject constructor(
}

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

val currentItem = destination.toDestination()
currentScreenState.value = CurrentScreen.fromDestination(
currentItem,
arguments,
isApplicationVisibleFlow.value
)

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

private fun currentScreenName() = currentScreenState.value.let { currentScreen ->
when (currentScreen) {
is CurrentScreen.Home,
is CurrentScreen.Conversation,
is CurrentScreen.OtherUserProfile,
is CurrentScreen.ImportMedia,
is CurrentScreen.DeviceManager -> return@let currentScreen.toScreenName()

is CurrentScreen.AuthRelated -> return@let currentScreen.route?.getBaseRoute() ?: currentScreen.toString()
else -> return@let (currentScreen as? CurrentScreen.SomeOther)?.route?.getBaseRoute() ?: currentScreen.toString()
}
}

override fun onCreate(owner: LifecycleOwner) {
Expand Down Expand Up @@ -164,23 +179,31 @@ class CurrentScreenManager @Inject constructor(
sealed class CurrentScreen {

// Home Screen is being displayed
data object Home : CurrentScreen()
data object Home : CurrentScreen() {
override fun toScreenName() = "HomeScreen"
}

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

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

// Import media screen is opened
data object ImportMedia : CurrentScreen()
data object ImportMedia : CurrentScreen() {
override fun toScreenName() = "ImportMediaScreen"
}

// SelfDevices screen is opened
data object DeviceManager : CurrentScreen()
data object DeviceManager : CurrentScreen() {
override fun toScreenName() = "DeviceManagerScreen"
}

// Auth related screen is opened
data class AuthRelated(val route: String?) : CurrentScreen()
Expand All @@ -191,6 +214,8 @@ sealed class CurrentScreen {
// App is in background (screen is turned off, or covered by another app), non of the screens is visible
data object InBackground : CurrentScreen()

open fun toScreenName(): String = "UnknownScreen"

companion object {
@SuppressLint("RestrictedApi")
@Suppress("ComplexMethod")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ object AnonymousAnalyticsManagerImpl : AnonymousAnalyticsManager {
coroutineScope.launch {
mutex.withLock {
if (!isAnonymousUsageDataEnabled) return@withLock
anonymousAnalyticsRecorder?.recordView(screen)
anonymousAnalyticsRecorder?.recordView(screen.convertToCamelCase())
}
}
}
Expand All @@ -195,7 +195,7 @@ object AnonymousAnalyticsManagerImpl : AnonymousAnalyticsManager {
coroutineScope.launch {
mutex.withLock {
if (!isAnonymousUsageDataEnabled) return@withLock
anonymousAnalyticsRecorder?.stopView(screen)
anonymousAnalyticsRecorder?.stopView(screen.convertToCamelCase())
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
* Wire
* Copyright (C) 2025 Wire Swiss GmbH
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://www.gnu.org/licenses/.
*/
package com.wire.android.feature.analytics

/**
* Converts a snake_case string to camelCase.
*/
fun String.convertToCamelCase(): String {
return this
.split('_')
.joinToString("") { it.replaceFirstChar { char -> char.uppercase() } }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* Wire
* Copyright (C) 2025 Wire Swiss GmbH
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://www.gnu.org/licenses/.
*/
package com.wire.android.feature.analytics

import org.junit.Assert.assertEquals
import org.junit.Test

class StringExtTest {

@Test
fun `given single word string when converted then return same string`() {
assertEquals("Username", "username".convertToCamelCase())
}

@Test
fun `given string with multiple underscores when converted then return camel case string`() {
assertEquals("ThisIsATestCase", ("this_is_a_test_case").convertToCamelCase())
}

@Test
fun `given empty string when converted then return empty string`() {
assertEquals("", ("").convertToCamelCase())
}

@Test
fun `given string with leading and trailing underscores when converted then return camel case string`() {
assertEquals("LeadingAndTrailing", ("_leading_and_trailing_").convertToCamelCase())
}

@Test
fun `given string with numbers when converted then return camel case string`() {
assertEquals("User123Name", ("user_123_name").convertToCamelCase())
}
}

0 comments on commit 0a8b0c6

Please sign in to comment.