Updated on 2026-08-14

This commit is contained in:
Tangem 2023-01-10 11:42:49 +03:00
parent 7794446857
commit 5d448b9500
87 changed files with 223 additions and 121 deletions

View file

@ -1,93 +0,0 @@
package com.tangem.tap.common.analytics
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.AnalyticsHandler
import com.tangem.tap.common.analytics.api.AnalyticsHandlerHolder
import com.tangem.tap.common.analytics.api.ParamsInterceptor
import com.tangem.tap.common.analytics.api.ParamsInterceptorHolder
import com.tangem.tap.common.analytics.events.AnalyticsEvent
import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.asCoroutineDispatcher
import kotlinx.coroutines.launch
import java.util.concurrent.Executors
/**
[REDACTED_AUTHOR]
*/
interface GlobalAnalyticsEventHandler : AnalyticsEventHandler,
AnalyticsHandlerHolder,
AnalyticsFilterHolder,
ParamsInterceptorHolder
object Analytics : GlobalAnalyticsEventHandler {
private val analyticsScope: CoroutineScope by lazy { createScope() }
private val handlers = mutableMapOf<String, AnalyticsHandler>()
private val paramsInterceptors = mutableMapOf<String, ParamsInterceptor>()
private val analyticsFilters = mutableSetOf<AnalyticsEventFilter>()
private val analyticsHandlers: List<AnalyticsHandler>
get() = handlers.values.toList()
override fun addHandler(name: String, handler: AnalyticsHandler) {
handlers[name] = handler
}
override fun removeHandler(name: String): AnalyticsHandler? {
return handlers.remove(name)
}
override fun addFilter(filter: AnalyticsEventFilter) {
analyticsFilters.add(filter)
}
override fun removeFilter(filter: AnalyticsEventFilter): Boolean {
return analyticsFilters.remove(filter)
}
override fun addParamsInterceptor(interceptor: ParamsInterceptor) {
paramsInterceptors[interceptor.id()] = interceptor
}
override fun removeParamsInterceptor(interceptor: ParamsInterceptor): ParamsInterceptor? {
return paramsInterceptors.remove(interceptor.id())
}
override fun send(event: AnalyticsEvent) {
analyticsScope.launch {
event.params = applyParamsInterceptors(event)
val eventFilter = analyticsFilters.firstOrNull { it.canBeAppliedTo(event) }
when {
eventFilter == null -> analyticsHandlers.forEach { handler -> handler.send(event) }
eventFilter.canBeSent(event) -> {
analyticsHandlers
.filter { handler -> eventFilter.canBeConsumedByHandler(handler, event) }
.forEach { handler -> handler.send(event) }
}
}
}
}
private fun applyParamsInterceptors(event: AnalyticsEvent): MutableMap<String, String> {
val interceptedParams = event.params.toMutableMap()
paramsInterceptors.values
.filter { it.canBeAppliedTo(event) }
.forEach { it.intercept(interceptedParams) }
return interceptedParams
}
private fun createScope(): CoroutineScope {
val name = "Analytics"
val dispatcher = Executors.newFixedThreadPool(1).asCoroutineDispatcher()
val exHandler = FeatureCoroutineExceptionHandler.create(name)
return CoroutineScope(SupervisorJob() + dispatcher + CoroutineName(name) + exHandler)
}
}

View file

@ -1,8 +1,8 @@
package com.tangem.tap.common.analytics
import com.tangem.common.json.MoshiJsonConverter
import com.tangem.tap.common.analytics.api.ErrorEventLogger
import com.tangem.tap.common.analytics.api.EventLogger
import com.tangem.analytics.api.ErrorEventLogger
import com.tangem.analytics.api.EventLogger
import timber.log.Timber
class AnalyticsEventsLogger(

View file

@ -1,8 +1,9 @@
package com.tangem.tap.common.analytics
import com.tangem.tap.common.analytics.api.AnalyticsEventFilter
import com.tangem.core.analytics.Analytics
import com.tangem.core.analytics.api.AnalyticsEventFilter
import com.tangem.core.analytics.api.ParamsInterceptor
import com.tangem.tap.common.analytics.api.AnalyticsHandlerBuilder
import com.tangem.tap.common.analytics.api.ParamsInterceptor
/**
[REDACTED_AUTHOR]

View file

@ -0,0 +1,19 @@
package com.tangem.tap.common.analytics.api
import android.app.Application
import com.tangem.common.json.MoshiJsonConverter
import com.tangem.core.analytics.api.AnalyticsHandler
import com.tangem.domain.common.AnalyticsHandlersLogConfig
import com.tangem.tap.domain.configurable.config.Config
interface AnalyticsHandlerBuilder {
fun build(data: Data): AnalyticsHandler?
data class Data(
val application: Application,
val config: Config,
val isDebug: Boolean,
val logConfig: AnalyticsHandlersLogConfig,
val jsonConverter: MoshiJsonConverter,
)
}

View file

@ -1,18 +0,0 @@
package com.tangem.tap.common.analytics.api
/**
[REDACTED_AUTHOR]
*/
interface EventLogger {
fun logEvent(
event: String,
params: Map<String, String> = emptyMap(),
)
}
interface ErrorEventLogger {
fun logErrorEvent(
error: Throwable,
params: Map<String, String> = emptyMap(),
)
}

View file

@ -1,30 +0,0 @@
package com.tangem.tap.common.analytics.api
import com.tangem.tap.common.analytics.events.AnalyticsEvent
/**
[REDACTED_AUTHOR]
* The filter is used to separate events to send and determine which handler it can be processed with.
*/
interface AnalyticsEventFilter {
/**
* Recognizes the event and checks if the given filter can be applied to the event.
*/
fun canBeAppliedTo(event: AnalyticsEvent): Boolean
/**
* An internal filter check that, on external or internal conditions, recognizes the possibility of
* sending an event.
*/
fun canBeSent(event: AnalyticsEvent): Boolean
/**
* Performs a check to see if the event can be dispatched by a specific handler
*/
fun canBeConsumedByHandler(handler: AnalyticsHandler, event: AnalyticsEvent): Boolean
}
interface AnalyticsFilterHolder {
fun addFilter(filter: AnalyticsEventFilter)
fun removeFilter(filter: AnalyticsEventFilter): Boolean
}

View file

@ -1,50 +0,0 @@
package com.tangem.tap.common.analytics.api
import android.app.Application
import com.tangem.common.json.MoshiJsonConverter
import com.tangem.domain.common.AnalyticsHandlersLogConfig
import com.tangem.tap.common.analytics.events.AnalyticsEvent
import com.tangem.tap.domain.configurable.config.Config
/**
[REDACTED_AUTHOR]
*/
interface AnalyticsEventHandler {
fun send(event: AnalyticsEvent)
}
interface AnalyticsHandler : AnalyticsEventHandler {
fun id(): String
fun send(event: String, params: Map<String, String> = emptyMap())
override fun send(event: AnalyticsEvent) {
send(prepareEventString(event), event.params)
}
fun prepareEventString(event: AnalyticsEvent): String = "[${event.category}] ${event.event}"
}
interface ErrorEventHandler {
fun send(
error: Throwable,
params: Map<String, String> = emptyMap(),
)
}
interface AnalyticsHandlerHolder {
fun addHandler(name: String, handler: AnalyticsHandler)
fun removeHandler(name: String): AnalyticsHandler?
}
interface AnalyticsHandlerBuilder {
fun build(data: Data): AnalyticsHandler?
data class Data(
val application: Application,
val config: Config,
val isDebug: Boolean,
val logConfig: AnalyticsHandlersLogConfig,
val jsonConverter: MoshiJsonConverter,
)
}

View file

@ -1,17 +0,0 @@
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?
}

View file

@ -1,12 +0,0 @@
package com.tangem.tap.common.analytics.events
/**
[REDACTED_AUTHOR]
*/
sealed class AnalyticsEvent(
val category: String,
val event: String,
var params: Map<String, String> = mapOf(),
val error: Throwable? = null,
var filterData: Any? = null,
)

View file

@ -1,5 +1,7 @@
package com.tangem.tap.common.analytics.events
import com.tangem.core.analytics.AnalyticsEvent
/**
[REDACTED_AUTHOR]
*/

View file

@ -1,5 +1,7 @@
package com.tangem.tap.common.analytics.events
import com.tangem.core.analytics.AnalyticsEvent
/**
[REDACTED_AUTHOR]
*/

View file

@ -1,5 +1,7 @@
package com.tangem.tap.common.analytics.events
import com.tangem.core.analytics.AnalyticsEvent
/**
[REDACTED_AUTHOR]
*/

View file

@ -1,5 +1,7 @@
package com.tangem.tap.common.analytics.events
import com.tangem.core.analytics.AnalyticsEvent
/**
[REDACTED_AUTHOR]
*/

View file

@ -1,5 +1,7 @@
package com.tangem.tap.common.analytics.events
import com.tangem.core.analytics.AnalyticsEvent
/**
[REDACTED_AUTHOR]
*/

View file

@ -1,5 +1,6 @@
package com.tangem.tap.common.analytics.events
import com.tangem.core.analytics.AnalyticsEvent
import com.tangem.domain.common.extensions.toNetworkId
import com.tangem.domain.features.addCustomToken.CustomCurrency
import com.tangem.tap.common.extensions.filterNotNull

View file

@ -1,5 +1,7 @@
package com.tangem.tap.common.analytics.events
import com.tangem.core.analytics.AnalyticsEvent
sealed class MyWallets(
event: String,
params: Map<String, String> = mapOf(),

View file

@ -1,5 +1,7 @@
package com.tangem.tap.common.analytics.events
import com.tangem.core.analytics.AnalyticsEvent
/**
[REDACTED_AUTHOR]
*/

View file

@ -1,5 +1,7 @@
package com.tangem.tap.common.analytics.events
import com.tangem.core.analytics.AnalyticsEvent
/**
[REDACTED_AUTHOR]
*/

View file

@ -1,5 +1,6 @@
package com.tangem.tap.common.analytics.events
import com.tangem.core.analytics.AnalyticsEvent
import com.tangem.tap.features.details.ui.details.SocialNetwork
/**

View file

@ -1,5 +1,6 @@
package com.tangem.tap.common.analytics.events
import com.tangem.core.analytics.AnalyticsEvent
import com.tangem.tap.common.extensions.filterNotNull
/**

View file

@ -1,5 +1,6 @@
package com.tangem.tap.common.analytics.events
import com.tangem.core.analytics.AnalyticsEvent
import com.tangem.tap.common.analytics.events.AnalyticsParam.CurrencyType
/**

View file

@ -1,5 +1,7 @@
package com.tangem.tap.common.analytics.events
import com.tangem.core.analytics.AnalyticsEvent
/**
[REDACTED_AUTHOR]
*/

View file

@ -1,8 +1,8 @@
package com.tangem.tap.common.analytics.filters
import com.tangem.tap.common.analytics.api.AnalyticsEventFilter
import com.tangem.tap.common.analytics.api.AnalyticsHandler
import com.tangem.tap.common.analytics.events.AnalyticsEvent
import com.tangem.core.analytics.api.AnalyticsEventFilter
import com.tangem.core.analytics.api.AnalyticsHandler
import com.tangem.core.analytics.AnalyticsEvent
import com.tangem.tap.common.analytics.events.Basic
/**

View file

@ -1,9 +1,9 @@
package com.tangem.tap.common.analytics.filters
import com.tangem.common.extensions.guard
import com.tangem.tap.common.analytics.api.AnalyticsEventFilter
import com.tangem.tap.common.analytics.api.AnalyticsHandler
import com.tangem.tap.common.analytics.events.AnalyticsEvent
import com.tangem.core.analytics.api.AnalyticsEventFilter
import com.tangem.core.analytics.api.AnalyticsHandler
import com.tangem.core.analytics.AnalyticsEvent
import com.tangem.tap.common.analytics.events.AnalyticsParam
import com.tangem.tap.common.analytics.events.Basic
import com.tangem.tap.persistence.ToppedUpWalletStorage

View file

@ -1,8 +1,8 @@
package com.tangem.tap.common.analytics.filters
import com.tangem.tap.common.analytics.api.AnalyticsEventFilter
import com.tangem.tap.common.analytics.api.AnalyticsHandler
import com.tangem.tap.common.analytics.events.AnalyticsEvent
import com.tangem.core.analytics.api.AnalyticsEventFilter
import com.tangem.core.analytics.api.AnalyticsHandler
import com.tangem.core.analytics.AnalyticsEvent
import com.tangem.tap.common.analytics.events.Shop
import com.tangem.tap.common.analytics.handlers.appsFlyer.AppsFlyerAnalyticsHandler
import com.tangem.tap.common.analytics.handlers.firebase.FirebaseAnalyticsHandler

View file

@ -1,6 +1,6 @@
package com.tangem.tap.common.analytics.handlers.amplitude
import com.tangem.tap.common.analytics.api.AnalyticsHandler
import com.tangem.core.analytics.api.AnalyticsHandler
import com.tangem.tap.common.analytics.api.AnalyticsHandlerBuilder
class AmplitudeAnalyticsHandler(

View file

@ -4,7 +4,7 @@ import android.app.Application
import com.amplitude.api.Amplitude
import com.amplitude.api.AmplitudeClient
import com.tangem.common.Converter
import com.tangem.tap.common.analytics.api.EventLogger
import com.tangem.analytics.api.EventLogger
import org.json.JSONObject
/**

View file

@ -1,6 +1,6 @@
package com.tangem.tap.common.analytics.handlers.appsFlyer
import com.tangem.tap.common.analytics.api.AnalyticsHandler
import com.tangem.core.analytics.api.AnalyticsHandler
import com.tangem.tap.common.analytics.api.AnalyticsHandlerBuilder
class AppsFlyerAnalyticsHandler(

View file

@ -2,7 +2,7 @@ package com.tangem.tap.common.analytics.handlers.appsFlyer
import android.content.Context
import com.appsflyer.AppsFlyerLib
import com.tangem.tap.common.analytics.api.EventLogger
import com.tangem.analytics.api.EventLogger
/**
[REDACTED_AUTHOR]

View file

@ -1,10 +1,10 @@
package com.tangem.tap.common.analytics.handlers.firebase
import com.tangem.tap.common.analytics.api.AnalyticsHandler
import com.tangem.core.analytics.AnalyticsEvent
import com.tangem.core.analytics.api.AnalyticsHandler
import com.tangem.core.analytics.api.ErrorEventHandler
import com.tangem.tap.common.analytics.api.AnalyticsHandlerBuilder
import com.tangem.tap.common.analytics.api.ErrorEventHandler
import com.tangem.tap.common.analytics.converters.AnalyticsErrorConverter
import com.tangem.tap.common.analytics.events.AnalyticsEvent
class FirebaseAnalyticsHandler(
private val client: FirebaseAnalyticsClient,
@ -17,18 +17,18 @@ class FirebaseAnalyticsHandler(
}
override fun send(event: AnalyticsEvent) {
when (event.error) {
null -> super.send(event)
else -> {
val errorConverter = AnalyticsErrorConverter()
if (!errorConverter.canBeHandled(event.error)) return
val error = event.error
if (error == null) {
super.send(event)
} else {
val errorConverter = AnalyticsErrorConverter()
if (!errorConverter.canBeHandled(error)) return
val errorParams = errorConverter.convert(event.error).toMutableMap()
errorParams["Category"] = event.category
errorParams["Event"] = event.event
errorParams.putAll(event.params)
send(event.error, errorParams)
}
val errorParams = errorConverter.convert(error).toMutableMap()
errorParams["Category"] = event.category
errorParams["Event"] = event.event
errorParams.putAll(event.params)
send(error, errorParams)
}
}

View file

@ -6,8 +6,8 @@ import com.google.firebase.analytics.FirebaseAnalytics
import com.google.firebase.analytics.ktx.analytics
import com.google.firebase.crashlytics.ktx.crashlytics
import com.google.firebase.ktx.Firebase
import com.tangem.tap.common.analytics.api.ErrorEventLogger
import com.tangem.tap.common.analytics.api.EventLogger
import com.tangem.analytics.api.ErrorEventLogger
import com.tangem.analytics.api.EventLogger
/**
[REDACTED_AUTHOR]

View file

@ -1,7 +1,7 @@
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.core.analytics.api.ParamsInterceptor
import com.tangem.core.analytics.AnalyticsEvent
import com.tangem.tap.common.analytics.events.AnalyticsParam
/**

View file

@ -4,7 +4,7 @@ import android.app.Application
import android.content.Intent
import com.google.android.gms.wallet.PaymentData
import com.shopify.buy3.Storefront
import com.tangem.tap.common.analytics.Analytics
import com.tangem.core.analytics.Analytics
import com.tangem.tap.common.analytics.converters.ShopOrderToEventConverter
import com.tangem.tap.common.extensions.filterNotNull
import com.tangem.tap.common.shop.data.ProductType