From b2fbe74220ec3fd0eb3ddc33e01d36da4d4740dd Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 23 Dec 2025 14:01:21 +0300 Subject: [PATCH] Updated on 2026-08-14 --- .../java/com/tangem/tap/TangemApplication.kt | 2 + .../amplitude/AmplitudeAnalyticsHandler.kt | 6 +-- .../appsflyer/AppsFlyerAnalyticsClient.kt | 19 ++++++++- .../appsflyer/AppsFlyerAnalyticsHandler.kt | 39 +++++++++++++------ .../handlers/appsflyer/AppsFlyerLogClient.kt | 8 ++++ .../firebase/FirebaseAnalyticsHandler.kt | 6 +-- .../handlers/firebase/FirebaseClient.kt | 2 +- ...t => UnderscoreAnalyticsEventConverter.kt} | 2 +- .../analytics/models/AppsFlyerOnlyEvent.kt | 15 +++++++ .../models/event/OnboardingAnalyticsEvent.kt | 12 +++++- .../core/analytics/api/EventHandlerApi.kt | 6 +-- .../analytics/filter/AppsFlyerEventFilter.kt | 23 +++++++++++ .../CreateMobileWalletModel.kt | 5 +-- .../v2/common/analytics/OnboardingEvent.kt | 8 +++- 14 files changed, 121 insertions(+), 32 deletions(-) rename app/src/main/java/com/tangem/tap/common/analytics/handlers/firebase/{FirebaseAnalyticsEventConverter.kt => UnderscoreAnalyticsEventConverter.kt} (95%) create mode 100644 core/analytics/models/src/main/java/com/tangem/core/analytics/models/AppsFlyerOnlyEvent.kt create mode 100644 core/analytics/src/main/java/com/tangem/core/analytics/filter/AppsFlyerEventFilter.kt diff --git a/app/src/main/java/com/tangem/tap/TangemApplication.kt b/app/src/main/java/com/tangem/tap/TangemApplication.kt index 2815df1738..abbd2d80de 100644 --- a/app/src/main/java/com/tangem/tap/TangemApplication.kt +++ b/app/src/main/java/com/tangem/tap/TangemApplication.kt @@ -19,6 +19,7 @@ import com.tangem.common.routing.AppRouter import com.tangem.core.abtests.manager.ABTestsManager import com.tangem.core.analytics.Analytics import com.tangem.core.analytics.api.ParamsInterceptor +import com.tangem.core.analytics.filter.AppsFlyerEventFilter import com.tangem.core.analytics.filter.OneTimeEventFilter import com.tangem.core.analytics.models.AnalyticsEvent import com.tangem.core.analytics.models.AnalyticsParam @@ -423,6 +424,7 @@ open class TangemApplication : Application(), ImageLoaderFactory, Configuration. factory.addHandlerBuilder(AppsFlyerAnalyticsHandler.Builder()) factory.addFilter(oneTimeEventFilter) + factory.addFilter(AppsFlyerEventFilter()) val buildData = AnalyticsHandlerBuilder.Data( application = application, diff --git a/app/src/main/java/com/tangem/tap/common/analytics/handlers/amplitude/AmplitudeAnalyticsHandler.kt b/app/src/main/java/com/tangem/tap/common/analytics/handlers/amplitude/AmplitudeAnalyticsHandler.kt index 1fdb08a185..a9a25c5acc 100644 --- a/app/src/main/java/com/tangem/tap/common/analytics/handlers/amplitude/AmplitudeAnalyticsHandler.kt +++ b/app/src/main/java/com/tangem/tap/common/analytics/handlers/amplitude/AmplitudeAnalyticsHandler.kt @@ -2,6 +2,7 @@ package com.tangem.tap.common.analytics.handlers.amplitude import com.tangem.core.analytics.api.AnalyticsHandler import com.tangem.core.analytics.api.AnalyticsUserIdHandler +import com.tangem.core.analytics.models.AnalyticsEvent import com.tangem.tap.common.analytics.api.AnalyticsHandlerBuilder class AmplitudeAnalyticsHandler( @@ -9,7 +10,6 @@ class AmplitudeAnalyticsHandler( ) : AnalyticsHandler, AnalyticsUserIdHandler { override fun id(): String = ID - override fun setUserId(userId: String) { client.setUserId(userId) } @@ -18,8 +18,8 @@ class AmplitudeAnalyticsHandler( client.clearUserId() } - override fun send(eventId: String, params: Map) { - client.logEvent(eventId, params) + override fun send(event: AnalyticsEvent) { + client.logEvent(event.id, event.params) } companion object { diff --git a/app/src/main/java/com/tangem/tap/common/analytics/handlers/appsflyer/AppsFlyerAnalyticsClient.kt b/app/src/main/java/com/tangem/tap/common/analytics/handlers/appsflyer/AppsFlyerAnalyticsClient.kt index aa2b3b40db..755f393182 100644 --- a/app/src/main/java/com/tangem/tap/common/analytics/handlers/appsflyer/AppsFlyerAnalyticsClient.kt +++ b/app/src/main/java/com/tangem/tap/common/analytics/handlers/appsflyer/AppsFlyerAnalyticsClient.kt @@ -3,8 +3,10 @@ package com.tangem.tap.common.analytics.handlers.appsflyer import android.content.Context import com.appsflyer.AppsFlyerLib import com.tangem.core.analytics.api.EventLogger +import com.tangem.core.analytics.api.UserIdHolder +import com.tangem.tap.common.analytics.handlers.firebase.UnderscoreAnalyticsEventConverter -interface AppsFlyerAnalyticsClient : EventLogger +interface AppsFlyerAnalyticsClient : EventLogger, UserIdHolder internal class AppsFlyerClient( private val context: Context, @@ -13,6 +15,7 @@ internal class AppsFlyerClient( ) : AppsFlyerAnalyticsClient { private val appsFlyerLib: AppsFlyerLib = AppsFlyerLib.getInstance() + private val eventConverter = UnderscoreAnalyticsEventConverter() init { appsFlyerLib.init(key, null, context) @@ -20,7 +23,19 @@ internal class AppsFlyerClient( appsFlyerLib.start(context) } + override fun setUserId(userId: String) { + appsFlyerLib.setCustomerUserId(userId) + } + + override fun clearUserId() { + appsFlyerLib.setCustomerUserId(null) + } + override fun logEvent(event: String, params: Map) { - appsFlyerLib.logEvent(context, event, params) + appsFlyerLib.logEvent( + context, + event, + eventConverter.convertEventParams(params), + ) } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/analytics/handlers/appsflyer/AppsFlyerAnalyticsHandler.kt b/app/src/main/java/com/tangem/tap/common/analytics/handlers/appsflyer/AppsFlyerAnalyticsHandler.kt index bd7036b570..d9f80b3476 100644 --- a/app/src/main/java/com/tangem/tap/common/analytics/handlers/appsflyer/AppsFlyerAnalyticsHandler.kt +++ b/app/src/main/java/com/tangem/tap/common/analytics/handlers/appsflyer/AppsFlyerAnalyticsHandler.kt @@ -1,21 +1,38 @@ package com.tangem.tap.common.analytics.handlers.appsflyer import com.tangem.core.analytics.api.AnalyticsHandler +import com.tangem.core.analytics.api.AnalyticsUserIdHandler import com.tangem.core.analytics.models.AnalyticsEvent +import com.tangem.core.analytics.models.AppsFlyerIncludedEvent +import com.tangem.core.analytics.models.AppsFlyerOnlyEvent import com.tangem.tap.common.analytics.api.AnalyticsHandlerBuilder class AppsFlyerAnalyticsHandler( private val client: AppsFlyerAnalyticsClient, -) : AnalyticsHandler { +) : AnalyticsHandler, AnalyticsUserIdHandler { override fun id(): String = ID - override fun send(eventId: String, params: Map) { - client.logEvent(eventId, params) + override fun send(event: AnalyticsEvent) { + when (event) { + is AppsFlyerOnlyEvent -> { + client.logEvent(event.id, event.params) + } + is AppsFlyerIncludedEvent -> { + client.logEvent( + event = AnalyticsEvent(category = event.category, event = event.appsFlyerReplacedEvent).id, + params = event.params, + ) + } + } } - override fun send(event: AnalyticsEvent) { - super.send(event) + override fun setUserId(userId: String) { + client.setUserId(userId) + } + + override fun clearUserId() { + client.clearUserId() } companion object { @@ -23,12 +40,10 @@ class AppsFlyerAnalyticsHandler( } class Builder : AnalyticsHandlerBuilder { - override fun build(data: AnalyticsHandlerBuilder.Data): AnalyticsHandler? = null - // disabled for now until analytics strategy is defined - // when { - // !data.isDebug -> AppsFlyerClient(data.application, data.config.appsFlyerApiKey, data.config.appsAppId) - // data.isDebug && data.logConfig.appsflyer -> AppsFlyerLogClient(data.jsonConverter) - // else -> null - // }?.let { AppsFlyerAnalyticsHandler(it) } + override fun build(data: AnalyticsHandlerBuilder.Data): AnalyticsHandler? = when { + !data.isDebug -> AppsFlyerClient(data.application, data.config.appsFlyerApiKey, data.config.appsAppId) + data.isDebug && data.logConfig.isAppsflyerLogEnabled -> AppsFlyerLogClient(data.jsonConverter) + else -> null + }?.let { AppsFlyerAnalyticsHandler(it) } } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/analytics/handlers/appsflyer/AppsFlyerLogClient.kt b/app/src/main/java/com/tangem/tap/common/analytics/handlers/appsflyer/AppsFlyerLogClient.kt index 36284bd8df..a953e00897 100644 --- a/app/src/main/java/com/tangem/tap/common/analytics/handlers/appsflyer/AppsFlyerLogClient.kt +++ b/app/src/main/java/com/tangem/tap/common/analytics/handlers/appsflyer/AppsFlyerLogClient.kt @@ -12,4 +12,12 @@ internal class AppsFlyerLogClient( override fun logEvent(event: String, params: Map) { logger.logEvent(event, params) } + + override fun setUserId(userId: String) { + // No-op + } + + override fun clearUserId() { + // No-op + } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/analytics/handlers/firebase/FirebaseAnalyticsHandler.kt b/app/src/main/java/com/tangem/tap/common/analytics/handlers/firebase/FirebaseAnalyticsHandler.kt index 7e7106ebb4..4d662a7828 100644 --- a/app/src/main/java/com/tangem/tap/common/analytics/handlers/firebase/FirebaseAnalyticsHandler.kt +++ b/app/src/main/java/com/tangem/tap/common/analytics/handlers/firebase/FirebaseAnalyticsHandler.kt @@ -1,8 +1,8 @@ package com.tangem.tap.common.analytics.handlers.firebase import com.tangem.core.analytics.api.AnalyticsErrorHandler -import com.tangem.core.analytics.api.AnalyticsHandler import com.tangem.core.analytics.api.AnalyticsExceptionHandler +import com.tangem.core.analytics.api.AnalyticsHandler import com.tangem.core.analytics.api.AnalyticsUserIdHandler import com.tangem.core.analytics.models.AnalyticsEvent import com.tangem.core.analytics.models.ExceptionAnalyticsEvent @@ -25,8 +25,8 @@ class FirebaseAnalyticsHandler( client.clearUserId() } - override fun send(eventId: String, params: Map) { - client.logEvent(eventId, params) + override fun send(event: AnalyticsEvent) { + client.logEvent(event.id, event.params) } override fun sendException(event: ExceptionAnalyticsEvent) { diff --git a/app/src/main/java/com/tangem/tap/common/analytics/handlers/firebase/FirebaseClient.kt b/app/src/main/java/com/tangem/tap/common/analytics/handlers/firebase/FirebaseClient.kt index 6268ebf230..d982f6e7ae 100644 --- a/app/src/main/java/com/tangem/tap/common/analytics/handlers/firebase/FirebaseClient.kt +++ b/app/src/main/java/com/tangem/tap/common/analytics/handlers/firebase/FirebaseClient.kt @@ -20,7 +20,7 @@ internal class FirebaseClient : FirebaseAnalyticsClient { private val fbAnalytics = Firebase.analytics private val fbCrashlytics = Firebase.crashlytics - private val eventConverter = FirebaseAnalyticsEventConverter() + private val eventConverter = UnderscoreAnalyticsEventConverter() override fun setUserId(userId: String) { Firebase.analytics.setUserId(userId) diff --git a/app/src/main/java/com/tangem/tap/common/analytics/handlers/firebase/FirebaseAnalyticsEventConverter.kt b/app/src/main/java/com/tangem/tap/common/analytics/handlers/firebase/UnderscoreAnalyticsEventConverter.kt similarity index 95% rename from app/src/main/java/com/tangem/tap/common/analytics/handlers/firebase/FirebaseAnalyticsEventConverter.kt rename to app/src/main/java/com/tangem/tap/common/analytics/handlers/firebase/UnderscoreAnalyticsEventConverter.kt index 559fc3418b..8d4e3d1261 100644 --- a/app/src/main/java/com/tangem/tap/common/analytics/handlers/firebase/FirebaseAnalyticsEventConverter.kt +++ b/app/src/main/java/com/tangem/tap/common/analytics/handlers/firebase/UnderscoreAnalyticsEventConverter.kt @@ -1,6 +1,6 @@ package com.tangem.tap.common.analytics.handlers.firebase -internal class FirebaseAnalyticsEventConverter { +internal class UnderscoreAnalyticsEventConverter { fun convertEventName(event: String): String { return convertString(event, FIREBASE_EVENT_NAME_MAX_LENGTH) diff --git a/core/analytics/models/src/main/java/com/tangem/core/analytics/models/AppsFlyerOnlyEvent.kt b/core/analytics/models/src/main/java/com/tangem/core/analytics/models/AppsFlyerOnlyEvent.kt new file mode 100644 index 0000000000..a265fcfd9f --- /dev/null +++ b/core/analytics/models/src/main/java/com/tangem/core/analytics/models/AppsFlyerOnlyEvent.kt @@ -0,0 +1,15 @@ +package com.tangem.core.analytics.models + +/** + * Marker interface for AppsFlyer events + * Only events implementing this interface will be sent to AppsFlyer + */ +interface AppsFlyerOnlyEvent + +/** + * Marker interface for AppsFlyer included events + * Events implementing this interface will be sent to AppsFlyer along with other analytics handlers + */ +interface AppsFlyerIncludedEvent { + val appsFlyerReplacedEvent: String +} \ No newline at end of file diff --git a/core/analytics/models/src/main/java/com/tangem/core/analytics/models/event/OnboardingAnalyticsEvent.kt b/core/analytics/models/src/main/java/com/tangem/core/analytics/models/event/OnboardingAnalyticsEvent.kt index f4bc6d2ca5..c1cad4c1a7 100644 --- a/core/analytics/models/src/main/java/com/tangem/core/analytics/models/event/OnboardingAnalyticsEvent.kt +++ b/core/analytics/models/src/main/java/com/tangem/core/analytics/models/event/OnboardingAnalyticsEvent.kt @@ -2,6 +2,8 @@ package com.tangem.core.analytics.models.event import com.tangem.core.analytics.models.AnalyticsEvent import com.tangem.core.analytics.models.AnalyticsParam +import com.tangem.core.analytics.models.AppsFlyerIncludedEvent +import com.tangem.core.analytics.models.AppsFlyerOnlyEvent sealed class OnboardingAnalyticsEvent( category: String, @@ -14,6 +16,8 @@ sealed class OnboardingAnalyticsEvent( params: Map = mapOf(), ) : OnboardingAnalyticsEvent(category = "Onboarding", event = event, params = params) { + class AppsFlyerOnlyEntryScreenView : Onboarding(event = "wallet_entry_screen_view"), AppsFlyerOnlyEvent + class Started( source: String, ) : Onboarding( @@ -64,7 +68,12 @@ sealed class OnboardingAnalyticsEvent( put("Seed Phrase Length", seedPhraseLength.toString()) } }, - ) + ), AppsFlyerIncludedEvent { + override val appsFlyerReplacedEvent = when (creationType) { + WalletCreationType.NewSeed -> "wallet_created_successfully" + WalletCreationType.SeedImport -> "wallet_imported" + } + } sealed class WalletCreationType(val value: String) { data object NewSeed : WalletCreationType(value = "New Seed") @@ -85,6 +94,7 @@ sealed class OnboardingAnalyticsEvent( AnalyticsParam.SOURCE to source, ), ) + class ButtonImportWallet : SeedPhrase("Button - Import Wallet") class ImportSeedPhraseScreenOpened : SeedPhrase("Import Seed Phrase Screen Opened") class ButtonImport : SeedPhrase("Button - Import") diff --git a/core/analytics/src/main/java/com/tangem/core/analytics/api/EventHandlerApi.kt b/core/analytics/src/main/java/com/tangem/core/analytics/api/EventHandlerApi.kt index 08e59781a0..868a56d99c 100644 --- a/core/analytics/src/main/java/com/tangem/core/analytics/api/EventHandlerApi.kt +++ b/core/analytics/src/main/java/com/tangem/core/analytics/api/EventHandlerApi.kt @@ -27,11 +27,7 @@ interface AnalyticsHandler : AnalyticsEventHandler { fun id(): String - fun send(eventId: String, params: Map = emptyMap()) - - override fun send(event: AnalyticsEvent) { - send(event.id, event.params) - } + override fun send(event: AnalyticsEvent) } interface AnalyticsHandlerHolder { diff --git a/core/analytics/src/main/java/com/tangem/core/analytics/filter/AppsFlyerEventFilter.kt b/core/analytics/src/main/java/com/tangem/core/analytics/filter/AppsFlyerEventFilter.kt new file mode 100644 index 0000000000..d316469bb1 --- /dev/null +++ b/core/analytics/src/main/java/com/tangem/core/analytics/filter/AppsFlyerEventFilter.kt @@ -0,0 +1,23 @@ +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.AppsFlyerIncludedEvent +import com.tangem.core.analytics.models.AppsFlyerOnlyEvent + +class AppsFlyerEventFilter : AnalyticsEventFilter { + + override fun canBeAppliedTo(event: AnalyticsEvent): Boolean = + event is AppsFlyerOnlyEvent || event is AppsFlyerIncludedEvent + + override suspend fun canBeSent(event: AnalyticsEvent): Boolean = true + + override fun canBeConsumedByHandler(handler: AnalyticsHandler, event: AnalyticsEvent): Boolean { + return when (event) { + is AppsFlyerOnlyEvent -> handler.id() == "AppsFlyer" + is AppsFlyerIncludedEvent -> true + else -> false + } + } +} \ No newline at end of file diff --git a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/createmobilewallet/CreateMobileWalletModel.kt b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/createmobilewallet/CreateMobileWalletModel.kt index 0f188dfe43..1d9409b60b 100644 --- a/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/createmobilewallet/CreateMobileWalletModel.kt +++ b/features/hot-wallet/impl/src/main/kotlin/com/tangem/features/hotwallet/createmobilewallet/CreateMobileWalletModel.kt @@ -60,9 +60,8 @@ internal class CreateMobileWalletModel @Inject constructor( init { trackingContextProxy.addHotWalletContext() - analyticsEventHandler.send( - event = OnboardingAnalyticsEvent.Onboarding.Started(source = params.source), - ) + analyticsEventHandler.send(event = OnboardingAnalyticsEvent.Onboarding.Started(source = params.source)) + analyticsEventHandler.send(event = OnboardingAnalyticsEvent.Onboarding.AppsFlyerOnlyEntryScreenView()) analyticsEventHandler.send( event = OnboardingAnalyticsEvent.SeedPhrase.CreateMobileScreenOpened(source = params.source), ) diff --git a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/common/analytics/OnboardingEvent.kt b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/common/analytics/OnboardingEvent.kt index 1aa8695bab..101eb5ff04 100644 --- a/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/common/analytics/OnboardingEvent.kt +++ b/features/onboarding-v2/impl/src/main/kotlin/com/tangem/features/onboarding/v2/common/analytics/OnboardingEvent.kt @@ -2,6 +2,7 @@ package com.tangem.features.onboarding.v2.common.analytics import com.tangem.core.analytics.models.AnalyticsEvent import com.tangem.core.analytics.models.AnalyticsParam +import com.tangem.core.analytics.models.AppsFlyerIncludedEvent sealed class OnboardingEvent( category: String, @@ -33,7 +34,12 @@ sealed class OnboardingEvent( put("Seed Phrase Length", seedPhraseLength.toString()) } }, - ) + ), AppsFlyerIncludedEvent { + override val appsFlyerReplacedEvent = when (creationType) { + WalletCreationType.NewSeed, WalletCreationType.PrivateKey -> "wallet_created_successfully" + WalletCreationType.SeedImport -> "wallet_imported" + } + } sealed class WalletCreationType(val value: String) { data object PrivateKey : WalletCreationType(value = "Private Key")