Updated on 2026-08-14
This commit is contained in:
parent
d7db2c77f5
commit
de93d0f350
14 changed files with 140 additions and 89 deletions
|
|
@ -1 +1 @@
|
|||
Subproject commit cf6ea50867477f97655da9da47ff94987e8f3354
|
||||
Subproject commit a1658496e777b611fc990ef2bc1a1a1fd48bc1e6
|
||||
|
|
@ -6,9 +6,15 @@ import com.tangem.common.core.TangemSdkError
|
|||
import com.tangem.domain.common.FeatureCoroutineExceptionHandler
|
||||
import com.tangem.tap.common.analytics.api.AnalyticsEventFilter
|
||||
import com.tangem.tap.common.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.tap.common.analytics.api.AnalyticsFilterHolder
|
||||
import com.tangem.tap.common.analytics.api.AnalyticsHandlerHolder
|
||||
import com.tangem.tap.common.analytics.api.BlockchainSdkErrorEventHandler
|
||||
import com.tangem.tap.common.analytics.api.CardSdkErrorEventHandler
|
||||
import com.tangem.tap.common.analytics.api.ErrorEventHandler
|
||||
import com.tangem.tap.common.analytics.api.ErrorEventLogger
|
||||
import com.tangem.tap.common.analytics.api.ParamsInterceptor
|
||||
import com.tangem.tap.common.analytics.api.ParamsInterceptorHolder
|
||||
import com.tangem.tap.common.analytics.api.SdkErrorEventHandler
|
||||
import com.tangem.tap.common.analytics.events.AnalyticsEvent
|
||||
import com.tangem.tap.common.extensions.filterNotNull
|
||||
import kotlinx.coroutines.CoroutineName
|
||||
|
|
@ -21,18 +27,27 @@ import java.util.concurrent.Executors
|
|||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
interface GlobalAnalyticsEventHandler : AnalyticsEventHandler,
|
||||
ErrorEventHandler,
|
||||
SdkErrorEventHandler,
|
||||
AnalyticsHandlerHolder,
|
||||
AnalyticsFilterHolder,
|
||||
ParamsInterceptorHolder {
|
||||
|
||||
fun send(event: AnalyticsEvent, card: Card? = null, blockchain: String? = null)
|
||||
}
|
||||
|
||||
object Analytics : GlobalAnalyticsEventHandler {
|
||||
|
||||
internal val analyticsScope: CoroutineScope by lazy { createScope() }
|
||||
private val analyticsScope: CoroutineScope by lazy { createScope() }
|
||||
|
||||
private val eventFilters = mutableListOf<AnalyticsEventFilter>()
|
||||
private val handlers = mutableMapOf<String, AnalyticsEventHandler>()
|
||||
private val analyticsFilters = mutableSetOf<AnalyticsEventFilter>()
|
||||
private val paramsInterceptors = mutableMapOf<String, ParamsInterceptor>()
|
||||
|
||||
private val analyticsHandlers: List<AnalyticsEventHandler>
|
||||
get() = handlers.values.toList()
|
||||
|
||||
private val attachToAllEventsParams: MutableMap<String, String> = mutableMapOf()
|
||||
|
||||
override fun id(): String = analyticsHandlers.joinToString(", ") { it.id() }
|
||||
|
||||
override fun addHandler(name: String, handler: AnalyticsEventHandler) {
|
||||
|
|
@ -44,33 +59,39 @@ object Analytics : GlobalAnalyticsEventHandler {
|
|||
}
|
||||
|
||||
override fun addFilter(filter: AnalyticsEventFilter) {
|
||||
eventFilters.add(filter)
|
||||
analyticsFilters.add(filter)
|
||||
}
|
||||
|
||||
override fun removeFilter(filter: AnalyticsEventFilter) {
|
||||
eventFilters.remove(filter)
|
||||
override fun removeFilter(filter: AnalyticsEventFilter): Boolean {
|
||||
return analyticsFilters.remove(filter)
|
||||
}
|
||||
|
||||
override fun attachToAllEvents(key: String, value: String) {
|
||||
attachToAllEventsParams[key] = value
|
||||
override fun addParamsInterceptor(interceptor: ParamsInterceptor) {
|
||||
paramsInterceptors[interceptor.id()] = interceptor
|
||||
}
|
||||
|
||||
override fun removeParamsInterceptor(interceptor: ParamsInterceptor): ParamsInterceptor? {
|
||||
return paramsInterceptors.remove(interceptor.id())
|
||||
}
|
||||
|
||||
override fun send(event: String, params: Map<String, String>) {
|
||||
analyticsScope.launch {
|
||||
analyticsHandlers.forEach { it.send(event, params) }
|
||||
analyticsHandlers.forEach { it.send(event, params.interceptParams()) }
|
||||
}
|
||||
}
|
||||
|
||||
override fun send(event: AnalyticsEvent, card: Card?, blockchain: String?) {
|
||||
analyticsScope.launch {
|
||||
val eventFilter = eventFilters.firstOrNull { it.canBeAppliedTo(event) }
|
||||
val eventString = prepareEventString(event.category, event.event)
|
||||
val eventParams = prepareParams(card, blockchain, event.interceptParams())
|
||||
|
||||
val eventFilter = analyticsFilters.firstOrNull { it.canBeAppliedTo(event) }
|
||||
when {
|
||||
eventFilter == null -> analyticsHandlers.forEach { it.send(event, card, blockchain) }
|
||||
eventFilter == null -> analyticsHandlers.forEach { it.send(eventString, eventParams) }
|
||||
eventFilter.canBeSent(event) -> {
|
||||
analyticsHandlers
|
||||
.filter { eventFilter.canBeConsumedBy(it, event) }
|
||||
.forEach { it.send(event) }
|
||||
.filter { handler -> eventFilter.canBeConsumedByHandler(handler, event) }
|
||||
.forEach { it.send(eventString, eventParams) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -84,7 +105,7 @@ object Analytics : GlobalAnalyticsEventHandler {
|
|||
) {
|
||||
analyticsScope.launch {
|
||||
analyticsHandlers.forEach {
|
||||
it.handleAnalyticsEvent(event, params, card, blockchain)
|
||||
it.handleAnalyticsEvent(event, params.interceptParams(), card, blockchain)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -123,17 +144,21 @@ object Analytics : GlobalAnalyticsEventHandler {
|
|||
}
|
||||
}
|
||||
|
||||
override fun prepareParams(card: Card?, blockchain: String?, params: Map<String, String>): Map<String, String> {
|
||||
return super.prepareParams(card, blockchain, params).toMutableMap().apply {
|
||||
putAll(attachToAllEventsParams)
|
||||
}
|
||||
private fun AnalyticsEvent.interceptParams(): Map<String, String> = params.interceptParams()
|
||||
|
||||
private fun Map<String, String>.interceptParams(): Map<String, String> {
|
||||
return this.toMutableMap().apply { interceptParams() }
|
||||
}
|
||||
|
||||
private fun MutableMap<String, String>.interceptParams() {
|
||||
paramsInterceptors.values.forEach { it.intercept(this) }
|
||||
}
|
||||
|
||||
private fun createScope(): CoroutineScope {
|
||||
val name = "Analytics"
|
||||
val dispatcher = Executors.newFixedThreadPool(1).asCoroutineDispatcher()
|
||||
val exceptionHandler = FeatureCoroutineExceptionHandler.create(name)
|
||||
return CoroutineScope(Job() + dispatcher + CoroutineName(name) + exceptionHandler)
|
||||
val exHandler = FeatureCoroutineExceptionHandler.create(name)
|
||||
return CoroutineScope(Job() + dispatcher + CoroutineName(name) + exHandler)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,21 +0,0 @@
|
|||
package com.tangem.tap.common.analytics
|
||||
|
||||
import com.tangem.tap.common.analytics.api.AnalyticsEventFilter
|
||||
import com.tangem.tap.common.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.tap.common.analytics.api.ErrorEventHandler
|
||||
import com.tangem.tap.common.analytics.api.SdkErrorEventHandler
|
||||
|
||||
interface GlobalAnalyticsEventHandler : AnalyticsEventHandler,
|
||||
ErrorEventHandler,
|
||||
SdkErrorEventHandler {
|
||||
|
||||
fun addHandler(name: String, handler: AnalyticsEventHandler)
|
||||
|
||||
fun removeHandler(name: String): AnalyticsEventHandler?
|
||||
|
||||
fun addFilter(filter: AnalyticsEventFilter)
|
||||
|
||||
fun removeFilter(filter: AnalyticsEventFilter)
|
||||
|
||||
fun attachToAllEvents(key: String, value: String)
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
package com.tangem.tap.common.analytics.api
|
||||
|
||||
import com.tangem.blockchain.common.BlockchainError
|
||||
import com.tangem.common.card.Card
|
||||
import com.tangem.common.core.TangemSdkError
|
||||
import com.tangem.tap.common.analytics.AnalyticsAnOld
|
||||
import com.tangem.tap.common.analytics.AnalyticsParamAnOld
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
interface ErrorEventHandler {
|
||||
fun send(
|
||||
error: Throwable,
|
||||
params: Map<String, String> = emptyMap(),
|
||||
)
|
||||
}
|
||||
|
||||
interface SdkErrorEventHandler : CardSdkErrorEventHandler, BlockchainSdkErrorEventHandler
|
||||
|
||||
interface CardSdkErrorEventHandler {
|
||||
fun send(
|
||||
error: TangemSdkError,
|
||||
action: AnalyticsAnOld.ActionToLog,
|
||||
params: Map<AnalyticsParamAnOld, String> = emptyMap(),
|
||||
card: Card? = null,
|
||||
)
|
||||
}
|
||||
|
||||
interface BlockchainSdkErrorEventHandler {
|
||||
fun send(
|
||||
error: BlockchainError,
|
||||
action: AnalyticsAnOld.ActionToLog,
|
||||
params: Map<AnalyticsParamAnOld, String> = mapOf(),
|
||||
card: Card? = null,
|
||||
)
|
||||
}
|
||||
|
|
@ -8,5 +8,10 @@ import com.tangem.tap.common.analytics.events.AnalyticsEvent
|
|||
interface AnalyticsEventFilter {
|
||||
fun canBeAppliedTo(event: AnalyticsEvent): Boolean
|
||||
fun canBeSent(event: AnalyticsEvent): Boolean
|
||||
fun canBeConsumedBy(handler: AnalyticsEventHandler, event: AnalyticsEvent): Boolean
|
||||
fun canBeConsumedByHandler(handler: AnalyticsEventHandler, event: AnalyticsEvent): Boolean
|
||||
}
|
||||
|
||||
interface AnalyticsFilterHolder {
|
||||
fun addFilter(filter: AnalyticsEventFilter)
|
||||
fun removeFilter(filter: AnalyticsEventFilter): Boolean
|
||||
}
|
||||
|
|
@ -1,15 +1,10 @@
|
|||
package com.tangem.tap.common.analytics.api
|
||||
|
||||
import android.app.Application
|
||||
import com.tangem.blockchain.common.BlockchainError
|
||||
import com.tangem.common.card.Card
|
||||
import com.tangem.common.core.TangemSdkError
|
||||
import com.tangem.common.json.MoshiJsonConverter
|
||||
import com.tangem.domain.common.AnalyticsHandlersLogConfig
|
||||
import com.tangem.tap.common.analytics.AnalyticsAnOld
|
||||
import com.tangem.tap.common.analytics.AnalyticsEventAnOld
|
||||
import com.tangem.tap.common.analytics.AnalyticsParamAnOld
|
||||
import com.tangem.tap.common.analytics.events.AnalyticsEvent
|
||||
import com.tangem.tap.common.analytics.events.AnalyticsParam
|
||||
import com.tangem.tap.common.extensions.filterNotNull
|
||||
import com.tangem.tap.domain.configurable.config.Config
|
||||
|
|
@ -22,13 +17,6 @@ interface AnalyticsEventHandler {
|
|||
|
||||
fun send(event: String, params: Map<String, String> = emptyMap())
|
||||
|
||||
fun send(event: AnalyticsEvent, card: Card? = null, blockchain: String? = null) {
|
||||
send(
|
||||
event = prepareEventString(event.category, event.event),
|
||||
params = prepareParams(card, blockchain, event.params),
|
||||
)
|
||||
}
|
||||
|
||||
@Deprecated("Migrate to AnalyticsEvent")
|
||||
fun handleAnalyticsEvent(
|
||||
event: AnalyticsEventAnOld,
|
||||
|
|
@ -45,7 +33,6 @@ interface AnalyticsEventHandler {
|
|||
params: Map<String, String> = emptyMap(),
|
||||
): Map<String, String> = mapOf(
|
||||
AnalyticsParam.Firmware to card?.firmwareVersion?.stringValue,
|
||||
AnalyticsParam.BatchId to card?.batchId,
|
||||
AnalyticsParam.Blockchain to blockchain,
|
||||
).filterNotNull() + params
|
||||
|
||||
|
|
@ -54,31 +41,9 @@ interface AnalyticsEventHandler {
|
|||
}
|
||||
}
|
||||
|
||||
interface ErrorEventHandler {
|
||||
fun send(
|
||||
error: Throwable,
|
||||
params: Map<String, String> = emptyMap(),
|
||||
)
|
||||
}
|
||||
|
||||
interface SdkErrorEventHandler : CardSdkErrorEventHandler, BlockchainSdkErrorEventHandler
|
||||
|
||||
interface CardSdkErrorEventHandler {
|
||||
fun send(
|
||||
error: TangemSdkError,
|
||||
action: AnalyticsAnOld.ActionToLog,
|
||||
params: Map<AnalyticsParamAnOld, String> = emptyMap(),
|
||||
card: Card? = null,
|
||||
)
|
||||
}
|
||||
|
||||
interface BlockchainSdkErrorEventHandler {
|
||||
fun send(
|
||||
error: BlockchainError,
|
||||
action: AnalyticsAnOld.ActionToLog,
|
||||
params: Map<AnalyticsParamAnOld, String> = mapOf(),
|
||||
card: Card? = null,
|
||||
)
|
||||
interface AnalyticsHandlerHolder {
|
||||
fun addHandler(name: String, handler: AnalyticsEventHandler)
|
||||
fun removeHandler(name: String): AnalyticsEventHandler?
|
||||
}
|
||||
|
||||
interface AnalyticsHandlerBuilder {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,17 @@
|
|||
package com.tangem.tap.common.analytics.api
|
||||
|
||||
import com.tangem.tap.common.analytics.events.AnalyticsEvent
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
interface ParamsInterceptor {
|
||||
fun id(): String
|
||||
fun canBeAppliedTo(event: AnalyticsEvent): Boolean
|
||||
fun intercept(params: MutableMap<String, String>)
|
||||
}
|
||||
|
||||
interface ParamsInterceptorHolder {
|
||||
fun addParamsInterceptor(interceptor: ParamsInterceptor)
|
||||
fun removeParamsInterceptor(interceptor: ParamsInterceptor): ParamsInterceptor?
|
||||
}
|
||||
|
|
@ -55,8 +55,10 @@ sealed class AnalyticsParam {
|
|||
}
|
||||
|
||||
companion object Key {
|
||||
const val BatchId = "Batch"
|
||||
|
||||
// old keys
|
||||
const val Blockchain = "blockchain"
|
||||
const val BatchId = "batch_id"
|
||||
const val Firmware = "firmware"
|
||||
}
|
||||
}
|
||||
|
|
@ -17,7 +17,7 @@ sealed class Basic(
|
|||
params = mapOf(
|
||||
"State" to state.value,
|
||||
"Currency" to currency.value,
|
||||
"Batch" to batch,
|
||||
AnalyticsParam.BatchId to batch,
|
||||
),
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -23,5 +23,5 @@ class BasicSignInFilter : AnalyticsEventFilter {
|
|||
return canBeSent
|
||||
}
|
||||
|
||||
override fun canBeConsumedBy(handler: AnalyticsEventHandler, event: AnalyticsEvent): Boolean = true
|
||||
override fun canBeConsumedByHandler(handler: AnalyticsEventHandler, event: AnalyticsEvent): Boolean = true
|
||||
}
|
||||
|
|
@ -39,7 +39,7 @@ class BasicTopUpFilter(
|
|||
}
|
||||
}
|
||||
|
||||
override fun canBeConsumedBy(handler: AnalyticsEventHandler, event: AnalyticsEvent): Boolean = true
|
||||
override fun canBeConsumedByHandler(handler: AnalyticsEventHandler, event: AnalyticsEvent): Boolean = true
|
||||
|
||||
data class Data(
|
||||
val walletId: String,
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ class ShopPurchasedEventFilter : AnalyticsEventFilter {
|
|||
|
||||
override fun canBeSent(event: AnalyticsEvent): Boolean = true
|
||||
|
||||
override fun canBeConsumedBy(handler: AnalyticsEventHandler, event: AnalyticsEvent): Boolean {
|
||||
override fun canBeConsumedByHandler(handler: AnalyticsEventHandler, event: AnalyticsEvent): Boolean {
|
||||
return when (handler) {
|
||||
is AppsFlyerAnalyticsHandler, is FirebaseAnalyticsHandler -> true
|
||||
else -> false
|
||||
|
|
|
|||
|
|
@ -0,0 +1,21 @@
|
|||
package com.tangem.tap.common.analytics.paramsInterceptor
|
||||
|
||||
import com.tangem.tap.common.analytics.api.ParamsInterceptor
|
||||
import com.tangem.tap.common.analytics.events.AnalyticsEvent
|
||||
import com.tangem.tap.common.analytics.events.AnalyticsParam
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
class BatchIdParamsInterceptor(
|
||||
val batchId: String,
|
||||
) : ParamsInterceptor {
|
||||
|
||||
override fun id(): String = this::class.java.simpleName
|
||||
|
||||
override fun canBeAppliedTo(event: AnalyticsEvent): Boolean = true
|
||||
|
||||
override fun intercept(params: MutableMap<String, String>) {
|
||||
params[AnalyticsParam.BatchId] = batchId
|
||||
}
|
||||
}
|
||||
|
|
@ -13,8 +13,8 @@ import com.tangem.tap.common.analytics.Analytics
|
|||
import com.tangem.tap.common.analytics.AnalyticsEventAnOld
|
||||
import com.tangem.tap.common.analytics.AnalyticsParamAnOld
|
||||
import com.tangem.tap.common.analytics.GetCardSourceParamsAnOld
|
||||
import com.tangem.tap.common.analytics.events.AnalyticsParam
|
||||
import com.tangem.tap.common.analytics.events.IntroductionProcess
|
||||
import com.tangem.tap.common.analytics.paramsInterceptor.BatchIdParamsInterceptor
|
||||
import com.tangem.tap.common.entities.IndeterminateProgressButton
|
||||
import com.tangem.tap.common.extensions.dispatchDialogShow
|
||||
import com.tangem.tap.common.extensions.dispatchOpenUrl
|
||||
|
|
@ -165,7 +165,7 @@ private fun onScanSuccess(scanResponse: ScanResponse) {
|
|||
val tapWalletManager = globalState.tapWalletManager
|
||||
tapWalletManager.updateConfigManager(scanResponse)
|
||||
|
||||
Analytics.attachToAllEvents(AnalyticsParam.BatchId, scanResponse.card.batchId)
|
||||
Analytics.addParamsInterceptor(BatchIdParamsInterceptor(scanResponse.card.batchId))
|
||||
|
||||
store.dispatch(TwinCardsAction.IfTwinsPrepareState(scanResponse))
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue