Updated on 2026-08-14

This commit is contained in:
Tangem 2025-11-12 13:27:24 +02:00
parent 0fe533f267
commit 7a7204c6c4
32 changed files with 386 additions and 120 deletions

View file

@ -5,7 +5,9 @@ import com.tangem.TangemSdkLogger
import com.tangem.blockchainsdk.BlockchainSDKFactory
import com.tangem.blockchainsdk.utils.ExcludedBlockchains
import com.tangem.common.routing.AppRouter
import com.tangem.core.abtests.manager.ABTestsManager
import com.tangem.core.analytics.filter.OneTimeEventFilter
import com.tangem.core.analytics.utils.TrackingContextProxy
import com.tangem.core.configtoggle.blockchain.ExcludedBlockchainsManager
import com.tangem.core.configtoggle.feature.FeatureTogglesManager
import com.tangem.core.decompose.di.GlobalUiMessageSender
@ -151,4 +153,8 @@ interface ApplicationEntryPoint {
fun getHotWalletFeatureToggles(): HotWalletFeatureToggles
fun getWcInitializeUseCase(): WcInitializeUseCase
fun getTrackingContextProxy(): TrackingContextProxy
fun getABTestsManager(): ABTestsManager
}

View file

@ -16,6 +16,7 @@ import com.tangem.blockchain.network.BlockchainSdkRetrofitBuilder
import com.tangem.blockchainsdk.BlockchainSDKFactory
import com.tangem.blockchainsdk.utils.ExcludedBlockchains
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.OneTimeEventFilter
@ -240,6 +241,12 @@ open class TangemApplication : Application(), ImageLoaderFactory, Configuration.
private val wcInitializeUseCase
get() = entryPoint.getWcInitializeUseCase()
private val trackingContextProxy
get() = entryPoint.getTrackingContextProxy()
private val abTestsManager: ABTestsManager
get() = entryPoint.getABTestsManager()
// endregion
private val appScope = MainScope()
@ -310,6 +317,8 @@ open class TangemApplication : Application(), ImageLoaderFactory, Configuration.
initWithConfigDependency(environmentConfig = environmentConfigStorage.initialize())
}
abTestsManager.init()
appScope.launch {
launch(Dispatchers.IO) {
loadNativeLibraries()
@ -385,6 +394,7 @@ open class TangemApplication : Application(), ImageLoaderFactory, Configuration.
userWalletsListRepository = userWalletsListRepository,
tangemHotSdk = tangemHotSdk,
hotWalletFeatureToggles = hotWalletFeatureToggles,
trackingContextProxy = trackingContextProxy,
),
),
)

View file

@ -1,46 +0,0 @@
package com.tangem.tap.common.analytics
import com.tangem.core.analytics.Analytics
import com.tangem.core.analytics.utils.AnalyticsContextProxy
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.tap.common.extensions.addContext
import com.tangem.tap.common.extensions.addHotWalletContext
import com.tangem.tap.common.extensions.eraseContext
import com.tangem.tap.common.extensions.removeContext
import com.tangem.tap.common.extensions.setContext
import com.tangem.tap.common.extensions.setHotWalletContext
/**
[REDACTED_AUTHOR]
*/
internal class DefaultAnalyticsContextProxy : AnalyticsContextProxy {
override fun setContext(scanResponse: ScanResponse) {
Analytics.setContext(scanResponse)
}
override fun addContext(userWallet: UserWallet) {
Analytics.addContext(userWallet)
}
override fun setHotWalletContext() {
Analytics.setHotWalletContext()
}
override fun eraseContext() {
Analytics.eraseContext()
}
override fun addContext(scanResponse: ScanResponse) {
Analytics.addContext(scanResponse)
}
override fun addHotWalletContext() {
Analytics.addHotWalletContext()
}
override fun removeContext() {
Analytics.removeContext()
}
}

View file

@ -1,13 +0,0 @@
package com.tangem.tap.common.analytics
import com.tangem.core.analytics.Analytics
import com.tangem.domain.analytics.ChangeCardAnalyticsContextUseCase
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.tap.common.extensions.setContext
internal class DefaultChangeCardAnalyticsContextUseCase : ChangeCardAnalyticsContextUseCase {
override fun invoke(scanResponse: ScanResponse) {
Analytics.setContext(scanResponse)
}
}

View file

@ -0,0 +1,98 @@
package com.tangem.tap.common.analytics
import com.tangem.core.abtests.manager.ABTestsManager
import com.tangem.core.analytics.Analytics
import com.tangem.core.analytics.utils.TrackingContextProxy
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.wallets.builder.UserWalletIdBuilder
import com.tangem.tap.common.extensions.addContext
import com.tangem.tap.common.extensions.addHotWalletContext
import com.tangem.tap.common.extensions.eraseContext
import com.tangem.tap.common.extensions.removeContext
import com.tangem.tap.common.extensions.setContext
import com.tangem.tap.common.extensions.setHotWalletContext
import com.tangem.common.extensions.calculateSha256
import com.tangem.common.extensions.toHexString
import com.tangem.domain.models.wallet.UserWalletId
/**
[REDACTED_AUTHOR]
*/
internal class DefaultTrackingContextProxy(private val abTestsManager: ABTestsManager) : TrackingContextProxy {
override fun setContext(scanResponse: ScanResponse) {
val userWalletId = UserWalletIdBuilder.scanResponse(scanResponse).build()
Analytics.setContext(userWalletId, scanResponse)
abTestsManager.setUserProperties(
userId = calculateUserIdHash(userWalletId),
batch = scanResponse.card.batchId,
productType = scanResponse.productType.name,
firmware = scanResponse.card.firmwareVersion.stringValue,
)
}
override fun setContext(userWallet: UserWallet) {
Analytics.setContext(userWallet)
when (userWallet) {
is UserWallet.Cold -> {
setColdWalletUserProperties(userWallet)
}
is UserWallet.Hot -> {
setHotWalletUserProperties(userWallet)
}
}
}
override fun addContext(userWallet: UserWallet) {
Analytics.addContext(userWallet)
}
override fun setHotWalletContext() {
Analytics.setHotWalletContext()
}
override fun eraseContext() {
Analytics.eraseContext()
abTestsManager.removeUserProperties()
}
override fun addContext(scanResponse: ScanResponse) {
Analytics.addContext(scanResponse)
}
override fun addHotWalletContext() {
Analytics.addHotWalletContext()
}
override fun removeContext() {
Analytics.removeContext()
}
private fun calculateUserIdHash(userWalletId: UserWalletId?): String? {
return userWalletId?.value
?.calculateSha256()
?.toHexString()
}
private fun setColdWalletUserProperties(userWallet: UserWallet.Cold) {
abTestsManager.setUserProperties(
userId = calculateUserIdHash(userWallet.walletId),
batch = userWallet.scanResponse.card.batchId,
productType = userWallet.scanResponse.productType.name,
firmware = userWallet.scanResponse.card.firmwareVersion.stringValue,
)
}
private fun setHotWalletUserProperties(userWallet: UserWallet.Hot) {
abTestsManager.setUserProperties(
userId = calculateUserIdHash(userWallet.walletId),
batch = null,
productType = "Mobile Wallet",
firmware = null,
)
}
}

View file

@ -3,6 +3,7 @@ package com.tangem.tap.common.extensions
import com.tangem.core.analytics.Analytics
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.wallets.builder.UserWalletIdBuilder
import com.tangem.tap.common.analytics.paramsInterceptor.HotWalletContextInterceptor
import com.tangem.tap.common.analytics.paramsInterceptor.LinkedCardContextInterceptor
@ -14,8 +15,7 @@ import com.tangem.tap.common.analytics.paramsInterceptor.LinkedCardContextInterc
/**
* Sets the new context
*/
fun Analytics.setContext(scanResponse: ScanResponse) {
val userWalletId = UserWalletIdBuilder.scanResponse(scanResponse).build()
fun Analytics.setContext(userWalletId: UserWalletId?, scanResponse: ScanResponse) {
if (userWalletId != null) {
setUserId(userWalletId.stringValue)
}
@ -63,7 +63,7 @@ fun Analytics.addContext(userWallet: UserWallet) {
is UserWallet.Hot -> HotWalletContextInterceptor(parent = currentContext)
}
setUserId(userWalletId = userWallet.walletId.stringValue)
setUserId(userId = userWallet.walletId.stringValue)
addParamsInterceptor(newContext)
}

View file

@ -1,10 +1,9 @@
package com.tangem.tap.di.analytics
import com.tangem.core.abtests.manager.ABTestsManager
import com.tangem.core.analytics.AppInstanceIdProvider
import com.tangem.core.analytics.utils.AnalyticsContextProxy
import com.tangem.domain.analytics.ChangeCardAnalyticsContextUseCase
import com.tangem.tap.common.analytics.DefaultAnalyticsContextProxy
import com.tangem.tap.common.analytics.DefaultChangeCardAnalyticsContextUseCase
import com.tangem.core.analytics.utils.TrackingContextProxy
import com.tangem.tap.common.analytics.DefaultTrackingContextProxy
import com.tangem.tap.common.analytics.handlers.firebase.FirebaseAppInstanceIdProvider
import dagger.Module
import dagger.Provides
@ -18,13 +17,8 @@ internal object AnalyticsModule {
@Provides
@Singleton
fun provideChangeCardAnalyticsContextUseCase(): ChangeCardAnalyticsContextUseCase {
return DefaultChangeCardAnalyticsContextUseCase()
}
@Provides
@Singleton
fun provideAnalyticsContextProxy(): AnalyticsContextProxy = DefaultAnalyticsContextProxy()
fun provideAnalyticsContextProxy(abtestsManager: ABTestsManager): TrackingContextProxy =
DefaultTrackingContextProxy(abtestsManager)
@Provides
@Singleton

View file

@ -2,11 +2,11 @@ package com.tangem.tap.domain
import com.tangem.blockchain.common.Token
import com.tangem.blockchain.common.Wallet
import com.tangem.core.analytics.Analytics
import com.tangem.domain.common.extensions.withMainContext
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.tap.common.extensions.setContext
import com.tangem.tap.common.extensions.inject
import com.tangem.tap.common.redux.global.GlobalAction
import com.tangem.tap.proxy.redux.DaggerGraphState
import com.tangem.tap.store
import com.tangem.tap.tangemSdkManager
import com.tangem.utils.coroutines.AppCoroutineDispatcherProvider
@ -34,7 +34,8 @@ class TapWalletManager(
}
private suspend fun loadUserWalletData(userWallet: UserWallet) {
Analytics.setContext(userWallet)
val trackingContextProxy = store.inject(DaggerGraphState::trackingContextProxy)
trackingContextProxy.setContext(userWallet)
if (userWallet is UserWallet.Cold) {
val scanResponse = userWallet.scanResponse

View file

@ -12,6 +12,7 @@ import com.tangem.core.analytics.models.AnalyticsEvent
import com.tangem.core.analytics.models.AnalyticsParam
import com.tangem.core.analytics.models.Basic
import com.tangem.core.analytics.models.event.OnboardingAnalyticsEvent
import com.tangem.core.analytics.utils.TrackingContextProxy
import com.tangem.core.decompose.di.GlobalUiMessageSender
import com.tangem.core.decompose.ui.UiMessageSender
import com.tangem.core.ui.R
@ -47,6 +48,7 @@ import javax.inject.Singleton
internal class LegacyScanProcessor @Inject constructor(
@GlobalUiMessageSender private val uiMessageSender: UiMessageSender,
private val analyticsEventHandler: AnalyticsEventHandler,
private val trackingContextProxy: TrackingContextProxy,
) {
suspend fun scan(
@ -211,7 +213,7 @@ internal class LegacyScanProcessor @Inject constructor(
},
) {
if (OnboardingHelper.isOnboardingCase(scanResponse)) {
Analytics.addContext(scanResponse)
trackingContextProxy.addContext(scanResponse)
onWalletNotCreated()
navigateTo(
AppRoute.Onboarding(
@ -220,7 +222,7 @@ internal class LegacyScanProcessor @Inject constructor(
),
) { onProgressStateChange(it) }
} else {
Analytics.setContext(scanResponse)
trackingContextProxy.setContext(scanResponse)
val wasTwinsOnboardingShown =
store.inject(DaggerGraphState::wasTwinsOnboardingShownUseCase).invokeSync()

View file

@ -3,15 +3,12 @@ package com.tangem.tap.domain.scanCard.chains
import arrow.core.left
import arrow.core.right
import com.tangem.common.routing.AppRoute
import com.tangem.core.analytics.Analytics
import com.tangem.domain.card.ScanCardException
import com.tangem.domain.card.common.util.twinsIsTwinned
import com.tangem.domain.core.chain.Chain
import com.tangem.domain.core.chain.ResultChain
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.tap.common.extensions.addContext
import com.tangem.tap.common.extensions.inject
import com.tangem.tap.common.extensions.setContext
import com.tangem.tap.common.redux.AppState
import com.tangem.tap.features.onboarding.OnboardingHelper
import com.tangem.tap.proxy.redux.DaggerGraphState
@ -34,9 +31,11 @@ class CheckForOnboardingChain(
) : ResultChain<ScanCardException, ScanResponse>() {
override suspend fun launch(previousChainResult: ScanResponse): ScanChainResult {
val trackingContextProxy = store.inject(DaggerGraphState::trackingContextProxy)
return when {
OnboardingHelper.isOnboardingCase(previousChainResult) -> {
Analytics.addContext(previousChainResult)
trackingContextProxy.addContext(previousChainResult)
ScanChainException.OnboardingNeeded(
AppRoute.Onboarding(
scanResponse = previousChainResult,
@ -45,7 +44,7 @@ class CheckForOnboardingChain(
).left()
}
else -> {
Analytics.setContext(previousChainResult)
trackingContextProxy.setContext(previousChainResult)
val wasTwinsOnboardingShown = store.inject(DaggerGraphState::wasTwinsOnboardingShownUseCase)
.invokeSync()

View file

@ -4,9 +4,9 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.tangem.blockchainsdk.BlockchainSDKFactory
import com.tangem.common.keyboard.KeyboardValidator
import com.tangem.core.analytics.Analytics
import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.core.analytics.models.event.TechAnalyticsEvent
import com.tangem.core.analytics.utils.TrackingContextProxy
import com.tangem.core.decompose.di.GlobalUiMessageSender
import com.tangem.core.decompose.ui.UiMessageSender
import com.tangem.core.ui.R
@ -38,7 +38,6 @@ import com.tangem.domain.wallets.usecase.GetSavedWalletsCountUseCase
import com.tangem.domain.wallets.usecase.GetSelectedWalletUseCase
import com.tangem.domain.wallets.usecase.UpdateRemoteWalletsInfoUseCase
import com.tangem.feature.swap.analytics.StoriesEvents
import com.tangem.tap.common.extensions.setContext
import com.tangem.tap.network.exchangeServices.SellService
import com.tangem.tap.proxy.AppStateHolder
import com.tangem.tap.routing.configurator.AppRouterConfig
@ -82,6 +81,7 @@ internal class MainViewModel @Inject constructor(
private val getSelectedWalletUseCase: GetSelectedWalletUseCase,
private val appRouterConfig: AppRouterConfig,
private val sellService: SellService,
private val trackingContextProxy: TrackingContextProxy,
getBalanceHidingSettingsUseCase: GetBalanceHidingSettingsUseCase,
) : ViewModel() {
@ -177,7 +177,7 @@ internal class MainViewModel @Inject constructor(
.mapLeft { emptyFlow<UserWallet>() }
.onRight { wallet ->
wallet.distinctUntilChanged()
.onEach { Analytics.setContext(it) }
.onEach { trackingContextProxy.setContext(it) }
.flowOn(dispatchers.io)
.launchIn(viewModelScope)
}

View file

@ -100,7 +100,10 @@ internal class WelcomeMiddleware {
val currency = ParamCardCurrencyConverter().convert(
value = scanResponse.cardTypesResolver,
)
Analytics.addContext(scanResponse)
val trackingContextProxy = store.inject(DaggerGraphState::trackingContextProxy)
trackingContextProxy.addContext(scanResponse)
if (currency != null) {
val userWalletsListManager = store.inject(DaggerGraphState::generalUserWalletsListManager)

View file

@ -3,6 +3,7 @@ package com.tangem.tap.proxy.redux
import com.tangem.blockchainsdk.BlockchainSDKFactory
import com.tangem.blockchainsdk.utils.ExcludedBlockchains
import com.tangem.common.routing.AppRouter
import com.tangem.core.analytics.utils.TrackingContextProxy
import com.tangem.core.decompose.ui.UiMessageSender
import com.tangem.core.navigation.settings.SettingsManager
import com.tangem.core.navigation.share.ShareManager
@ -79,4 +80,5 @@ data class DaggerGraphState(
val userWalletsListRepository: UserWalletsListRepository? = null,
val hotWalletFeatureToggles: HotWalletFeatureToggles? = null,
val tangemHotSdk: TangemHotSdk? = null,
val trackingContextProxy: TrackingContextProxy? = null,
) : StateType