Updated on 2026-08-14

This commit is contained in:
Tangem 2025-12-23 14:01:21 +03:00
parent e904e54848
commit b2fbe74220
14 changed files with 121 additions and 32 deletions

View file

@ -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,

View file

@ -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<String, String>) {
client.logEvent(eventId, params)
override fun send(event: AnalyticsEvent) {
client.logEvent(event.id, event.params)
}
companion object {

View file

@ -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<String, String>) {
appsFlyerLib.logEvent(context, event, params)
appsFlyerLib.logEvent(
context,
event,
eventConverter.convertEventParams(params),
)
}
}

View file

@ -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<String, String>) {
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) }
}
}

View file

@ -12,4 +12,12 @@ internal class AppsFlyerLogClient(
override fun logEvent(event: String, params: Map<String, String>) {
logger.logEvent(event, params)
}
override fun setUserId(userId: String) {
// No-op
}
override fun clearUserId() {
// No-op
}
}

View file

@ -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<String, String>) {
client.logEvent(eventId, params)
override fun send(event: AnalyticsEvent) {
client.logEvent(event.id, event.params)
}
override fun sendException(event: ExceptionAnalyticsEvent) {

View file

@ -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)

View file

@ -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)

View file

@ -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
}

View file

@ -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<String, String> = 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")

View file

@ -27,11 +27,7 @@ interface AnalyticsHandler : AnalyticsEventHandler {
fun id(): String
fun send(eventId: String, params: Map<String, String> = emptyMap())
override fun send(event: AnalyticsEvent) {
send(event.id, event.params)
}
override fun send(event: AnalyticsEvent)
}
interface AnalyticsHandlerHolder {

View file

@ -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
}
}
}

View file

@ -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),
)

View file

@ -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")