Skip to content

Commit

Permalink
feat: Track ended call segmentation [#WPB-14256] (#3756)
Browse files Browse the repository at this point in the history
  • Loading branch information
m-zagorski authored Dec 23, 2024
1 parent 22a0b22 commit 8d9c35d
Show file tree
Hide file tree
Showing 5 changed files with 123 additions and 5 deletions.
20 changes: 20 additions & 0 deletions app/src/main/kotlin/com/wire/android/WireApplication.kt
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import com.wire.android.datastore.UserDataStoreProvider
import com.wire.android.debug.DatabaseProfilingManager
import com.wire.android.di.ApplicationScope
import com.wire.android.di.KaliumCoreLogic
import com.wire.android.feature.analytics.AnonymousAnalyticsManager
import com.wire.android.feature.analytics.AnonymousAnalyticsManagerImpl
import com.wire.android.feature.analytics.AnonymousAnalyticsRecorderImpl
import com.wire.android.feature.analytics.globalAnalyticsManager
Expand All @@ -48,12 +49,15 @@ import com.wire.kalium.logger.KaliumLogLevel
import com.wire.kalium.logger.KaliumLogger
import com.wire.kalium.logic.CoreLogger
import com.wire.kalium.logic.CoreLogic
import com.wire.kalium.logic.feature.session.CurrentSessionResult
import dagger.Lazy
import dagger.hilt.android.HiltAndroidApp
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.filterIsInstance
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import javax.inject.Inject
Expand Down Expand Up @@ -93,6 +97,9 @@ class WireApplication : BaseApp() {
@Inject
lateinit var databaseProfilingManager: DatabaseProfilingManager

@Inject
lateinit var analyticsManager: Lazy<AnonymousAnalyticsManager>

override val workManagerConfiguration: Configuration
get() = Configuration.Builder()
.setWorkerFactory(wireWorkerFactory.get())
Expand Down Expand Up @@ -121,9 +128,22 @@ class WireApplication : BaseApp() {

appLogger.i("$TAG global observers")
globalObserversManager.get().observe()

observeRecentlyEndedCall()
}
}

private suspend fun observeRecentlyEndedCall() {
coreLogic.get().getGlobalScope().session.currentSessionFlow().filterIsInstance(CurrentSessionResult.Success::class)
.filter { session -> session.accountInfo.isValid() }
.flatMapLatest { session ->
coreLogic.get().getSessionScope(session.accountInfo.userId).calls.observeRecentlyEndedCallMetadata()
}
.collect { metadata ->
analyticsManager.get().sendEvent(AnalyticsEvent.RecentlyEndedCallEvent(metadata))
}
}

private fun enableStrictMode() {
if (BuildConfig.DEBUG) {
StrictMode.setThreadPolicy(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,6 @@ package com.wire.android.di.accountScoped

import com.wire.android.di.CurrentAccount
import com.wire.android.di.KaliumCoreLogic
import dagger.Module
import dagger.Provides
import com.wire.kalium.logic.CoreLogic
import com.wire.kalium.logic.data.user.UserId
import com.wire.kalium.logic.feature.call.CallsScope
Expand All @@ -40,6 +38,8 @@ import com.wire.kalium.logic.feature.call.usecase.TurnLoudSpeakerOnUseCase
import com.wire.kalium.logic.feature.call.usecase.UnMuteCallUseCase
import com.wire.kalium.logic.feature.call.usecase.video.SetVideoSendStateUseCase
import com.wire.kalium.logic.feature.call.usecase.video.UpdateVideoStateUseCase
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.components.ViewModelComponent
import dagger.hilt.android.scopes.ViewModelScoped
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ class WireActivityViewModel @Inject constructor(
private val observeScreenshotCensoringConfigUseCaseProviderFactory: ObserveScreenshotCensoringConfigUseCaseProvider.Factory,
private val globalDataStore: Lazy<GlobalDataStore>,
private val observeIfE2EIRequiredDuringLoginUseCaseProviderFactory: ObserveIfE2EIRequiredDuringLoginUseCaseProvider.Factory,
private val workManager: Lazy<WorkManager>,
private val workManager: Lazy<WorkManager>
) : ViewModel() {

var globalAppState: GlobalAppState by mutableStateOf(GlobalAppState())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ import com.wire.kalium.logic.data.auth.AccountInfo
import com.wire.kalium.logic.data.auth.PersistentWebSocketStatus
import com.wire.kalium.logic.data.call.Call
import com.wire.kalium.logic.data.call.CallStatus
import com.wire.kalium.logic.data.call.RecentlyEndedCallMetadata
import com.wire.kalium.logic.data.conversation.Conversation
import com.wire.kalium.logic.data.id.ConversationId
import com.wire.kalium.logic.data.id.QualifiedID
Expand Down Expand Up @@ -974,6 +975,28 @@ class WireActivityViewModelTest {
callerTeamName = "team1"
)

val recentlyEndedCallMetadata = RecentlyEndedCallMetadata(
callEndReason = 1,
callDetails = RecentlyEndedCallMetadata.CallDetails(
isCallScreenShare = false,
screenShareDurationInSeconds = 20L,
callScreenShareUniques = 5,
isOutgoingCall = true,
callDurationInSeconds = 100L,
callParticipantsCount = 5,
conversationServices = 1,
callAVSwitchToggle = false,
callVideoEnabled = false
),
conversationDetails = RecentlyEndedCallMetadata.ConversationDetails(
conversationType = Conversation.Type.ONE_ON_ONE,
conversationSize = 5,
conversationGuests = 2,
conversationGuestsPro = 1
),
isTeamMember = true
)

fun invalidAccountInfo(logoutReason: LogoutReason): AccountInfo.Invalid = AccountInfo.Invalid(USER_ID, logoutReason)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,21 @@
*/
package com.wire.android.feature.analytics.model

import com.wire.android.feature.analytics.model.AnalyticsEventConstants.CALLING_ENDED
import com.wire.android.feature.analytics.model.AnalyticsEventConstants.CALLING_ENDED_AV_SWITCH_TOGGLE
import com.wire.android.feature.analytics.model.AnalyticsEventConstants.CALLING_ENDED_CALL_DIRECTION
import com.wire.android.feature.analytics.model.AnalyticsEventConstants.CALLING_ENDED_CALL_DURATION
import com.wire.android.feature.analytics.model.AnalyticsEventConstants.CALLING_ENDED_CALL_PARTICIPANTS
import com.wire.android.feature.analytics.model.AnalyticsEventConstants.CALLING_ENDED_CALL_SCREEN_SHARE
import com.wire.android.feature.analytics.model.AnalyticsEventConstants.CALLING_ENDED_CALL_VIDEO
import com.wire.android.feature.analytics.model.AnalyticsEventConstants.CALLING_ENDED_CONVERSATION_GUESTS
import com.wire.android.feature.analytics.model.AnalyticsEventConstants.CALLING_ENDED_CONVERSATION_GUESTS_PRO
import com.wire.android.feature.analytics.model.AnalyticsEventConstants.CALLING_ENDED_CONVERSATION_SERVICES
import com.wire.android.feature.analytics.model.AnalyticsEventConstants.CALLING_ENDED_CONVERSATION_SIZE
import com.wire.android.feature.analytics.model.AnalyticsEventConstants.CALLING_ENDED_CONVERSATION_TYPE
import com.wire.android.feature.analytics.model.AnalyticsEventConstants.CALLING_ENDED_END_REASON
import com.wire.android.feature.analytics.model.AnalyticsEventConstants.CALLING_ENDED_IS_TEAM_MEMBER
import com.wire.android.feature.analytics.model.AnalyticsEventConstants.CALLING_ENDED_UNIQUE_SCREEN_SHARE
import com.wire.android.feature.analytics.model.AnalyticsEventConstants.CALLING_QUALITY_REVIEW_IGNORE_REASON
import com.wire.android.feature.analytics.model.AnalyticsEventConstants.CALLING_QUALITY_REVIEW_IGNORE_REASON_KEY
import com.wire.android.feature.analytics.model.AnalyticsEventConstants.CALLING_QUALITY_REVIEW_LABEL_ANSWERED
Expand All @@ -29,8 +44,6 @@ import com.wire.android.feature.analytics.model.AnalyticsEventConstants.CLICKED_
import com.wire.android.feature.analytics.model.AnalyticsEventConstants.CLICKED_PERSONAL_MIGRATION_CTA_EVENT
import com.wire.android.feature.analytics.model.AnalyticsEventConstants.CONTRIBUTED_LOCATION
import com.wire.android.feature.analytics.model.AnalyticsEventConstants.MESSAGE_ACTION_KEY
import com.wire.android.feature.analytics.model.AnalyticsEventConstants.QR_CODE_SEGMENTATION_USER_TYPE_PERSONAL
import com.wire.android.feature.analytics.model.AnalyticsEventConstants.QR_CODE_SEGMENTATION_USER_TYPE_TEAM
import com.wire.android.feature.analytics.model.AnalyticsEventConstants.MIGRATION_DOT_ACTIVE
import com.wire.android.feature.analytics.model.AnalyticsEventConstants.MODAL_BACK_TO_WIRE_CLICKED
import com.wire.android.feature.analytics.model.AnalyticsEventConstants.MODAL_CONTINUE_CLICKED
Expand All @@ -40,8 +53,12 @@ import com.wire.android.feature.analytics.model.AnalyticsEventConstants.MODAL_TE
import com.wire.android.feature.analytics.model.AnalyticsEventConstants.PERSONAL_TEAM_CREATION_FLOW_CANCELLED
import com.wire.android.feature.analytics.model.AnalyticsEventConstants.PERSONAL_TEAM_CREATION_FLOW_COMPLETED
import com.wire.android.feature.analytics.model.AnalyticsEventConstants.PERSONAL_TEAM_CREATION_FLOW_STARTED_EVENT
import com.wire.android.feature.analytics.model.AnalyticsEventConstants.QR_CODE_SEGMENTATION_USER_TYPE_PERSONAL
import com.wire.android.feature.analytics.model.AnalyticsEventConstants.QR_CODE_SEGMENTATION_USER_TYPE_TEAM
import com.wire.android.feature.analytics.model.AnalyticsEventConstants.STEP_MODAL_CREATE_TEAM
import com.wire.android.feature.analytics.model.AnalyticsEventConstants.USER_PROFILE_OPENED
import com.wire.kalium.logic.data.call.RecentlyEndedCallMetadata
import com.wire.kalium.logic.data.conversation.Conversation

interface AnalyticsEvent {
/**
Expand Down Expand Up @@ -129,6 +146,45 @@ interface AnalyticsEvent {
}
}

data class RecentlyEndedCallEvent(val metadata: RecentlyEndedCallMetadata) : AnalyticsEvent {
override val key: String = CALLING_ENDED

override fun toSegmentation(): Map<String, Any> {
return mapOf(
CALLING_ENDED_IS_TEAM_MEMBER to metadata.isTeamMember,
CALLING_ENDED_CALL_SCREEN_SHARE to metadata.callDetails.screenShareDurationInSeconds,
CALLING_ENDED_UNIQUE_SCREEN_SHARE to metadata.callDetails.callScreenShareUniques,
CALLING_ENDED_CALL_DIRECTION to metadata.toCallDirection(),
CALLING_ENDED_CALL_DURATION to metadata.callDetails.callDurationInSeconds,
CALLING_ENDED_CONVERSATION_TYPE to metadata.toConversationType(),
CALLING_ENDED_CONVERSATION_SIZE to metadata.conversationDetails.conversationSize,
CALLING_ENDED_CONVERSATION_GUESTS to metadata.conversationDetails.conversationGuests,
CALLING_ENDED_CONVERSATION_GUESTS_PRO to metadata.conversationDetails.conversationGuestsPro,
CALLING_ENDED_CALL_PARTICIPANTS to metadata.callDetails.callParticipantsCount,
CALLING_ENDED_END_REASON to metadata.callEndReason,
CALLING_ENDED_CONVERSATION_SERVICES to metadata.callDetails.conversationServices,
CALLING_ENDED_AV_SWITCH_TOGGLE to metadata.callDetails.callAVSwitchToggle,
CALLING_ENDED_CALL_VIDEO to metadata.callDetails.callVideoEnabled,
)
}

private fun RecentlyEndedCallMetadata.toCallDirection(): String {
return if (callDetails.isOutgoingCall) {
"outgoing"
} else {
"incoming"
}
}

private fun RecentlyEndedCallMetadata.toConversationType(): String {
return when (conversationDetails.conversationType) {
Conversation.Type.ONE_ON_ONE -> "one_to_one"
Conversation.Type.GROUP -> "group"
else -> throw IllegalStateException("Call should not happen for ${conversationDetails.conversationType}")
}
}
}

/**
* Backup
*/
Expand Down Expand Up @@ -336,6 +392,7 @@ object AnalyticsEventConstants {
*/
const val CALLING_INITIATED = "calling.initiated_call"
const val CALLING_JOINED = "calling.joined_call"
const val CALLING_ENDED = "calling.ended_call"

const val CALLING_QUALITY_REVIEW = "calling.call_quality_review"
const val CALLING_QUALITY_REVIEW_LABEL_KEY = "label"
Expand All @@ -346,6 +403,24 @@ object AnalyticsEventConstants {
const val CALLING_QUALITY_REVIEW_IGNORE_REASON_KEY = "ignore-reason"
const val CALLING_QUALITY_REVIEW_IGNORE_REASON = "muted"

/**
* Call ended
*/
const val CALLING_ENDED_IS_TEAM_MEMBER = "is_team_member"
const val CALLING_ENDED_CALL_SCREEN_SHARE = "call_screen_share_duration"
const val CALLING_ENDED_UNIQUE_SCREEN_SHARE = "call_screen_share_unique"
const val CALLING_ENDED_CALL_DIRECTION = "call_direction"
const val CALLING_ENDED_CALL_DURATION = "call_duration"
const val CALLING_ENDED_CONVERSATION_TYPE = "conversation_type"
const val CALLING_ENDED_CONVERSATION_SIZE = "conversation_size"
const val CALLING_ENDED_CONVERSATION_GUESTS = "conversation_guests"
const val CALLING_ENDED_CONVERSATION_GUESTS_PRO = "conversation_guest_pro"
const val CALLING_ENDED_CALL_PARTICIPANTS = "call_participants"
const val CALLING_ENDED_END_REASON = "call_end_reason"
const val CALLING_ENDED_CONVERSATION_SERVICES = "conversation_services"
const val CALLING_ENDED_AV_SWITCH_TOGGLE = "call_av_switch_toggle"
const val CALLING_ENDED_CALL_VIDEO = "call_video"

/**
* Backup
*/
Expand Down

0 comments on commit 8d9c35d

Please sign in to comment.