Updated on 2026-08-14
This commit is contained in:
parent
94703ae459
commit
34ffa9a1e7
20 changed files with 158 additions and 21 deletions
|
|
@ -74,6 +74,7 @@ dependencies {
|
|||
implementation(projects.data.tokens)
|
||||
implementation(projects.data.txhistory)
|
||||
implementation(projects.data.wallets)
|
||||
implementation(projects.data.analytics)
|
||||
|
||||
/** Features */
|
||||
implementation(projects.features.onboarding)
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import com.tangem.blockchain.common.BlockchainSdkConfig
|
|||
import com.tangem.blockchain.common.WalletManagerFactory
|
||||
import com.tangem.blockchain.network.BlockchainSdkRetrofitBuilder
|
||||
import com.tangem.core.analytics.Analytics
|
||||
import com.tangem.core.analytics.filter.OneTimeEventFilter
|
||||
import com.tangem.core.featuretoggle.manager.FeatureTogglesManager
|
||||
import com.tangem.data.source.preferences.PreferencesDataSource
|
||||
import com.tangem.datasource.api.common.MoshiConverter
|
||||
|
|
@ -216,6 +217,9 @@ internal class TapApplication : Application(), ImageLoaderFactory {
|
|||
|
||||
@Inject
|
||||
lateinit var sendFeatureToggles: SendFeatureToggles
|
||||
|
||||
@Inject
|
||||
lateinit var oneTimeEventFilter: OneTimeEventFilter
|
||||
// endregion Injected
|
||||
|
||||
override fun onCreate() {
|
||||
|
|
@ -353,6 +357,8 @@ internal class TapApplication : Application(), ImageLoaderFactory {
|
|||
factory.addHandlerBuilder(AppsFlyerAnalyticsHandler.Builder())
|
||||
factory.addHandlerBuilder(FirebaseAnalyticsHandler.Builder())
|
||||
|
||||
factory.addFilter(oneTimeEventFilter)
|
||||
|
||||
val buildData = AnalyticsHandlerBuilder.Data(
|
||||
application = application,
|
||||
config = config,
|
||||
|
|
|
|||
|
|
@ -9,8 +9,8 @@ class AmplitudeAnalyticsHandler(
|
|||
|
||||
override fun id(): String = ID
|
||||
|
||||
override fun send(event: String, params: Map<String, String>) {
|
||||
client.logEvent(event, params)
|
||||
override fun send(eventId: String, params: Map<String, String>) {
|
||||
client.logEvent(eventId, params)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
|
|
|||
|
|
@ -12,8 +12,8 @@ class AppsFlyerAnalyticsHandler(
|
|||
|
||||
override fun id(): String = ID
|
||||
|
||||
override fun send(event: String, params: Map<String, String>) {
|
||||
client.logEvent(event, params)
|
||||
override fun send(eventId: String, params: Map<String, String>) {
|
||||
client.logEvent(eventId, params)
|
||||
}
|
||||
|
||||
override fun send(event: AnalyticsEvent) {
|
||||
|
|
|
|||
|
|
@ -14,8 +14,8 @@ class FirebaseAnalyticsHandler(
|
|||
|
||||
override fun id(): String = ID
|
||||
|
||||
override fun send(event: String, params: Map<String, String>) {
|
||||
client.logEvent(event, params)
|
||||
override fun send(eventId: String, params: Map<String, String>) {
|
||||
client.logEvent(eventId, params)
|
||||
}
|
||||
|
||||
override fun send(event: AnalyticsEvent) {
|
||||
|
|
|
|||
|
|
@ -1,19 +1,18 @@
|
|||
plugins {
|
||||
alias(deps.plugins.android.library)
|
||||
alias(deps.plugins.kotlin.android)
|
||||
alias(deps.plugins.kotlin.jvm)
|
||||
alias(deps.plugins.kotlin.kapt)
|
||||
alias(deps.plugins.hilt.android)
|
||||
id("configuration")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
||||
/** DI */
|
||||
implementation(deps.hilt.android)
|
||||
implementation(deps.hilt.core)
|
||||
kapt(deps.hilt.kapt)
|
||||
|
||||
/** Core shouldn't depends on core, but in case with utils and logging its necessary */
|
||||
implementation(projects.core.utils)
|
||||
|
||||
implementation(projects.core.analytics.models)
|
||||
|
||||
implementation(deps.kotlin.coroutines)
|
||||
}
|
||||
|
|
@ -8,4 +8,7 @@ open class AnalyticsEvent(
|
|||
val event: String,
|
||||
var params: Map<String, String> = mapOf(),
|
||||
val error: Throwable? = null,
|
||||
)
|
||||
) {
|
||||
|
||||
val id: String = "[$category] $event"
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
package com.tangem.core.analytics.models
|
||||
|
||||
interface OneTimeAnalyticsEvent {
|
||||
|
||||
val oneTimeEventId: String
|
||||
}
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest package="com.tangem.core.analytics" />
|
||||
|
|
@ -16,7 +16,7 @@ interface AnalyticsEventFilter {
|
|||
* An internal filter check that, on external or internal conditions, recognizes the possibility of
|
||||
* sending an event.
|
||||
*/
|
||||
fun canBeSent(event: AnalyticsEvent): Boolean
|
||||
suspend fun canBeSent(event: AnalyticsEvent): Boolean
|
||||
|
||||
/**
|
||||
* Performs a check to see if the event can be dispatched by a specific handler
|
||||
|
|
|
|||
|
|
@ -12,13 +12,11 @@ interface AnalyticsEventHandler {
|
|||
interface AnalyticsHandler : AnalyticsEventHandler {
|
||||
fun id(): String
|
||||
|
||||
fun send(event: String, params: Map<String, String> = emptyMap())
|
||||
fun send(eventId: String, params: Map<String, String> = emptyMap())
|
||||
|
||||
override fun send(event: AnalyticsEvent) {
|
||||
send(prepareEventString(event), event.params)
|
||||
send(event.id, event.params)
|
||||
}
|
||||
|
||||
fun prepareEventString(event: AnalyticsEvent): String = "[${event.category}] ${event.event}"
|
||||
}
|
||||
|
||||
interface ErrorEventHandler {
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@ package com.tangem.core.analytics.di
|
|||
|
||||
import com.tangem.core.analytics.Analytics
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.analytics.filter.OneTimeEventFilter
|
||||
import com.tangem.core.analytics.repository.AnalyticsRepository
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
|
|
@ -10,11 +12,16 @@ import javax.inject.Singleton
|
|||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
class AnalyticsModule {
|
||||
internal object AnalyticsModule {
|
||||
|
||||
@Singleton
|
||||
@Provides
|
||||
fun provideAnalyticsHandler(): AnalyticsEventHandler {
|
||||
return Analytics // todo replace after refactoring calling Analytics in whole project
|
||||
}
|
||||
|
||||
@Provides
|
||||
fun provideOneTimeEventFilter(analyticsRepository: AnalyticsRepository): OneTimeEventFilter {
|
||||
return OneTimeEventFilter(analyticsRepository)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
package com.tangem.core.analytics.filter
|
||||
|
||||
import com.tangem.core.analytics.api.AnalyticsEventFilter
|
||||
import com.tangem.core.analytics.api.AnalyticsHandler
|
||||
import com.tangem.core.analytics.models.AnalyticsEvent
|
||||
import com.tangem.core.analytics.models.OneTimeAnalyticsEvent
|
||||
import com.tangem.core.analytics.repository.AnalyticsRepository
|
||||
|
||||
class OneTimeEventFilter(
|
||||
private val analyticsRepository: AnalyticsRepository,
|
||||
) : AnalyticsEventFilter {
|
||||
|
||||
override fun canBeAppliedTo(event: AnalyticsEvent): Boolean = event is OneTimeAnalyticsEvent
|
||||
|
||||
override suspend fun canBeSent(event: AnalyticsEvent): Boolean {
|
||||
if (event !is OneTimeAnalyticsEvent) return true
|
||||
|
||||
val isSent = analyticsRepository.checkIsEventSent(event.oneTimeEventId)
|
||||
|
||||
if (!isSent) {
|
||||
analyticsRepository.setIsEventSent(event.oneTimeEventId)
|
||||
}
|
||||
|
||||
return !isSent
|
||||
}
|
||||
|
||||
override fun canBeConsumedByHandler(handler: AnalyticsHandler, event: AnalyticsEvent): Boolean {
|
||||
return canBeAppliedTo(event)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
package com.tangem.core.analytics.repository
|
||||
|
||||
interface AnalyticsRepository {
|
||||
|
||||
suspend fun checkIsEventSent(eventId: String): Boolean
|
||||
|
||||
suspend fun setIsEventSent(eventId: String)
|
||||
}
|
||||
|
|
@ -34,6 +34,8 @@ object PreferencesKeys {
|
|||
val BALANCE_HIDING_SETTINGS_KEY by lazy { stringPreferencesKey(name = "balanceHidingSettings") }
|
||||
|
||||
val WALLETS_SCROLL_PREVIEW_KEY by lazy { booleanPreferencesKey(name = "walletsScrollPreview") }
|
||||
|
||||
val SENT_ONE_TIME_EVENTS_KEY by lazy { stringPreferencesKey(name = "sentOneTimeEvents") }
|
||||
}
|
||||
|
||||
/** Preferences keys set that should be migrated from "PreferencesDataSource" to a new DataStore<Preferences> */
|
||||
|
|
|
|||
1
data/analytics/.gitignore
vendored
Normal file
1
data/analytics/.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
/buld
|
||||
32
data/analytics/build.gradle.kts
Normal file
32
data/analytics/build.gradle.kts
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
plugins {
|
||||
alias(deps.plugins.android.library)
|
||||
alias(deps.plugins.kotlin.android)
|
||||
alias(deps.plugins.kotlin.kapt)
|
||||
id("configuration")
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "com.tangem.data.analytics"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
||||
/** Project - Analytics */
|
||||
implementation(projects.core.analytics)
|
||||
implementation(projects.core.analytics.models)
|
||||
|
||||
/** Project - Data */
|
||||
implementation(projects.core.datasource)
|
||||
implementation(projects.data.common)
|
||||
|
||||
/** AndroidX */
|
||||
implementation(deps.androidx.datastore)
|
||||
|
||||
/** DI */
|
||||
implementation(deps.hilt.core)
|
||||
kapt(deps.hilt.kapt)
|
||||
|
||||
/** Other */
|
||||
implementation(deps.kotlin.coroutines)
|
||||
implementation(deps.timber)
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
package com.tangem.data.analytics
|
||||
|
||||
import com.tangem.core.analytics.repository.AnalyticsRepository
|
||||
import com.tangem.datasource.local.preferences.AppPreferencesStore
|
||||
import com.tangem.datasource.local.preferences.PreferencesKeys
|
||||
import com.tangem.datasource.local.preferences.utils.getObjectListSync
|
||||
|
||||
internal class DefaultAnalyticsRepository(
|
||||
private val appPreferencesStore: AppPreferencesStore,
|
||||
) : AnalyticsRepository {
|
||||
|
||||
override suspend fun checkIsEventSent(eventId: String): Boolean {
|
||||
val sentEvents = appPreferencesStore
|
||||
.getObjectListSync<String>(PreferencesKeys.SENT_ONE_TIME_EVENTS_KEY)
|
||||
|
||||
return eventId in sentEvents
|
||||
}
|
||||
|
||||
override suspend fun setIsEventSent(eventId: String) {
|
||||
appPreferencesStore.editData { mutablePreferences ->
|
||||
val sentEvents = mutablePreferences.getObject<List<String>>(PreferencesKeys.SENT_ONE_TIME_EVENTS_KEY)
|
||||
val updatedSentEvents = sentEvents.orEmpty() + eventId
|
||||
|
||||
mutablePreferences.setObject(PreferencesKeys.SENT_ONE_TIME_EVENTS_KEY, updatedSentEvents)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
package com.tangem.data.analytics.di
|
||||
|
||||
import com.tangem.core.analytics.repository.AnalyticsRepository
|
||||
import com.tangem.data.analytics.DefaultAnalyticsRepository
|
||||
import com.tangem.datasource.local.preferences.AppPreferencesStore
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
internal object AnalyticsDataModule {
|
||||
|
||||
@Provides
|
||||
fun provideAnalyticsRepository(appPreferencesStore: AppPreferencesStore): AnalyticsRepository {
|
||||
return DefaultAnalyticsRepository(appPreferencesStore)
|
||||
}
|
||||
}
|
||||
|
|
@ -128,7 +128,6 @@ include(":domain:app-theme:models")
|
|||
include(":domain:balance-hiding")
|
||||
include(":domain:balance-hiding:models")
|
||||
include(":domain:transaction")
|
||||
|
||||
// endregion Domain modules
|
||||
|
||||
// region Data modules
|
||||
|
|
@ -142,4 +141,5 @@ include(":data:source:preferences")
|
|||
include(":data:settings")
|
||||
include(":data:txhistory")
|
||||
include(":data:wallets")
|
||||
include(":data:analytics")
|
||||
// endregion Data modules
|
||||
Loading…
Add table
Add a link
Reference in a new issue