Updated on 2026-08-14
This commit is contained in:
parent
7794446857
commit
5d448b9500
87 changed files with 223 additions and 121 deletions
2
core/analytics/src/main/AndroidManifest.xml
Normal file
2
core/analytics/src/main/AndroidManifest.xml
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest package="com.tangem.core.analytics" />
|
||||
|
|
@ -0,0 +1,92 @@
|
|||
package com.tangem.core.analytics
|
||||
|
||||
import com.tangem.core.analytics.api.AnalyticsEventFilter
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.analytics.api.AnalyticsFilterHolder
|
||||
import com.tangem.core.analytics.api.AnalyticsHandler
|
||||
import com.tangem.core.analytics.api.AnalyticsHandlerHolder
|
||||
import com.tangem.core.analytics.api.ParamsInterceptor
|
||||
import com.tangem.core.analytics.api.ParamsInterceptorHolder
|
||||
import com.tangem.utils.coroutines.FeatureCoroutineExceptionHandler
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
package com.tangem.core.analytics
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
open class AnalyticsEvent(
|
||||
val category: String,
|
||||
val event: String,
|
||||
var params: Map<String, String> = mapOf(),
|
||||
val error: Throwable? = null,
|
||||
var filterData: Any? = null,
|
||||
)
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
package com.tangem.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(),
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
package com.tangem.core.analytics.api
|
||||
|
||||
import com.tangem.core.analytics.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
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
package com.tangem.core.analytics.api
|
||||
|
||||
import com.tangem.core.analytics.AnalyticsEvent
|
||||
|
||||
/**
|
||||
[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?
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
package com.tangem.core.analytics.api
|
||||
|
||||
import com.tangem.core.analytics.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?
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
package com.tangem.core.analytics.di
|
||||
|
||||
import com.tangem.core.analytics.Analytics
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
class AnalyticsModule {
|
||||
|
||||
@Singleton
|
||||
@Provides
|
||||
fun provideAnalyticsHandler(): AnalyticsEventHandler {
|
||||
return Analytics //todo replace after refactoring calling Analytics in whole project
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue