From 0b09414eb72f47f986b91bb40e07de976efc05ac Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 6 Jun 2022 10:46:46 +0400 Subject: [PATCH 1/8] Updated on 2026-08-14 --- app/build.gradle | 7 ++- app/src/main/AndroidManifest.xml | 1 + .../java/com/tangem/tap/TapApplication.kt | 21 +++---- .../analytics/AmplitudeAnalyticsHandler.kt | 56 +++++++++++++++++++ .../tap/common/analytics/AnalyticsHandler.kt | 8 ++- .../analytics/AppsFlyerAnalyticsHandler.kt | 2 +- .../analytics/FirebaseAnalyticsHandler.kt | 4 +- .../analytics/GlobalAnalyticsHandler.kt | 28 +++++++++- .../configurable/config/ConfigManager.kt | 3 + .../domain/configurable/config/JsonModels.kt | 1 + 10 files changed, 107 insertions(+), 24 deletions(-) create mode 100644 app/src/main/java/com/tangem/tap/common/analytics/AmplitudeAnalyticsHandler.kt diff --git a/app/build.gradle b/app/build.gradle index 3e16223f89..a875995cbe 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -127,8 +127,9 @@ dependencies { implementation 'com.google.firebase:firebase-config-ktx' implementation 'com.google.firebase:firebase-analytics-ktx' - // AppsFlyer + // Analytics implementation 'com.appsflyer:af-android-sdk:6.5.1' + implementation 'com.amplitude:android-sdk:2.36.1' // WalletConnect implementation 'com.github.salomonbrys.kotson:kotson:2.5.0' @@ -144,8 +145,8 @@ dependencies { implementation("com.squareup.okhttp3:okhttp") implementation("com.squareup.okhttp3:logging-interceptor") -// -// //Crypto + + //Crypto implementation "com.madgag.spongycastle:core:1.58.0.0" // animation diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 4dfca87b23..3a5c5bf394 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -16,6 +16,7 @@ + diff --git a/app/src/main/java/com/tangem/tap/TapApplication.kt b/app/src/main/java/com/tangem/tap/TapApplication.kt index b58485ab88..35e0c31944 100644 --- a/app/src/main/java/com/tangem/tap/TapApplication.kt +++ b/app/src/main/java/com/tangem/tap/TapApplication.kt @@ -1,7 +1,6 @@ package com.tangem.tap import android.app.Application -import com.appsflyer.AppsFlyerLib import com.google.firebase.ktx.Firebase import com.google.firebase.remoteconfig.ktx.remoteConfig import com.google.firebase.remoteconfig.ktx.remoteConfigSettings @@ -59,20 +58,22 @@ class TapApplication : Application() { }) } - NetworkConnectivity.createInstance(store, this) - preferencesStorage = PreferencesStorage(this) - PicassoHelper.initPicassoWithCaching(this) + val application = this + NetworkConnectivity.createInstance(store, application) + preferencesStorage = PreferencesStorage(application) + PicassoHelper.initPicassoWithCaching(application) currenciesRepository = CurrenciesRepository( this, store.state.domainNetworks.tangemTechService ) - walletConnectRepository = WalletConnectRepository(this) + walletConnectRepository = WalletConnectRepository(application) initFeedbackManager() loadConfigs() BlockchainSdkRetrofitBuilder.enableNetworkLogging = BuildConfig.DEBUG - initAppsFlyer() + val analyticsHandler = GlobalAnalyticsHandler.createDefaultAnalyticHandlers(application) + store.dispatch(GlobalAction.SetAnanlyticHandlers(analyticsHandler)) } private fun loadConfigs() { @@ -97,14 +98,6 @@ class TapApplication : Application() { store.dispatch(GlobalAction.SetFeedbackManager(FeedbackManager(infoHolder, logWriter))) } - - private fun initAppsFlyer() { - val devKey = store.state.globalState.configManager?.config?.appsFlyerDevKey ?: return - AppsFlyerLib.getInstance().init(devKey, null, this) - AppsFlyerLib.getInstance().start(this) - val analyticsHandler = GlobalAnalyticsHandler.createDefaultAnalyticHandlers(this) - store.dispatch(GlobalAction.SetAnanlyticHandlers(analyticsHandler)) - } } data class LogConfig( diff --git a/app/src/main/java/com/tangem/tap/common/analytics/AmplitudeAnalyticsHandler.kt b/app/src/main/java/com/tangem/tap/common/analytics/AmplitudeAnalyticsHandler.kt new file mode 100644 index 0000000000..b68a9a3eb5 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/common/analytics/AmplitudeAnalyticsHandler.kt @@ -0,0 +1,56 @@ +package com.tangem.tap.common.analytics + +import com.amplitude.api.Amplitude +import com.amplitude.api.AmplitudeClient +import com.shopify.buy3.Storefront +import com.tangem.common.card.Card +import com.tangem.common.core.TangemSdkError +import org.json.JSONObject + +class AmplitudeAnalyticsHandler : AnalyticsHandler() { + + private val client: AmplitudeClient = Amplitude.getInstance() + + override fun triggerEvent( + event: AnalyticsEvent, + card: Card?, + blockchain: String?, + params: Map + ) { + client.logEvent(event.event, prepareParams(card, blockchain, params).toJson()) + } + + override fun triggerEvent(event: String, params: Map) { + client.logEvent(event, params.toJson()) + } + + override fun logCardSdkError( + error: TangemSdkError, + actionToLog: Analytics.ActionToLog, + parameters: Map?, + card: Card? + ) { + //TODO + } + + override fun logError(error: Throwable, params: Map) { + //TODO + } + + override fun getOrderEvent(): String { + //TODO + return "" + } + + override fun getOrderParams(order: Storefront.Order): Map { + //TODO + return emptyMap() + } + + private fun Map.toJson(): JSONObject { + val json = JSONObject() + this.map { json.put(it.key, it.value) } + return json + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/analytics/AnalyticsHandler.kt b/app/src/main/java/com/tangem/tap/common/analytics/AnalyticsHandler.kt index 424f06d9ce..5386779df4 100644 --- a/app/src/main/java/com/tangem/tap/common/analytics/AnalyticsHandler.kt +++ b/app/src/main/java/com/tangem/tap/common/analytics/AnalyticsHandler.kt @@ -31,12 +31,16 @@ abstract class AnalyticsHandler { params: Map = emptyMap() ) - protected fun prepareParams(card: Card?, blockchain: String? = null): Map { + protected fun prepareParams( + card: Card?, + blockchain: String? = null, + params: Map = emptyMap() + ): Map { return mapOf( AnalyticsParam.FIRMWARE.param to card?.firmwareVersion?.stringValue, AnalyticsParam.BATCH_ID.param to card?.batchId, AnalyticsParam.BLOCKCHAIN.param to blockchain, - ).filterNotNull() + ).filterNotNull() + params } fun logWcEvent(event: Analytics.WcAnalyticsEvent) { diff --git a/app/src/main/java/com/tangem/tap/common/analytics/AppsFlyerAnalyticsHandler.kt b/app/src/main/java/com/tangem/tap/common/analytics/AppsFlyerAnalyticsHandler.kt index 64beb11b4a..b36ecebbcf 100644 --- a/app/src/main/java/com/tangem/tap/common/analytics/AppsFlyerAnalyticsHandler.kt +++ b/app/src/main/java/com/tangem/tap/common/analytics/AppsFlyerAnalyticsHandler.kt @@ -17,7 +17,7 @@ class AppsFlyerAnalyticsHandler(val context: Application): AnalyticsHandler() { params: Map ) { AppsFlyerLib.getInstance().logEvent(context, - event.event, prepareParams(card, blockchain)) + event.event, prepareParams(card, blockchain, params)) } override fun logCardSdkError( diff --git a/app/src/main/java/com/tangem/tap/common/analytics/FirebaseAnalyticsHandler.kt b/app/src/main/java/com/tangem/tap/common/analytics/FirebaseAnalyticsHandler.kt index 4e0371206a..e691c2239f 100644 --- a/app/src/main/java/com/tangem/tap/common/analytics/FirebaseAnalyticsHandler.kt +++ b/app/src/main/java/com/tangem/tap/common/analytics/FirebaseAnalyticsHandler.kt @@ -17,7 +17,7 @@ object FirebaseAnalyticsHandler : AnalyticsHandler() { blockchain: String?, params: Map ) { - val fullParams = prepareParams(card, blockchain) + params + val fullParams = prepareParams(card, blockchain, params) Firebase.analytics.logEvent(event.event, fullParams.toBundle()) } @@ -42,7 +42,7 @@ object FirebaseAnalyticsHandler : AnalyticsHandler() { if (error is TangemSdkError.UserCancelled) return val params = parameters?.toMutableMap() ?: mutableMapOf() - if (card != null) params + prepareParams(card) + if (card != null) params + prepareParams(card) params[AnalyticsParam.ACTION] = actionToLog.key params[AnalyticsParam.ERROR_CODE] = error.code.toString() params[AnalyticsParam.ERROR_DESCRIPTION] = error.javaClass.simpleName diff --git a/app/src/main/java/com/tangem/tap/common/analytics/GlobalAnalyticsHandler.kt b/app/src/main/java/com/tangem/tap/common/analytics/GlobalAnalyticsHandler.kt index c500f330b2..0c23b37305 100644 --- a/app/src/main/java/com/tangem/tap/common/analytics/GlobalAnalyticsHandler.kt +++ b/app/src/main/java/com/tangem/tap/common/analytics/GlobalAnalyticsHandler.kt @@ -1,9 +1,12 @@ package com.tangem.tap.common.analytics import android.app.Application +import com.amplitude.api.Amplitude +import com.appsflyer.AppsFlyerLib import com.shopify.buy3.Storefront import com.tangem.common.card.Card import com.tangem.common.core.TangemSdkError +import com.tangem.tap.store class GlobalAnalyticsHandler(val analyticsHandlers: List) : AnalyticsHandler() { @@ -42,13 +45,34 @@ class GlobalAnalyticsHandler(val analyticsHandlers: List) : } companion object { - fun createDefaultAnalyticHandlers(context: Application): GlobalAnalyticsHandler { + fun createDefaultAnalyticHandlers(application: Application): GlobalAnalyticsHandler { + initAnalytics(application) return GlobalAnalyticsHandler( listOf( FirebaseAnalyticsHandler, - AppsFlyerAnalyticsHandler(context) + AppsFlyerAnalyticsHandler(application), + AmplitudeAnalyticsHandler() ) ) } + + private fun initAnalytics(application: Application) { + initAppsFlyer(application) + initAmplitude(application) + } + + private fun initAppsFlyer(application: Application) { + val devKey = store.state.globalState.configManager?.config?.appsFlyerDevKey ?: return + AppsFlyerLib.getInstance().init(devKey, null, application) + AppsFlyerLib.getInstance().start(application) + + } + + private fun initAmplitude(application: Application) { + val apiKey = store.state.globalState.configManager?.config?.amplitudeApiKey ?: return + Amplitude.getInstance() + .initialize(application.applicationContext, apiKey) + .enableForegroundTracking(application) + } } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/configurable/config/ConfigManager.kt b/app/src/main/java/com/tangem/tap/domain/configurable/config/ConfigManager.kt index cb587f277b..3bc8046f84 100644 --- a/app/src/main/java/com/tangem/tap/domain/configurable/config/ConfigManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/configurable/config/ConfigManager.kt @@ -14,6 +14,7 @@ data class Config( val onramperApiKey: String = "pk_test_Ix2aCF3ej_5tcDKkBR7MChIvf5Nb0oPORPQ3Oal5G8I0", val moonPayApiSecretKey: String = "sk_test_V8w4M19LbDjjYOt170s0tGuvXAgyEb1C", val appsFlyerDevKey: String = "", + val amplitudeApiKey: String = "", val blockchainSdkConfig: BlockchainSdkConfig = BlockchainSdkConfig(), val isSendingToPayIdEnabled: Boolean = true, val isTopUpEnabled: Boolean = false, @@ -91,6 +92,7 @@ class ConfigManager( infuraProjectId = values.infuraProjectId ), appsFlyerDevKey = values.appsFlyerDevKey, + amplitudeApiKey = values.amplitudeApiKey, shopify = values.shopifyShop, ) defaultConfig = defaultConfig.copy( @@ -105,6 +107,7 @@ class ConfigManager( infuraProjectId = values.infuraProjectId ), appsFlyerDevKey = values.appsFlyerDevKey, + amplitudeApiKey = values.amplitudeApiKey, shopify = values.shopifyShop, ) } diff --git a/app/src/main/java/com/tangem/tap/domain/configurable/config/JsonModels.kt b/app/src/main/java/com/tangem/tap/domain/configurable/config/JsonModels.kt index 41d1ea2022..f206d85362 100644 --- a/app/src/main/java/com/tangem/tap/domain/configurable/config/JsonModels.kt +++ b/app/src/main/java/com/tangem/tap/domain/configurable/config/JsonModels.kt @@ -23,6 +23,7 @@ class ConfigValueModel( val blockcypherTokens: Set?, val infuraProjectId: String?, val appsFlyerDevKey: String, + val amplitudeApiKey: String, val shopifyShop: ShopifyShop? ) From 34a865283d143a61437b946a9caa4ed4552e729e Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 8 Jul 2022 12:50:06 +0400 Subject: [PATCH 2/8] Updated on 2026-08-14 --- app/src/main/assets/tangem-app-config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/assets/tangem-app-config b/app/src/main/assets/tangem-app-config index 7f058ec409..9b3e3e810a 160000 --- a/app/src/main/assets/tangem-app-config +++ b/app/src/main/assets/tangem-app-config @@ -1 +1 @@ -Subproject commit 7f058ec409b4eaaf8688ecd4bf944ef8d58d3564 +Subproject commit 9b3e3e810ae5114207b50260579707c59ec28b0e From 8c8a6579c235279f0baf5bf890eef7c09a5ac800 Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 8 Jul 2022 13:12:18 +0400 Subject: [PATCH 3/8] Updated on 2026-08-14 --- app/src/main/assets/tangem-app-config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/assets/tangem-app-config b/app/src/main/assets/tangem-app-config index 9b3e3e810a..d275fc774b 160000 --- a/app/src/main/assets/tangem-app-config +++ b/app/src/main/assets/tangem-app-config @@ -1 +1 @@ -Subproject commit 9b3e3e810ae5114207b50260579707c59ec28b0e +Subproject commit d275fc774ba836d70c429f16c40046fb7ee5b96b From 320e663f859a1ef96b9e1f491bfdbe5098eec53d Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 23 Sep 2022 02:05:56 +0300 Subject: [PATCH 4/8] Updated on 2026-08-14 --- app/src/main/java/com/tangem/tap/TapApplication.kt | 4 ++-- .../tangem/tap/common/redux/global/GlobalAction.kt | 2 +- .../tap/common/redux/global/GlobalMidlleware.kt | 2 +- .../tangem/tap/common/redux/global/GlobalReducer.kt | 3 +-- .../tangem/tap/common/redux/global/GlobalState.kt | 4 ++-- .../tap/domain/walletconnect/WalletConnectManager.kt | 12 ++++++------ .../domain/walletconnect/WalletConnectSdkHelper.kt | 8 ++++---- .../tap/features/details/redux/DetailsMiddleware.kt | 4 ++-- .../tangem/tap/features/home/redux/HomeMiddleware.kt | 2 +- .../products/twins/redux/TwinCardsMiddleware.kt | 2 +- .../send/redux/middlewares/SendMiddleware.kt | 4 ++-- .../tangem/tap/features/shop/redux/ShopMiddleware.kt | 2 +- .../wallet/redux/middlewares/WalletMiddleware.kt | 2 +- .../wallet/redux/middlewares/WarningsMiddleware.kt | 4 ++-- .../wallet/ui/adapters/WarningMessagesAdapter.kt | 2 +- 15 files changed, 28 insertions(+), 29 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/TapApplication.kt b/app/src/main/java/com/tangem/tap/TapApplication.kt index 8908d91491..22165f40d0 100644 --- a/app/src/main/java/com/tangem/tap/TapApplication.kt +++ b/app/src/main/java/com/tangem/tap/TapApplication.kt @@ -81,7 +81,7 @@ class TapApplication : Application(), ImageLoaderFactory { override fun newImageLoader(): ImageLoader { return createCoilImageLoader( context = this, - logEnabled = LogConfig.imageLoader + logEnabled = LogConfig.imageLoader, ) } @@ -103,7 +103,7 @@ class TapApplication : Application(), ImageLoaderFactory { AppsFlyerLib.getInstance().init(config.appsFlyerDevKey, null, context) AppsFlyerLib.getInstance().start(context) val analyticsHandler = GlobalAnalyticsHandler.createDefaultAnalyticHandlers(context) - store.dispatch(GlobalAction.SetAnanlyticHandlers(analyticsHandler)) + store.dispatch(GlobalAction.SetGlobalAnalyticsHandler(analyticsHandler)) } private fun initFeedbackManager(context: Context, preferencesStorage: PreferencesStorage, config: Config) { diff --git a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalAction.kt b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalAction.kt index 4927405ccd..4e441f768e 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalAction.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalAction.kt @@ -75,7 +75,7 @@ sealed class GlobalAction : Action { data class SetConfigManager(val configManager: ConfigManager) : GlobalAction() data class SetWarningManager(val warningManager: WarningMessagesManager) : GlobalAction() data class SetFeedbackManager(val feedbackManager: FeedbackManager) : GlobalAction() - data class SetAnanlyticHandlers(val analyticsHandlers: GlobalAnalyticsHandler) : GlobalAction() + data class SetGlobalAnalyticsHandler(val analyticsHandler: GlobalAnalyticsHandler) : GlobalAction() data class SendEmail(val feedbackData: FeedbackData) : GlobalAction() data class OpenChat(val feedbackData: FeedbackData) : GlobalAction() diff --git a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalMidlleware.kt b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalMidlleware.kt index 8173f76aae..0b938c6bf7 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalMidlleware.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalMidlleware.kt @@ -140,7 +140,7 @@ private fun handleAction(action: Action, appState: () -> AppState?, dispatch: Di is GlobalAction.ScanCard -> { scope.launch { val result = tangemSdkManager.scanProduct( - store.state.globalState.analyticsHandlers, + store.state.globalState.analyticsHandler, userTokensRepository, action.additionalBlockchainsToDerive, action.messageResId, diff --git a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalReducer.kt b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalReducer.kt index 449d47ae10..e8ae540deb 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalReducer.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalReducer.kt @@ -49,8 +49,7 @@ fun globalReducer(action: Action, state: AppState): GlobalState { globalState.copy(configManager = action.configManager) } is GlobalAction.SetWarningManager -> globalState.copy(warningManager = action.warningManager) - is GlobalAction.SetAnanlyticHandlers -> - globalState.copy(analyticsHandlers = action.analyticsHandlers) + is GlobalAction.SetGlobalAnalyticsHandler -> globalState.copy(analyticsHandler = action.analyticsHandler) is GlobalAction.UpdateWalletSignedHashes -> { val card = globalState.scanResponse?.card ?: return globalState val wallet = card.wallet(action.walletPublicKey) ?: return globalState diff --git a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalState.kt b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalState.kt index 1fed436b47..17c8ed9f76 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalState.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalState.kt @@ -1,7 +1,7 @@ package com.tangem.tap.common.redux.global import com.tangem.domain.common.ScanResponse -import com.tangem.tap.common.analytics.AnalyticsHandler +import com.tangem.tap.common.analytics.GlobalAnalyticsHandler import com.tangem.tap.common.entities.FiatCurrency import com.tangem.tap.common.feedback.FeedbackManager import com.tangem.tap.common.redux.StateDialog @@ -27,7 +27,7 @@ data class GlobalState( val dialog: StateDialog? = null, val exchangeManager: CurrencyExchangeManager = CurrencyExchangeManager.dummy(), val resources: AndroidResources = AndroidResources(), - val analyticsHandlers: AnalyticsHandler? = null, + val analyticsHandler: GlobalAnalyticsHandler? = null, val userCountryCode: String? = null, ) : StateType diff --git a/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectManager.kt b/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectManager.kt index 45c3dee2e3..305f4ec978 100644 --- a/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectManager.kt @@ -200,7 +200,7 @@ class WalletConnectManager { } private fun onSessionClosed(session: WCSession) { - store.state.globalState.analyticsHandlers?.logWcEvent( + store.state.globalState.analyticsHandler?.logWcEvent( Analytics.WcAnalyticsEvent.Session( Analytics.WcSessionEvent.Disconnect, sessions[session]?.peerMeta?.url ) @@ -341,7 +341,7 @@ class WalletConnectManager { ) ) } - store.state.globalState.analyticsHandlers?.logWcEvent( + store.state.globalState.analyticsHandler?.logWcEvent( Analytics.WcAnalyticsEvent.Session( Analytics.WcSessionEvent.Connect, peer.url ) @@ -355,7 +355,7 @@ class WalletConnectManager { } client.onEthSendTransaction = { id: Long, transaction: WCEthereumTransaction -> Timber.d("onEthSendTransaction: $transaction") - store.state.globalState.analyticsHandlers?.logWcEvent( + store.state.globalState.analyticsHandler?.logWcEvent( Analytics.WcAnalyticsEvent.Action( Analytics.WcAction.SendTransaction ) @@ -373,7 +373,7 @@ class WalletConnectManager { } client.onEthSignTransaction = { id: Long, transaction: WCEthereumTransaction -> Timber.d("onEthSignTransaction: $transaction") - store.state.globalState.analyticsHandlers?.logWcEvent( + store.state.globalState.analyticsHandler?.logWcEvent( Analytics.WcAnalyticsEvent.Action( Analytics.WcAction.SignTransaction ) @@ -391,7 +391,7 @@ class WalletConnectManager { } client.onEthSign = { id: Long, message: WCEthereumSignMessage -> Timber.d("onEthSign: $message") - store.state.globalState.analyticsHandlers?.logWcEvent( + store.state.globalState.analyticsHandler?.logWcEvent( Analytics.WcAnalyticsEvent.Action( Analytics.WcAction.PersonalSign ) @@ -467,7 +467,7 @@ class WalletConnectManager { val message = EthSignHelper.tryToParseEthTypedMessage(request) if (message != null) { Timber.d("onEthSign_v4: $message") - store.state.globalState.analyticsHandlers?.logWcEvent( + store.state.globalState.analyticsHandler?.logWcEvent( Analytics.WcAnalyticsEvent.Action( Analytics.WcAction.PersonalSign ) diff --git a/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectSdkHelper.kt b/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectSdkHelper.kt index b23a5e1456..78db9b27fc 100644 --- a/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectSdkHelper.kt +++ b/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectSdkHelper.kt @@ -144,7 +144,7 @@ class WalletConnectSdkHelper { HEX_PREFIX + data.walletManager.wallet.recentTransactions.last().hash } is SimpleResult.Failure -> { - store.state.globalState.analyticsHandlers?.logSendTransactionError( + store.state.globalState.analyticsHandler?.logSendTransactionError( result.error, Analytics.ActionToLog.WalletConnectTransaction, ) @@ -174,7 +174,7 @@ class WalletConnectSdkHelper { } is CompletionResult.Failure -> { (result.error as? TangemSdkError)?.let { error -> - store.state.globalState.analyticsHandlers?.logCardSdkError( + store.state.globalState.analyticsHandler?.logCardSdkError( error, Analytics.ActionToLog.WalletConnectSign, ) @@ -203,7 +203,7 @@ class WalletConnectSdkHelper { } is CompletionResult.Failure -> { (result.error as? TangemSdkError)?.let { error -> - store.state.globalState.analyticsHandlers?.logCardSdkError( + store.state.globalState.analyticsHandler?.logCardSdkError( error, Analytics.ActionToLog.WalletConnectTransaction, ) @@ -279,7 +279,7 @@ class WalletConnectSdkHelper { } is CompletionResult.Failure -> { (result.error as? TangemSdkError)?.let { error -> - store.state.globalState.analyticsHandlers?.logCardSdkError( + store.state.globalState.analyticsHandler?.logCardSdkError( error, Analytics.ActionToLog.WalletConnectSign, ) diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsMiddleware.kt b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsMiddleware.kt index fa96a81962..30eebe686a 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsMiddleware.kt @@ -115,7 +115,7 @@ class DetailsMiddleware { } is CompletionResult.Failure -> { (result.error as? TangemSdkError)?.let { error -> - store.state.globalState.analyticsHandlers?.logCardSdkError( + store.state.globalState.analyticsHandler?.logCardSdkError( error, Analytics.ActionToLog.PurgeWallet, card = store.state.detailsState.scanResponse?.card, @@ -159,7 +159,7 @@ class DetailsMiddleware { } is CompletionResult.Failure -> { (result.error as? TangemSdkError)?.let { error -> - store.state.globalState.analyticsHandlers?.logCardSdkError( + store.state.globalState.analyticsHandler?.logCardSdkError( error = error, actionToLog = Analytics.ActionToLog.ChangeSecOptions, parameters = mapOf( diff --git a/app/src/main/java/com/tangem/tap/features/home/redux/HomeMiddleware.kt b/app/src/main/java/com/tangem/tap/features/home/redux/HomeMiddleware.kt index 6b0bf8e8e0..ceb36bc407 100644 --- a/app/src/main/java/com/tangem/tap/features/home/redux/HomeMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/home/redux/HomeMiddleware.kt @@ -62,7 +62,7 @@ private val homeMiddleware: Middleware = { _, _ -> store.dispatchOpenUrl(BUY_WALLET_URL) else -> store.dispatch(NavigationAction.NavigateTo(AppScreen.Shop)) } - store.state.globalState.analyticsHandlers?.triggerEvent( + store.state.globalState.analyticsHandler?.triggerEvent( event = AnalyticsEvent.GET_CARD, params = mapOf(AnalyticsParam.SOURCE.param to GetCardSourceParams.WELCOME.param) ) diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/redux/TwinCardsMiddleware.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/redux/TwinCardsMiddleware.kt index e7d3a6501c..47dd842d1d 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/redux/TwinCardsMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/redux/TwinCardsMiddleware.kt @@ -139,7 +139,7 @@ private fun handle(action: Action, dispatch: DispatchFunction) { val manager = TwinCardsManager( card = getScanResponse().card, assetReader = action.reader, - analyticsHandler = store.state.globalState.analyticsHandlers + analyticsHandler = store.state.globalState.analyticsHandler ) store.dispatch(TwinCardsAction.CardsManager.Set(manager)) diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/SendMiddleware.kt b/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/SendMiddleware.kt index 54be369642..8a3871a455 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/SendMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/SendMiddleware.kt @@ -222,7 +222,7 @@ private fun sendTransaction( when (sendResult) { is SimpleResult.Success -> { - store.state.globalState.analyticsHandlers?.triggerEvent( + store.state.globalState.analyticsHandler?.triggerEvent( event = AnalyticsEvent.TRANSACTION_IS_SENT, card = card, blockchain = walletManager.wallet.blockchain.currency, @@ -252,7 +252,7 @@ private fun sendTransaction( feeAmount = feeAmount, destinationAddress = destinationAddress, ) - store.state.globalState.analyticsHandlers?.logSendTransactionError( + store.state.globalState.analyticsHandler?.logSendTransactionError( error = sendResult.error, action = Analytics.ActionToLog.SendTransaction, parameters = mapOf(AnalyticsParam.BLOCKCHAIN to walletManager.wallet.blockchain.currency), diff --git a/app/src/main/java/com/tangem/tap/features/shop/redux/ShopMiddleware.kt b/app/src/main/java/com/tangem/tap/features/shop/redux/ShopMiddleware.kt index f3734950da..ceba9a5217 100644 --- a/app/src/main/java/com/tangem/tap/features/shop/redux/ShopMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/shop/redux/ShopMiddleware.kt @@ -121,7 +121,7 @@ private fun handle(action: Action) { scope.launch { shopService.waitForCheckout( productType = shopState.selectedProduct, - analyticsHandler = store.state.globalState.analyticsHandlers + analyticsHandler = store.state.globalState.analyticsHandler ) } } diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WalletMiddleware.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WalletMiddleware.kt index 256e72fd37..71506a46da 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WalletMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WalletMiddleware.kt @@ -174,7 +174,7 @@ class WalletMiddleware { } is CompletionResult.Failure -> { (result.error as? TangemSdkError)?.let { error -> - store.state.globalState.analyticsHandlers?.logCardSdkError( + store.state.globalState.analyticsHandler?.logCardSdkError( error, Analytics.ActionToLog.CreateWallet, card = store.state.detailsState.scanResponse?.card diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WarningsMiddleware.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WarningsMiddleware.kt index 7aa45f997d..52c93db66e 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WarningsMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WarningsMiddleware.kt @@ -25,10 +25,10 @@ import com.tangem.tap.preferencesStorage import com.tangem.tap.scope import com.tangem.tap.store import com.tangem.wallet.R -import java.math.BigDecimal import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext +import java.math.BigDecimal class WarningsMiddleware { fun handle(action: WalletAction.Warnings, globalState: GlobalState?) { @@ -81,7 +81,7 @@ class WarningsMiddleware { preferencesStorage.appRatingLaunchObserver.foundWalletWithFunds() } if (preferencesStorage.appRatingLaunchObserver.isReadyToShow()) { - store.state.globalState.analyticsHandlers?.triggerEvent(AnalyticsEvent.APP_RATING_DISPLAYED) + store.state.globalState.analyticsHandler?.triggerEvent(AnalyticsEvent.APP_RATING_DISPLAYED) addWarningMessage(WarningMessagesManager.appRatingWarning(), true) } } diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/adapters/WarningMessagesAdapter.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/adapters/WarningMessagesAdapter.kt index dee8fef67c..14cd39d812 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/adapters/WarningMessagesAdapter.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/adapters/WarningMessagesAdapter.kt @@ -107,7 +107,7 @@ class WarningMessageVH(val binding: LayoutWarningCardActionBinding) : RecyclerVi binding.groupControlsTemporary.hide() binding.groupControlsRating.show() binding.btnClose.show() - val analyticsHandler = store.state.globalState.analyticsHandlers + val analyticsHandler = store.state.globalState.analyticsHandler binding.btnClose.setOnClickListener { analyticsHandler?.triggerEvent(AnalyticsEvent.APP_RATING_DISMISS) store.dispatch(GlobalAction.HideWarningMessage(warning)) From 53f9b9d572687b364dd4dea9e7a65aa94383c816 Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 23 Sep 2022 02:07:29 +0300 Subject: [PATCH 5/8] Updated on 2026-08-14 --- .../com/tangem/tap/common/shop/TangemShopService.kt | 12 ++++-------- .../java/com/tangem/tap/domain/TangemSdkManager.kt | 6 +++--- .../com/tangem/tap/domain/twins/TwinCardsManager.kt | 4 ++-- 3 files changed, 9 insertions(+), 13 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/common/shop/TangemShopService.kt b/app/src/main/java/com/tangem/tap/common/shop/TangemShopService.kt index f5281cc796..6151af3761 100644 --- a/app/src/main/java/com/tangem/tap/common/shop/TangemShopService.kt +++ b/app/src/main/java/com/tangem/tap/common/shop/TangemShopService.kt @@ -4,7 +4,8 @@ import android.app.Application import android.content.Intent import com.google.android.gms.wallet.PaymentData import com.shopify.buy3.Storefront -import com.tangem.tap.common.analytics.AnalyticsHandler +import com.tangem.common.extensions.ifNotNull +import com.tangem.tap.common.analytics.GlobalAnalyticsHandler import com.tangem.tap.common.extensions.filterNotNull import com.tangem.tap.common.shop.data.ProductType import com.tangem.tap.common.shop.data.TangemProduct @@ -195,15 +196,10 @@ class TangemShopService(application: Application, shopifyShop: ShopifyShop) { return checkouts[productType]!!.webUrl } - suspend fun waitForCheckout( - productType: ProductType, - analyticsHandler: AnalyticsHandler? - ) { + suspend fun waitForCheckout(productType: ProductType, analyticsHandler: GlobalAnalyticsHandler?) { val result = shopifyService.checkout(true, checkouts[productType]!!.id) result.onSuccess { - if (it.order != null) { - analyticsHandler?.logShopifyOrder(order = it.order) - } + ifNotNull(analyticsHandler, it.order) { handler, order -> handler.logShopifyOrder(order) } } } diff --git a/app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt b/app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt index 34e58f0ba7..43e318bd82 100644 --- a/app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt @@ -27,8 +27,8 @@ import com.tangem.operations.pins.CheckUserCodesResponse import com.tangem.operations.pins.SetUserCodeCommand import com.tangem.tap.common.analytics.Analytics import com.tangem.tap.common.analytics.AnalyticsEvent -import com.tangem.tap.common.analytics.AnalyticsHandler import com.tangem.tap.common.analytics.AnalyticsParam +import com.tangem.tap.common.analytics.GlobalAnalyticsHandler import com.tangem.tap.domain.tasks.CreateWalletAndRescanTask import com.tangem.tap.domain.tasks.product.ResetToFactorySettingsTask import com.tangem.tap.domain.tasks.product.ScanProductTask @@ -44,7 +44,7 @@ import kotlin.coroutines.suspendCoroutine class TangemSdkManager(private val tangemSdk: TangemSdk, private val context: Context) { suspend fun scanProduct( - analyticsHandler: AnalyticsHandler?, + analyticsHandler: GlobalAnalyticsHandler?, userTokensRepository: UserTokensRepository, additionalBlockchainsToDerive: Collection? = null, messageRes: Int? = null, @@ -69,7 +69,7 @@ class TangemSdkManager(private val tangemSdk: TangemSdk, private val context: Co } private fun sendScanResultsToAnalytics( - analyticsHandler: AnalyticsHandler?, + analyticsHandler: GlobalAnalyticsHandler?, result: CompletionResult ) { when (result) { diff --git a/app/src/main/java/com/tangem/tap/domain/twins/TwinCardsManager.kt b/app/src/main/java/com/tangem/tap/domain/twins/TwinCardsManager.kt index 1d02623442..d4313cf946 100644 --- a/app/src/main/java/com/tangem/tap/domain/twins/TwinCardsManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/twins/TwinCardsManager.kt @@ -15,13 +15,13 @@ import com.tangem.network.common.MoshiConverter import com.tangem.operations.wallet.CreateWalletResponse import com.tangem.tap.common.AssetReader import com.tangem.tap.common.analytics.Analytics -import com.tangem.tap.common.analytics.AnalyticsHandler +import com.tangem.tap.common.analytics.GlobalAnalyticsHandler import com.tangem.tap.tangemSdkManager class TwinCardsManager( private val card: Card, assetReader: AssetReader, - val analyticsHandler: AnalyticsHandler? + val analyticsHandler: GlobalAnalyticsHandler? ) { private val currentCardId: String = card.cardId From d17d9e35bf46b4203666d46578b35c76a76a0d2e Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 23 Sep 2022 02:11:50 +0300 Subject: [PATCH 6/8] Updated on 2026-08-14 --- app/build.gradle | 3 +- .../java/com/tangem/tap/TapApplication.kt | 20 +-- .../analytics/AmplitudeAnalyticsHandler.kt | 56 -------- .../common/analytics/AnalyticsEventHandler.kt | 66 +++++++++ .../analytics/AnalyticsEventHandlerBuilder.kt | 66 +++++++++ .../tap/common/analytics/AnalyticsHandler.kt | 93 ------------- .../analytics/AppsFlyerAnalyticsHandler.kt | 65 --------- .../analytics/FirebaseAnalyticsHandler.kt | 88 ------------ .../analytics/GlobalAnalyticsHandler.kt | 128 +++++++++++------- .../amplitude/AmplitudeAnalyticsHandler.kt | 43 ++++++ .../appsFlyer/AppsFlyerAnalyticsHandler.kt | 67 +++++++++ .../firebase/FirebaseAnalyticsHandler.kt | 127 +++++++++++++++++ .../tap/common/extensions/AnalyticsHandler.kt | 51 ------- .../tap/common/redux/global/GlobalAction.kt | 4 +- .../tap/common/redux/global/GlobalState.kt | 4 +- .../tap/common/shop/TangemShopService.kt | 6 +- .../com/tangem/tap/domain/TangemSdkManager.kt | 20 +-- .../tap/domain/twins/TwinCardsManager.kt | 10 +- .../walletconnect/WalletConnectManager.kt | 1 + .../walletconnect/WalletConnectSdkHelper.kt | 9 +- .../details/redux/DetailsMiddleware.kt | 8 +- .../tap/features/home/redux/HomeMiddleware.kt | 2 +- .../send/redux/middlewares/SendMiddleware.kt | 7 +- .../redux/middlewares/WalletMiddleware.kt | 2 +- .../redux/middlewares/WarningsMiddleware.kt | 2 +- .../ui/adapters/WarningMessagesAdapter.kt | 6 +- .../main/java/com/tangem/common/Converter.kt | 8 ++ .../com/tangem/domain/common/LogConfig.kt | 7 + 28 files changed, 514 insertions(+), 455 deletions(-) delete mode 100644 app/src/main/java/com/tangem/tap/common/analytics/AmplitudeAnalyticsHandler.kt create mode 100644 app/src/main/java/com/tangem/tap/common/analytics/AnalyticsEventHandler.kt create mode 100644 app/src/main/java/com/tangem/tap/common/analytics/AnalyticsEventHandlerBuilder.kt delete mode 100644 app/src/main/java/com/tangem/tap/common/analytics/AnalyticsHandler.kt delete mode 100644 app/src/main/java/com/tangem/tap/common/analytics/AppsFlyerAnalyticsHandler.kt delete mode 100644 app/src/main/java/com/tangem/tap/common/analytics/FirebaseAnalyticsHandler.kt create mode 100644 app/src/main/java/com/tangem/tap/common/analytics/handlers/amplitude/AmplitudeAnalyticsHandler.kt create mode 100644 app/src/main/java/com/tangem/tap/common/analytics/handlers/appsFlyer/AppsFlyerAnalyticsHandler.kt create mode 100644 app/src/main/java/com/tangem/tap/common/analytics/handlers/firebase/FirebaseAnalyticsHandler.kt delete mode 100644 app/src/main/java/com/tangem/tap/common/extensions/AnalyticsHandler.kt create mode 100644 common/src/main/java/com/tangem/common/Converter.kt diff --git a/app/build.gradle b/app/build.gradle index 1ba0960f0b..d517123715 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -122,9 +122,8 @@ dependencies { // Firebase implementation platform('com.google.firebase:firebase-bom:26.0.0') - implementation 'com.google.firebase:firebase-crashlytics' - implementation 'com.google.firebase:firebase-config-ktx' implementation 'com.google.firebase:firebase-analytics-ktx' + implementation 'com.google.firebase:firebase-crashlytics-ktx' // Analytics implementation 'com.appsflyer:af-android-sdk:6.5.1' diff --git a/app/src/main/java/com/tangem/tap/TapApplication.kt b/app/src/main/java/com/tangem/tap/TapApplication.kt index 22165f40d0..8a15017fe3 100644 --- a/app/src/main/java/com/tangem/tap/TapApplication.kt +++ b/app/src/main/java/com/tangem/tap/TapApplication.kt @@ -5,16 +5,16 @@ import android.content.Context import android.content.pm.PackageManager import coil.ImageLoader import coil.ImageLoaderFactory -import com.appsflyer.AppsFlyerLib import com.tangem.Log import com.tangem.LogFormat import com.tangem.blockchain.network.BlockchainSdkRetrofitBuilder +import com.tangem.common.json.MoshiJsonConverter import com.tangem.domain.DomainLayer import com.tangem.domain.common.LogConfig import com.tangem.network.common.MoshiConverter import com.tangem.tap.common.AndroidAssetReader import com.tangem.tap.common.AssetReader -import com.tangem.tap.common.analytics.GlobalAnalyticsHandler +import com.tangem.tap.common.analytics.AnalyticsEventHandlerBuilder import com.tangem.tap.common.feedback.AdditionalFeedbackInfo import com.tangem.tap.common.feedback.FeedbackManager import com.tangem.tap.common.images.createCoilImageLoader @@ -95,15 +95,19 @@ class TapApplication : Application(), ImageLoaderFactory { private fun initWithConfigDependency(config: Config) { shopService = TangemShopService(this, config.shopify!!) - initAppsFlyer(this, config) + initAnalytics(this, config) initFeedbackManager(this, preferencesStorage, config) } - private fun initAppsFlyer(context: Context, config: Config) { - AppsFlyerLib.getInstance().init(config.appsFlyerDevKey, null, context) - AppsFlyerLib.getInstance().start(context) - val analyticsHandler = GlobalAnalyticsHandler.createDefaultAnalyticHandlers(context) - store.dispatch(GlobalAction.SetGlobalAnalyticsHandler(analyticsHandler)) + private fun initAnalytics(application: Application, config: Config) { + val globalAnalyticsHandler = AnalyticsEventHandlerBuilder( + application = application, + config = config, + isDebug = BuildConfig.DEBUG, + logConfig = LogConfig.analyticsHandlers, + jsonConverter = MoshiJsonConverter.INSTANCE, + ).default() + store.dispatch(GlobalAction.SetGlobalAnalyticsHandler(globalAnalyticsHandler)) } private fun initFeedbackManager(context: Context, preferencesStorage: PreferencesStorage, config: Config) { diff --git a/app/src/main/java/com/tangem/tap/common/analytics/AmplitudeAnalyticsHandler.kt b/app/src/main/java/com/tangem/tap/common/analytics/AmplitudeAnalyticsHandler.kt deleted file mode 100644 index b68a9a3eb5..0000000000 --- a/app/src/main/java/com/tangem/tap/common/analytics/AmplitudeAnalyticsHandler.kt +++ /dev/null @@ -1,56 +0,0 @@ -package com.tangem.tap.common.analytics - -import com.amplitude.api.Amplitude -import com.amplitude.api.AmplitudeClient -import com.shopify.buy3.Storefront -import com.tangem.common.card.Card -import com.tangem.common.core.TangemSdkError -import org.json.JSONObject - -class AmplitudeAnalyticsHandler : AnalyticsHandler() { - - private val client: AmplitudeClient = Amplitude.getInstance() - - override fun triggerEvent( - event: AnalyticsEvent, - card: Card?, - blockchain: String?, - params: Map - ) { - client.logEvent(event.event, prepareParams(card, blockchain, params).toJson()) - } - - override fun triggerEvent(event: String, params: Map) { - client.logEvent(event, params.toJson()) - } - - override fun logCardSdkError( - error: TangemSdkError, - actionToLog: Analytics.ActionToLog, - parameters: Map?, - card: Card? - ) { - //TODO - } - - override fun logError(error: Throwable, params: Map) { - //TODO - } - - override fun getOrderEvent(): String { - //TODO - return "" - } - - override fun getOrderParams(order: Storefront.Order): Map { - //TODO - return emptyMap() - } - - private fun Map.toJson(): JSONObject { - val json = JSONObject() - this.map { json.put(it.key, it.value) } - return json - } - -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/analytics/AnalyticsEventHandler.kt b/app/src/main/java/com/tangem/tap/common/analytics/AnalyticsEventHandler.kt new file mode 100644 index 0000000000..d973b4fa2c --- /dev/null +++ b/app/src/main/java/com/tangem/tap/common/analytics/AnalyticsEventHandler.kt @@ -0,0 +1,66 @@ +package com.tangem.tap.common.analytics + +import com.shopify.buy3.Storefront +import com.tangem.blockchain.common.BlockchainError +import com.tangem.common.card.Card +import com.tangem.common.core.TangemSdkError +import com.tangem.tap.common.extensions.filterNotNull + +interface EventHandler { + fun handleEvent( + event: String, + params: Map = emptyMap(), + ) +} + +interface AnalyticsEventHandler : EventHandler { + fun handleAnalyticsEvent( + event: AnalyticsEvent, + params: Map = emptyMap(), + card: Card? = null, + blockchain: String? = null, + ) + + fun prepareParams( + card: Card?, + blockchain: String? = null, + params: Map = emptyMap(), + ): Map { + return mapOf( + AnalyticsParam.FIRMWARE.param to card?.firmwareVersion?.stringValue, + AnalyticsParam.BATCH_ID.param to card?.batchId, + AnalyticsParam.BLOCKCHAIN.param to blockchain, + ).filterNotNull() + params + } +} + +interface ShopifyOrderEventHandler { + fun handleShopifyOrderEvent(order: Storefront.Order) +} + +interface ErrorEventHandler { + fun handleErrorEvent( + error: Throwable, + params: Map = emptyMap(), + ) +} + +interface SdkErrorEventHandler : CardSdkErrorEventHandler, BlockchainSdkErrorEventHandler + +interface CardSdkErrorEventHandler { + fun handleCardSdkErrorEvent( + error: TangemSdkError, + action: Analytics.ActionToLog, + params: Map = emptyMap(), + card: Card? = null, + ) +} + +interface BlockchainSdkErrorEventHandler { + fun handleBlockchainSdkErrorEvent( + error: BlockchainError, + action: Analytics.ActionToLog, + params: Map = mapOf(), + card: Card? = null, + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/analytics/AnalyticsEventHandlerBuilder.kt b/app/src/main/java/com/tangem/tap/common/analytics/AnalyticsEventHandlerBuilder.kt new file mode 100644 index 0000000000..e5a2ec3283 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/common/analytics/AnalyticsEventHandlerBuilder.kt @@ -0,0 +1,66 @@ +package com.tangem.tap.common.analytics + +import android.app.Application +import com.tangem.common.json.MoshiJsonConverter +import com.tangem.domain.common.AnalyticsHandlersLogConfig +import com.tangem.tap.common.analytics.handlers.amplitude.AmplitudeAnalyticsHandler +import com.tangem.tap.common.analytics.handlers.appsFlyer.AppsFlyerAnalyticsHandler +import com.tangem.tap.common.analytics.handlers.firebase.FirebaseAnalyticsHandler +import com.tangem.tap.domain.configurable.config.Config + +/** +[REDACTED_AUTHOR] + */ +class AnalyticsEventHandlerBuilder( + private val application: Application, + private val config: Config, + private val isDebug: Boolean, + private val logConfig: AnalyticsHandlersLogConfig, + private val jsonConverter: MoshiJsonConverter, +) { + + private val analyticHandlers = mutableListOf() + + fun build(): GlobalAnalyticsEventHandler { + return GlobalAnalyticsHandler(analyticHandlers.toList()) + } + + fun default(): GlobalAnalyticsEventHandler { + addFirebaseAnalytics() + addAppsFlyer() + addAmplitude() + + return GlobalAnalyticsHandler(analyticHandlers.toList()) + } + + fun addFirebaseAnalytics(): AnalyticsEventHandlerBuilder { + if (isDebug) { + addEventLogger(logConfig.firebase, FirebaseAnalyticsHandler::class.java.simpleName) + } else { + analyticHandlers.add(FirebaseAnalyticsHandler()) + } + return this + } + + fun addAppsFlyer(): AnalyticsEventHandlerBuilder { + if (isDebug) { + addEventLogger(logConfig.appsFlyer, AppsFlyerAnalyticsHandler::class.java.simpleName) + } else { + analyticHandlers.add(AppsFlyerAnalyticsHandler(application, config.appsFlyerDevKey)) + } + return this + } + + fun addAmplitude(): AnalyticsEventHandlerBuilder { + if (isDebug) { + addEventLogger(logConfig.amplitude, AmplitudeAnalyticsHandler::class.java.simpleName) + } else { + analyticHandlers.add(AmplitudeAnalyticsHandler(application, config.amplitudeApiKey)) + } + return this + } + + private fun addEventLogger(logsEnabled: Boolean, name: String) { + // if (logsEnabled) analyticHandlers.add(AnalyticsLogsPrinter(name, jsonConverter)) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/analytics/AnalyticsHandler.kt b/app/src/main/java/com/tangem/tap/common/analytics/AnalyticsHandler.kt deleted file mode 100644 index 5386779df4..0000000000 --- a/app/src/main/java/com/tangem/tap/common/analytics/AnalyticsHandler.kt +++ /dev/null @@ -1,93 +0,0 @@ -package com.tangem.tap.common.analytics - -import com.shopify.buy3.Storefront -import com.tangem.common.card.Card -import com.tangem.common.core.TangemSdkError -import com.tangem.tap.common.extensions.filterNotNull - - -abstract class AnalyticsHandler { - abstract fun triggerEvent( - event: AnalyticsEvent, - card: Card? = null, - blockchain: String? = null, - params: Map = emptyMap() - ) - - abstract fun triggerEvent( - event: String, - params: Map - ) - - abstract fun logCardSdkError( - error: TangemSdkError, - actionToLog: Analytics.ActionToLog, - parameters: Map? = null, - card: Card? = null - ) - - abstract fun logError( - error: Throwable, - params: Map = emptyMap() - ) - - protected fun prepareParams( - card: Card?, - blockchain: String? = null, - params: Map = emptyMap() - ): Map { - return mapOf( - AnalyticsParam.FIRMWARE.param to card?.firmwareVersion?.stringValue, - AnalyticsParam.BATCH_ID.param to card?.batchId, - AnalyticsParam.BLOCKCHAIN.param to blockchain, - ).filterNotNull() + params - } - - fun logWcEvent(event: Analytics.WcAnalyticsEvent) { - when (event) { - is Analytics.WcAnalyticsEvent.Action -> { - triggerEvent( - event = AnalyticsEvent.WC_SUCCESS_RESPONSE, - params = mapOf( - AnalyticsParam.WALLET_CONNECT_ACTION.param to event.action.name - ) - ) - } - is Analytics.WcAnalyticsEvent.Error -> { - val params = mapOf( - AnalyticsParam.WALLET_CONNECT_ACTION.param to event.action?.name, - AnalyticsParam.ERROR_DESCRIPTION.param to event.error.message - ) - .filterNotNull() - logError(event.error, params) - } - is Analytics.WcAnalyticsEvent.InvalidRequest -> - triggerEvent( - event = AnalyticsEvent.WC_INVALID_REQUEST, - params = mapOf( - AnalyticsParam.WALLET_CONNECT_REQUEST.param to event.json - ).filterNotNull() - ) - is Analytics.WcAnalyticsEvent.Session -> { - val analyticsEvent = when (event.event) { - Analytics.WcSessionEvent.Disconnect -> AnalyticsEvent.WC_SESSION_DISCONNECTED - Analytics.WcSessionEvent.Connect -> AnalyticsEvent.WC_NEW_SESSION - } - triggerEvent( - event = analyticsEvent, - params = mapOf( - AnalyticsParam.WALLET_CONNECT_DAPP_URL.param to event.url - ).filterNotNull() - ) - } - } - } - - fun logShopifyOrder(order: Storefront.Order) { - triggerEvent(getOrderEvent(), getOrderParams(order)) - } - - protected abstract fun getOrderEvent(): String - - protected abstract fun getOrderParams(order: Storefront.Order): Map -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/analytics/AppsFlyerAnalyticsHandler.kt b/app/src/main/java/com/tangem/tap/common/analytics/AppsFlyerAnalyticsHandler.kt deleted file mode 100644 index 3ca690a971..0000000000 --- a/app/src/main/java/com/tangem/tap/common/analytics/AppsFlyerAnalyticsHandler.kt +++ /dev/null @@ -1,65 +0,0 @@ -package com.tangem.tap.common.analytics - -import android.content.Context -import com.appsflyer.AFInAppEventParameterName -import com.appsflyer.AFInAppEventType -import com.appsflyer.AppsFlyerLib -import com.shopify.buy3.Storefront -import com.tangem.common.card.Card -import com.tangem.common.core.TangemSdkError - -class AppsFlyerAnalyticsHandler(val context: Context): AnalyticsHandler() { - - override fun triggerEvent( - event: AnalyticsEvent, - card: Card?, - blockchain: String?, - params: Map - ) { - AppsFlyerLib.getInstance().logEvent( - context, - event.event, - prepareParams(card, blockchain, params) - ) - } - - override fun logCardSdkError( - error: TangemSdkError, - actionToLog: Analytics.ActionToLog, - parameters: Map?, - card: Card? - ) { - //TODO - } - - override fun logError(error: Throwable, params: Map) { - //TODO - } - - override fun getOrderEvent(): String = AFInAppEventType.PURCHASE - - override fun getOrderParams(order: Storefront.Order): Map { - - val sku = order.lineItems.edges.firstOrNull()?.node?.variant?.sku ?: "unknown" - - val discountCode = - (order.discountApplications.edges.firstOrNull()?.node as? Storefront.DiscountCodeApplication)?.code - - val discountParams = if (discountCode != null ){ - mapOf(AFInAppEventParameterName.COUPON_CODE to discountCode) - } else { - mapOf() - } - - return mapOf( - AFInAppEventParameterName.CONTENT_ID to sku, - AFInAppEventParameterName.REVENUE to order.totalPriceV2.amount, - AFInAppEventParameterName.CURRENCY to order.currencyCode.name - ) + discountParams - } - - override fun triggerEvent(event: String, params: Map) { - AppsFlyerLib.getInstance().logEvent(context, event, params) - } - -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/analytics/FirebaseAnalyticsHandler.kt b/app/src/main/java/com/tangem/tap/common/analytics/FirebaseAnalyticsHandler.kt deleted file mode 100644 index e691c2239f..0000000000 --- a/app/src/main/java/com/tangem/tap/common/analytics/FirebaseAnalyticsHandler.kt +++ /dev/null @@ -1,88 +0,0 @@ -package com.tangem.tap.common.analytics - -import android.os.Bundle -import androidx.core.os.bundleOf -import com.google.firebase.analytics.FirebaseAnalytics -import com.google.firebase.analytics.ktx.analytics -import com.google.firebase.crashlytics.FirebaseCrashlytics -import com.google.firebase.ktx.Firebase -import com.shopify.buy3.Storefront -import com.tangem.common.card.Card -import com.tangem.common.core.TangemSdkError - -object FirebaseAnalyticsHandler : AnalyticsHandler() { - override fun triggerEvent( - event: AnalyticsEvent, - card: Card?, - blockchain: String?, - params: Map - ) { - val fullParams = prepareParams(card, blockchain, params) - Firebase.analytics.logEvent(event.event, fullParams.toBundle()) - } - - fun logException(name: String, throwable: Throwable) { - Firebase.analytics.logEvent(name, bundleOf( - "message" to (throwable.message ?: "none"), - "cause_message" to (throwable.cause?.message ?: "none"), - "stack_trace" to throwable.stackTraceToString() - )) - } - - private fun Map.toBundle(): Bundle { - return bundleOf(*this.toList().toTypedArray()) - } - - override fun logCardSdkError( - error: TangemSdkError, - actionToLog: Analytics.ActionToLog, - parameters: Map?, - card: Card?, - ) { - if (error is TangemSdkError.UserCancelled) return - - val params = parameters?.toMutableMap() ?: mutableMapOf() - if (card != null) params + prepareParams(card) - params[AnalyticsParam.ACTION] = actionToLog.key - params[AnalyticsParam.ERROR_CODE] = error.code.toString() - params[AnalyticsParam.ERROR_DESCRIPTION] = error.javaClass.simpleName - params[AnalyticsParam.ERROR_KEY] = "TangemSdkError" - - params.forEach { - FirebaseCrashlytics.getInstance().setCustomKey(it.key.param, it.value) - } - val cardError = TangemSdk.map(error) - FirebaseCrashlytics.getInstance().recordException(cardError) - } - - override fun logError(error: Throwable, params: Map) { - params.forEach { (key, value) -> FirebaseCrashlytics.getInstance().setCustomKey(key, value) } - FirebaseCrashlytics.getInstance().recordException(error) - } - - override fun getOrderEvent(): String = FirebaseAnalytics.Event.PURCHASE - - override fun getOrderParams(order: Storefront.Order): Map { - - val sku = order.lineItems.edges.firstOrNull()?.node?.variant?.sku ?: "unknown" - - val discountCode = - (order.discountApplications.edges.firstOrNull()?.node as? Storefront.DiscountCodeApplication)?.code - - val discountParams = if (discountCode != null ){ - mapOf(FirebaseAnalytics.Param.DISCOUNT to discountCode) - } else { - mapOf() - } - - return mapOf( - FirebaseAnalytics.Param.ITEM_ID to sku, - FirebaseAnalytics.Param.VALUE to order.totalPriceV2.amount, - FirebaseAnalytics.Param.CURRENCY to order.currencyCode.name - ) + discountParams - } - - override fun triggerEvent(event: String, params: Map) { - Firebase.analytics.logEvent(event, params.toBundle()) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/analytics/GlobalAnalyticsHandler.kt b/app/src/main/java/com/tangem/tap/common/analytics/GlobalAnalyticsHandler.kt index 83b77c56e8..cd28857ae7 100644 --- a/app/src/main/java/com/tangem/tap/common/analytics/GlobalAnalyticsHandler.kt +++ b/app/src/main/java/com/tangem/tap/common/analytics/GlobalAnalyticsHandler.kt @@ -1,77 +1,103 @@ package com.tangem.tap.common.analytics -import android.content.Context -import com.amplitude.api.Amplitude -import com.appsflyer.AppsFlyerLib import com.shopify.buy3.Storefront +import com.tangem.blockchain.common.BlockchainError import com.tangem.common.card.Card import com.tangem.common.core.TangemSdkError -import com.tangem.tap.store +import com.tangem.tap.common.extensions.filterNotNull -class GlobalAnalyticsHandler(val analyticsHandlers: List) : - AnalyticsHandler() { - override fun triggerEvent( +interface GlobalAnalyticsEventHandler : AnalyticsEventHandler, + ShopifyOrderEventHandler, + ErrorEventHandler, + SdkErrorEventHandler + +class GlobalAnalyticsHandler( + private val analyticsHandlers: List, +) : GlobalAnalyticsEventHandler { + + override fun handleEvent(event: String, params: Map) { + analyticsHandlers.forEach { it.handleEvent(event, params) } + } + + override fun handleAnalyticsEvent( event: AnalyticsEvent, + params: Map, card: Card?, blockchain: String?, - params: Map ) { - analyticsHandlers.forEach { it.triggerEvent(event, card, blockchain, params) } + analyticsHandlers.forEach { it.handleAnalyticsEvent(event, params, card, blockchain) } } - override fun triggerEvent(event: String, params: Map) { - analyticsHandlers.forEach { it.triggerEvent(event, params) } + override fun handleShopifyOrderEvent(order: Storefront.Order) { + analyticsHandlers.filterIsInstance().forEach { + it.handleShopifyOrderEvent(order) + } } - override fun logCardSdkError( + override fun handleErrorEvent(error: Throwable, params: Map) { + analyticsHandlers.filterIsInstance().forEach { + it.handleErrorEvent(error, params) + } + } + + override fun handleCardSdkErrorEvent( error: TangemSdkError, - actionToLog: Analytics.ActionToLog, - parameters: Map?, - card: Card? + action: Analytics.ActionToLog, + params: Map, + card: Card?, ) { - analyticsHandlers.forEach { it.logCardSdkError(error, actionToLog, parameters, card) } + analyticsHandlers.filterIsInstance().forEach { + it.handleCardSdkErrorEvent(error, action, params, card) + } } - override fun logError(error: Throwable, params: Map) { - analyticsHandlers.forEach { it.logError(error, params) } + override fun handleBlockchainSdkErrorEvent( + error: BlockchainError, + action: Analytics.ActionToLog, + params: Map, + card: Card?, + ) { + analyticsHandlers.filterIsInstance().forEach { + it.handleBlockchainSdkErrorEvent(error, action, params, card) + } } +} - override fun getOrderEvent(): String { - return "" - } - - override fun getOrderParams(order: Storefront.Order): Map { - return emptyMap() - } - - companion object { - fun createDefaultAnalyticHandlers(context: Context): GlobalAnalyticsHandler { - return GlobalAnalyticsHandler( - listOf( - FirebaseAnalyticsHandler, - AppsFlyerAnalyticsHandler(context), - AmplitudeAnalyticsHandler() - ) +fun GlobalAnalyticsEventHandler.logWcEvent(event: Analytics.WcAnalyticsEvent) { + when (event) { + is Analytics.WcAnalyticsEvent.Action -> { + handleAnalyticsEvent( + event = AnalyticsEvent.WC_SUCCESS_RESPONSE, + params = mapOf( + AnalyticsParam.WALLET_CONNECT_ACTION.param to event.action.name, + ), ) } - - private fun initAnalytics(context: Context) { - initAppsFlyer(context) - initAmplitude(context) + is Analytics.WcAnalyticsEvent.Error -> { + val params = mapOf( + AnalyticsParam.WALLET_CONNECT_ACTION.param to event.action?.name, + AnalyticsParam.ERROR_DESCRIPTION.param to event.error.message, + ).filterNotNull() + handleErrorEvent(event.error, params) } - - private fun initAppsFlyer(context: Context) { - val devKey = store.state.globalState.configManager?.config?.appsFlyerDevKey ?: return - AppsFlyerLib.getInstance().init(devKey, null, context) - AppsFlyerLib.getInstance().start(context) - - } - - private fun initAmplitude(context: Context) { - val apiKey = store.state.globalState.configManager?.config?.amplitudeApiKey ?: return - Amplitude.getInstance() - .initialize(context.applicationContext, apiKey) - .enableForegroundTracking(context) + is Analytics.WcAnalyticsEvent.InvalidRequest -> + handleAnalyticsEvent( + event = AnalyticsEvent.WC_INVALID_REQUEST, + params = mapOf( + AnalyticsParam.WALLET_CONNECT_REQUEST.param to event.json, + ).filterNotNull(), + ) + is Analytics.WcAnalyticsEvent.Session -> { + val analyticsEvent = when (event.event) { + Analytics.WcSessionEvent.Disconnect -> AnalyticsEvent.WC_SESSION_DISCONNECTED + Analytics.WcSessionEvent.Connect -> AnalyticsEvent.WC_NEW_SESSION + } + handleAnalyticsEvent( + event = analyticsEvent, + params = mapOf( + AnalyticsParam.WALLET_CONNECT_DAPP_URL.param to event.url, + ).filterNotNull(), + ) } } } \ No newline at end of file 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 new file mode 100644 index 0000000000..22baff0a2f --- /dev/null +++ b/app/src/main/java/com/tangem/tap/common/analytics/handlers/amplitude/AmplitudeAnalyticsHandler.kt @@ -0,0 +1,43 @@ +package com.tangem.tap.common.analytics.handlers.amplitude + +import android.app.Application +import com.amplitude.api.Amplitude +import com.amplitude.api.AmplitudeClient +import com.tangem.common.Converter +import com.tangem.common.card.Card +import com.tangem.tap.common.analytics.AnalyticsEvent +import com.tangem.tap.common.analytics.AnalyticsEventHandler +import org.json.JSONObject + +class AmplitudeAnalyticsHandler( + application: Application, + key: String, +) : AnalyticsEventHandler { + + private val client: AmplitudeClient = Amplitude.getInstance() + + init { + client.initialize(application, key) + client.enableForegroundTracking(application) + } + + override fun handleEvent(event: String, params: Map) { + client.logEvent(event, ParamsJSONObjectConverter().convert(params)) + } + + override fun handleAnalyticsEvent( + event: AnalyticsEvent, + params: Map, + card: Card?, + blockchain: String?, + ) { + handleEvent(event.event, prepareParams(card, blockchain, params)) + } + + class ParamsJSONObjectConverter : Converter, JSONObject> { + override fun convert(value: Map): JSONObject = JSONObject().apply { + value.forEach { this.put(it.key, it.value) } + } + } +} + 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 new file mode 100644 index 0000000000..8b14fc2eba --- /dev/null +++ b/app/src/main/java/com/tangem/tap/common/analytics/handlers/appsFlyer/AppsFlyerAnalyticsHandler.kt @@ -0,0 +1,67 @@ +package com.tangem.tap.common.analytics.handlers.appsFlyer + +import android.content.Context +import com.appsflyer.AFInAppEventParameterName +import com.appsflyer.AFInAppEventType +import com.appsflyer.AppsFlyerLib +import com.shopify.buy3.Storefront +import com.tangem.common.Converter +import com.tangem.common.card.Card +import com.tangem.tap.common.analytics.AnalyticsEvent +import com.tangem.tap.common.analytics.AnalyticsEventHandler +import com.tangem.tap.common.analytics.ShopifyOrderEventHandler + +class AppsFlyerAnalyticsHandler( + private val context: Context, + key: String, +) : AnalyticsEventHandler, ShopifyOrderEventHandler { + + private val appsFlyerLib: AppsFlyerLib = AppsFlyerLib.getInstance() + + init { + appsFlyerLib.init(key, null, context) + appsFlyerLib.start(context) + } + + override fun handleEvent(event: String, params: Map) { + appsFlyerLib.logEvent(context, event, params) + } + + override fun handleAnalyticsEvent( + event: AnalyticsEvent, + params: Map, + card: Card?, + blockchain: String?, + ) { + handleEvent(event.event, prepareParams(card, blockchain, params)) + } + + override fun handleShopifyOrderEvent(order: Storefront.Order) { + handleEvent(ORDER_EVENT, OrderToParamsConverter().convert(order)) + } + + companion object { + const val ORDER_EVENT = AFInAppEventType.PURCHASE + } + + class OrderToParamsConverter : Converter> { + override fun convert(value: Storefront.Order): Map { + val sku = value.lineItems.edges.firstOrNull()?.node?.variant?.sku ?: "unknown" + + val discountCode = + (value.discountApplications.edges.firstOrNull()?.node as? Storefront.DiscountCodeApplication)?.code + + val discountParams = if (discountCode != null) { + mapOf(AFInAppEventParameterName.COUPON_CODE to discountCode) + } else { + mapOf() + } + + return mapOf( + AFInAppEventParameterName.CONTENT_ID to sku, + AFInAppEventParameterName.REVENUE to value.totalPriceV2.amount, + AFInAppEventParameterName.CURRENCY to value.currencyCode.name, + ) + discountParams + } + } +} \ 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 new file mode 100644 index 0000000000..b2be460eb5 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/common/analytics/handlers/firebase/FirebaseAnalyticsHandler.kt @@ -0,0 +1,127 @@ +package com.tangem.tap.common.analytics.handlers.firebase + +import android.os.Bundle +import androidx.core.os.bundleOf +import com.google.firebase.analytics.FirebaseAnalytics +import com.google.firebase.analytics.ktx.analytics +import com.google.firebase.crashlytics.ktx.crashlytics +import com.google.firebase.ktx.Firebase +import com.shopify.buy3.Storefront +import com.tangem.blockchain.common.BlockchainError +import com.tangem.blockchain.common.BlockchainSdkError +import com.tangem.common.Converter +import com.tangem.common.card.Card +import com.tangem.common.core.TangemSdkError +import com.tangem.tap.common.analytics.Analytics +import com.tangem.tap.common.analytics.AnalyticsEvent +import com.tangem.tap.common.analytics.AnalyticsEventHandler +import com.tangem.tap.common.analytics.AnalyticsParam +import com.tangem.tap.common.analytics.ErrorEventHandler +import com.tangem.tap.common.analytics.SdkErrorEventHandler +import com.tangem.tap.common.analytics.ShopifyOrderEventHandler +import com.tangem.tap.common.analytics.TangemSdk +import com.tangem.tap.features.demo.DemoTransactionSender + +class FirebaseAnalyticsHandler : AnalyticsEventHandler, ShopifyOrderEventHandler, ErrorEventHandler, + SdkErrorEventHandler { + + private val fbAnalytics = Firebase.analytics + private val fbCrashlytics = Firebase.crashlytics + + override fun handleEvent(event: String, params: Map) { + fbAnalytics.logEvent(event, params.toBundle()) + } + + override fun handleAnalyticsEvent( + event: AnalyticsEvent, + params: Map, + card: Card?, + blockchain: String?, + ) { + handleEvent(event.event, prepareParams(card, blockchain, params)) + } + + override fun handleShopifyOrderEvent(order: Storefront.Order) { + handleEvent(ORDER_EVENT, OrderToParamsConverter().convert(order)) + } + + override fun handleErrorEvent(error: Throwable, params: Map) { + params.forEach { (key, value) -> fbCrashlytics.setCustomKey(key, value) } + fbCrashlytics.recordException(error) + } + + override fun handleCardSdkErrorEvent( + error: TangemSdkError, + action: Analytics.ActionToLog, + params: Map, + card: Card?, + ) { + if (error is TangemSdkError.UserCancelled) return + + val mutableParams = params.toMutableMap() + if (card != null) mutableParams + prepareParams(card) + + mutableParams[AnalyticsParam.ACTION] = action.key + mutableParams[AnalyticsParam.ERROR_CODE] = error.code.toString() + mutableParams[AnalyticsParam.ERROR_DESCRIPTION] = error.javaClass.simpleName + mutableParams[AnalyticsParam.ERROR_KEY] = "TangemSdkError" + + mutableParams.forEach { fbCrashlytics.setCustomKey(it.key.param, it.value) } + fbCrashlytics.recordException(TangemSdk.map(error)) + } + + override fun handleBlockchainSdkErrorEvent( + error: BlockchainError, + action: Analytics.ActionToLog, + params: Map, + card: Card?, + ) { + val blockchainSdkError = (error as? BlockchainSdkError) ?: return + + if (blockchainSdkError is BlockchainSdkError.WrappedTangemError) { + val tangemSdkError = (blockchainSdkError.tangemError as? TangemSdkError) ?: return + handleCardSdkErrorEvent(tangemSdkError, action, params, card) + return + } + + if (blockchainSdkError.customMessage.contains(DemoTransactionSender.ID)) return + + val mutableParams = params.toMutableMap() + mutableParams[AnalyticsParam.ACTION] = action.key + mutableParams[AnalyticsParam.ERROR_CODE] = error.code.toString() + mutableParams[AnalyticsParam.ERROR_DESCRIPTION] = "${error.javaClass.simpleName}: ${error.customMessage}" + mutableParams[AnalyticsParam.ERROR_KEY] = "BlockchainSdkError" + + mutableParams.forEach { fbCrashlytics.setCustomKey(it.key.param, it.value) } + fbCrashlytics.recordException(error) + } + + private fun Map.toBundle(): Bundle { + return bundleOf(*this.toList().toTypedArray()) + } + + companion object { + const val ORDER_EVENT = FirebaseAnalytics.Event.PURCHASE + } + + class OrderToParamsConverter : Converter> { + override fun convert(value: Storefront.Order): Map { + val sku = value.lineItems.edges.firstOrNull()?.node?.variant?.sku ?: "unknown" + + val discountCode = + (value.discountApplications.edges.firstOrNull()?.node as? Storefront.DiscountCodeApplication)?.code + + val discountParams = if (discountCode != null) { + mapOf(FirebaseAnalytics.Param.DISCOUNT to discountCode) + } else { + mapOf() + } + + return mapOf( + FirebaseAnalytics.Param.ITEM_ID to sku, + FirebaseAnalytics.Param.VALUE to value.totalPriceV2.amount, + FirebaseAnalytics.Param.CURRENCY to value.currencyCode.name, + ) + discountParams + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/extensions/AnalyticsHandler.kt b/app/src/main/java/com/tangem/tap/common/extensions/AnalyticsHandler.kt deleted file mode 100644 index f2897e2b63..0000000000 --- a/app/src/main/java/com/tangem/tap/common/extensions/AnalyticsHandler.kt +++ /dev/null @@ -1,51 +0,0 @@ -package com.tangem.tap.common.extensions - -import com.google.firebase.crashlytics.FirebaseCrashlytics -import com.tangem.blockchain.common.BlockchainError -import com.tangem.blockchain.common.BlockchainSdkError -import com.tangem.common.card.Card -import com.tangem.common.core.TangemSdkError -import com.tangem.tap.common.analytics.Analytics -import com.tangem.tap.common.analytics.AnalyticsHandler -import com.tangem.tap.common.analytics.AnalyticsParam -import com.tangem.tap.features.demo.DemoTransactionSender - -/** -[REDACTED_AUTHOR] - */ -fun AnalyticsHandler.logSendTransactionError( - error: BlockchainError, - action: Analytics.ActionToLog, - parameters: Map? = mapOf(), - card: Card? = null, -) { - when (val blockchainSdkError = (error as BlockchainSdkError)) { - is BlockchainSdkError.WrappedTangemError -> { - val tangemSdkError = (blockchainSdkError.tangemError as? TangemSdkError) ?: return - - logCardSdkError( - error = tangemSdkError, - actionToLog = action, - parameters = parameters, - card = card, - ) - } - else -> { - when { - blockchainSdkError.customMessage.contains(DemoTransactionSender.ID) -> return - else -> { - val params = parameters?.toMutableMap() ?: mutableMapOf() - params[AnalyticsParam.ACTION] = action.key - params[AnalyticsParam.ERROR_CODE] = error.code.toString() - params[AnalyticsParam.ERROR_DESCRIPTION] = "${error.javaClass.simpleName}: ${error.customMessage}" - params[AnalyticsParam.ERROR_KEY] = "BlockchainSdkError" - - FirebaseCrashlytics.getInstance().apply { - params.forEach { setCustomKey(it.key.param, it.value) } - recordException(error) - } - } - } - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalAction.kt b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalAction.kt index 4e441f768e..8c50713103 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalAction.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalAction.kt @@ -5,7 +5,7 @@ import com.tangem.blockchain.common.WalletManager import com.tangem.common.CompletionResult import com.tangem.common.core.TangemError import com.tangem.domain.common.ScanResponse -import com.tangem.tap.common.analytics.GlobalAnalyticsHandler +import com.tangem.tap.common.analytics.GlobalAnalyticsEventHandler import com.tangem.tap.common.entities.FiatCurrency import com.tangem.tap.common.feedback.FeedbackData import com.tangem.tap.common.feedback.FeedbackManager @@ -75,7 +75,7 @@ sealed class GlobalAction : Action { data class SetConfigManager(val configManager: ConfigManager) : GlobalAction() data class SetWarningManager(val warningManager: WarningMessagesManager) : GlobalAction() data class SetFeedbackManager(val feedbackManager: FeedbackManager) : GlobalAction() - data class SetGlobalAnalyticsHandler(val analyticsHandler: GlobalAnalyticsHandler) : GlobalAction() + data class SetGlobalAnalyticsHandler(val analyticsHandler: GlobalAnalyticsEventHandler) : GlobalAction() data class SendEmail(val feedbackData: FeedbackData) : GlobalAction() data class OpenChat(val feedbackData: FeedbackData) : GlobalAction() diff --git a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalState.kt b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalState.kt index 17c8ed9f76..55b8d535b3 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalState.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalState.kt @@ -1,7 +1,7 @@ package com.tangem.tap.common.redux.global import com.tangem.domain.common.ScanResponse -import com.tangem.tap.common.analytics.GlobalAnalyticsHandler +import com.tangem.tap.common.analytics.GlobalAnalyticsEventHandler import com.tangem.tap.common.entities.FiatCurrency import com.tangem.tap.common.feedback.FeedbackManager import com.tangem.tap.common.redux.StateDialog @@ -27,7 +27,7 @@ data class GlobalState( val dialog: StateDialog? = null, val exchangeManager: CurrencyExchangeManager = CurrencyExchangeManager.dummy(), val resources: AndroidResources = AndroidResources(), - val analyticsHandler: GlobalAnalyticsHandler? = null, + val analyticsHandler: GlobalAnalyticsEventHandler? = null, val userCountryCode: String? = null, ) : StateType diff --git a/app/src/main/java/com/tangem/tap/common/shop/TangemShopService.kt b/app/src/main/java/com/tangem/tap/common/shop/TangemShopService.kt index 6151af3761..c8bd0a5405 100644 --- a/app/src/main/java/com/tangem/tap/common/shop/TangemShopService.kt +++ b/app/src/main/java/com/tangem/tap/common/shop/TangemShopService.kt @@ -5,7 +5,7 @@ import android.content.Intent import com.google.android.gms.wallet.PaymentData import com.shopify.buy3.Storefront import com.tangem.common.extensions.ifNotNull -import com.tangem.tap.common.analytics.GlobalAnalyticsHandler +import com.tangem.tap.common.analytics.GlobalAnalyticsEventHandler import com.tangem.tap.common.extensions.filterNotNull import com.tangem.tap.common.shop.data.ProductType import com.tangem.tap.common.shop.data.TangemProduct @@ -196,10 +196,10 @@ class TangemShopService(application: Application, shopifyShop: ShopifyShop) { return checkouts[productType]!!.webUrl } - suspend fun waitForCheckout(productType: ProductType, analyticsHandler: GlobalAnalyticsHandler?) { + suspend fun waitForCheckout(productType: ProductType, analyticsHandler: GlobalAnalyticsEventHandler?) { val result = shopifyService.checkout(true, checkouts[productType]!!.id) result.onSuccess { - ifNotNull(analyticsHandler, it.order) { handler, order -> handler.logShopifyOrder(order) } + ifNotNull(analyticsHandler, it.order) { handler, order -> handler.handleShopifyOrderEvent(order) } } } diff --git a/app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt b/app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt index 43e318bd82..b4a63f3277 100644 --- a/app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt @@ -28,7 +28,7 @@ import com.tangem.operations.pins.SetUserCodeCommand import com.tangem.tap.common.analytics.Analytics import com.tangem.tap.common.analytics.AnalyticsEvent import com.tangem.tap.common.analytics.AnalyticsParam -import com.tangem.tap.common.analytics.GlobalAnalyticsHandler +import com.tangem.tap.common.analytics.GlobalAnalyticsEventHandler import com.tangem.tap.domain.tasks.CreateWalletAndRescanTask import com.tangem.tap.domain.tasks.product.ResetToFactorySettingsTask import com.tangem.tap.domain.tasks.product.ScanProductTask @@ -44,12 +44,12 @@ import kotlin.coroutines.suspendCoroutine class TangemSdkManager(private val tangemSdk: TangemSdk, private val context: Context) { suspend fun scanProduct( - analyticsHandler: GlobalAnalyticsHandler?, + analyticsHandler: GlobalAnalyticsEventHandler?, userTokensRepository: UserTokensRepository, additionalBlockchainsToDerive: Collection? = null, messageRes: Int? = null, ): CompletionResult { - analyticsHandler?.triggerEvent(AnalyticsEvent.READY_TO_SCAN, null) + analyticsHandler?.handleAnalyticsEvent(AnalyticsEvent.READY_TO_SCAN, card = null) val message = Message(context.getString(messageRes ?: R.string.initial_message_scan_header)) return runTaskAsyncReturnOnMain( @@ -69,30 +69,30 @@ class TangemSdkManager(private val tangemSdk: TangemSdk, private val context: Co } private fun sendScanResultsToAnalytics( - analyticsHandler: GlobalAnalyticsHandler?, + analyticsHandler: GlobalAnalyticsEventHandler?, result: CompletionResult ) { when (result) { is CompletionResult.Success -> { - analyticsHandler?.triggerEvent( + analyticsHandler?.handleAnalyticsEvent( event = AnalyticsEvent.CARD_IS_SCANNED, card = result.data.card, blockchain = result.data.walletData?.blockchain ) if (DemoHelper.isDemoCard(result.data)) { - analyticsHandler?.triggerEvent( + analyticsHandler?.handleAnalyticsEvent( event = AnalyticsEvent.DEMO_MODE_ACTIVATED, + params = mapOf(AnalyticsParam.CARD_ID.param to result.data.card.cardId), card = result.data.card, - blockchain = result.data.walletData?.blockchain, - params = mapOf(AnalyticsParam.CARD_ID.param to result.data.card.cardId) + blockchain = result.data.walletData?.blockchain ) } } is CompletionResult.Failure -> (result.error as? TangemSdkError)?.let { error -> - analyticsHandler?.logCardSdkError( + analyticsHandler?.handleCardSdkErrorEvent( error = error, - actionToLog = Analytics.ActionToLog.Scan + action = Analytics.ActionToLog.Scan ) } } diff --git a/app/src/main/java/com/tangem/tap/domain/twins/TwinCardsManager.kt b/app/src/main/java/com/tangem/tap/domain/twins/TwinCardsManager.kt index d4313cf946..9d7353fb5f 100644 --- a/app/src/main/java/com/tangem/tap/domain/twins/TwinCardsManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/twins/TwinCardsManager.kt @@ -15,13 +15,13 @@ import com.tangem.network.common.MoshiConverter import com.tangem.operations.wallet.CreateWalletResponse import com.tangem.tap.common.AssetReader import com.tangem.tap.common.analytics.Analytics -import com.tangem.tap.common.analytics.GlobalAnalyticsHandler +import com.tangem.tap.common.analytics.GlobalAnalyticsEventHandler import com.tangem.tap.tangemSdkManager class TwinCardsManager( private val card: Card, assetReader: AssetReader, - val analyticsHandler: GlobalAnalyticsHandler? + val analyticsHandler: GlobalAnalyticsEventHandler? ) { private val currentCardId: String = card.cardId @@ -37,7 +37,7 @@ class TwinCardsManager( is CompletionResult.Success -> currentCardPublicKey = response.data.wallet.publicKey.toHexString() is CompletionResult.Failure -> { (response.error as? TangemSdkError)?.let { error -> - analyticsHandler?.logCardSdkError( + analyticsHandler?.handleCardSdkErrorEvent( error, Analytics.ActionToLog.CreateWallet, card = card @@ -67,7 +67,7 @@ class TwinCardsManager( } is CompletionResult.Failure -> { (response.error as? TangemSdkError)?.let { error -> - analyticsHandler?.logCardSdkError( + analyticsHandler?.handleCardSdkErrorEvent( error, Analytics.ActionToLog.CreateWallet, card = card @@ -87,7 +87,7 @@ class TwinCardsManager( is CompletionResult.Success -> Result.Success(response.data) is CompletionResult.Failure -> { (response.error as? TangemSdkError)?.let { error -> - analyticsHandler?.logCardSdkError( + analyticsHandler?.handleCardSdkErrorEvent( error, Analytics.ActionToLog.WriteIssuerData, card = card diff --git a/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectManager.kt b/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectManager.kt index 305f4ec978..8bea1d2aeb 100644 --- a/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectManager.kt @@ -5,6 +5,7 @@ import com.tangem.common.card.EllipticCurve import com.tangem.common.extensions.guard import com.tangem.domain.common.ScanResponse import com.tangem.tap.common.analytics.Analytics +import com.tangem.tap.common.analytics.logWcEvent import com.tangem.tap.common.extensions.dispatchOnMain import com.tangem.tap.common.redux.global.GlobalAction import com.tangem.tap.features.details.redux.walletconnect.WalletConnectAction diff --git a/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectSdkHelper.kt b/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectSdkHelper.kt index 78db9b27fc..482cf3f42b 100644 --- a/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectSdkHelper.kt +++ b/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectSdkHelper.kt @@ -25,7 +25,6 @@ import com.tangem.common.extensions.toHexString import com.tangem.crypto.CryptoUtils import com.tangem.operations.sign.SignHashCommand import com.tangem.tap.common.analytics.Analytics -import com.tangem.tap.common.extensions.logSendTransactionError import com.tangem.tap.common.extensions.safeUpdate import com.tangem.tap.common.extensions.toFormattedString import com.tangem.tap.features.details.redux.walletconnect.WalletConnectSession @@ -144,7 +143,7 @@ class WalletConnectSdkHelper { HEX_PREFIX + data.walletManager.wallet.recentTransactions.last().hash } is SimpleResult.Failure -> { - store.state.globalState.analyticsHandler?.logSendTransactionError( + store.state.globalState.analyticsHandler?.handleBlockchainSdkErrorEvent( result.error, Analytics.ActionToLog.WalletConnectTransaction, ) @@ -174,7 +173,7 @@ class WalletConnectSdkHelper { } is CompletionResult.Failure -> { (result.error as? TangemSdkError)?.let { error -> - store.state.globalState.analyticsHandler?.logCardSdkError( + store.state.globalState.analyticsHandler?.handleCardSdkErrorEvent( error, Analytics.ActionToLog.WalletConnectSign, ) @@ -203,7 +202,7 @@ class WalletConnectSdkHelper { } is CompletionResult.Failure -> { (result.error as? TangemSdkError)?.let { error -> - store.state.globalState.analyticsHandler?.logCardSdkError( + store.state.globalState.analyticsHandler?.handleCardSdkErrorEvent( error, Analytics.ActionToLog.WalletConnectTransaction, ) @@ -279,7 +278,7 @@ class WalletConnectSdkHelper { } is CompletionResult.Failure -> { (result.error as? TangemSdkError)?.let { error -> - store.state.globalState.analyticsHandler?.logCardSdkError( + store.state.globalState.analyticsHandler?.handleCardSdkErrorEvent( error, Analytics.ActionToLog.WalletConnectSign, ) diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsMiddleware.kt b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsMiddleware.kt index 30eebe686a..daf25b5425 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsMiddleware.kt @@ -115,7 +115,7 @@ class DetailsMiddleware { } is CompletionResult.Failure -> { (result.error as? TangemSdkError)?.let { error -> - store.state.globalState.analyticsHandler?.logCardSdkError( + store.state.globalState.analyticsHandler?.handleCardSdkErrorEvent( error, Analytics.ActionToLog.PurgeWallet, card = store.state.detailsState.scanResponse?.card, @@ -159,10 +159,10 @@ class DetailsMiddleware { } is CompletionResult.Failure -> { (result.error as? TangemSdkError)?.let { error -> - store.state.globalState.analyticsHandler?.logCardSdkError( + store.state.globalState.analyticsHandler?.handleCardSdkErrorEvent( error = error, - actionToLog = Analytics.ActionToLog.ChangeSecOptions, - parameters = mapOf( + action = Analytics.ActionToLog.ChangeSecOptions, + params = mapOf( AnalyticsParam.NEW_SECURITY_OPTION to (selectedOption?.name ?: ""), ), diff --git a/app/src/main/java/com/tangem/tap/features/home/redux/HomeMiddleware.kt b/app/src/main/java/com/tangem/tap/features/home/redux/HomeMiddleware.kt index ceb36bc407..e33abf03e4 100644 --- a/app/src/main/java/com/tangem/tap/features/home/redux/HomeMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/home/redux/HomeMiddleware.kt @@ -62,7 +62,7 @@ private val homeMiddleware: Middleware = { _, _ -> store.dispatchOpenUrl(BUY_WALLET_URL) else -> store.dispatch(NavigationAction.NavigateTo(AppScreen.Shop)) } - store.state.globalState.analyticsHandler?.triggerEvent( + store.state.globalState.analyticsHandler?.handleAnalyticsEvent( event = AnalyticsEvent.GET_CARD, params = mapOf(AnalyticsParam.SOURCE.param to GetCardSourceParams.WELCOME.param) ) diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/SendMiddleware.kt b/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/SendMiddleware.kt index 8a3871a455..4e57c0ea39 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/SendMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/SendMiddleware.kt @@ -23,7 +23,6 @@ import com.tangem.tap.common.analytics.AnalyticsParam import com.tangem.tap.common.extensions.dispatchDialogShow import com.tangem.tap.common.extensions.dispatchErrorNotification import com.tangem.tap.common.extensions.dispatchOnMain -import com.tangem.tap.common.extensions.logSendTransactionError import com.tangem.tap.common.extensions.safeUpdate import com.tangem.tap.common.extensions.stripZeroPlainString import com.tangem.tap.common.redux.AppDialog @@ -222,7 +221,7 @@ private fun sendTransaction( when (sendResult) { is SimpleResult.Success -> { - store.state.globalState.analyticsHandler?.triggerEvent( + store.state.globalState.analyticsHandler?.handleAnalyticsEvent( event = AnalyticsEvent.TRANSACTION_IS_SENT, card = card, blockchain = walletManager.wallet.blockchain.currency, @@ -252,10 +251,10 @@ private fun sendTransaction( feeAmount = feeAmount, destinationAddress = destinationAddress, ) - store.state.globalState.analyticsHandler?.logSendTransactionError( + store.state.globalState.analyticsHandler?.handleBlockchainSdkErrorEvent( error = sendResult.error, action = Analytics.ActionToLog.SendTransaction, - parameters = mapOf(AnalyticsParam.BLOCKCHAIN to walletManager.wallet.blockchain.currency), + params = mapOf(AnalyticsParam.BLOCKCHAIN to walletManager.wallet.blockchain.currency), card = card, ) diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WalletMiddleware.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WalletMiddleware.kt index 71506a46da..2825983575 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WalletMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WalletMiddleware.kt @@ -174,7 +174,7 @@ class WalletMiddleware { } is CompletionResult.Failure -> { (result.error as? TangemSdkError)?.let { error -> - store.state.globalState.analyticsHandler?.logCardSdkError( + store.state.globalState.analyticsHandler?.handleCardSdkErrorEvent( error, Analytics.ActionToLog.CreateWallet, card = store.state.detailsState.scanResponse?.card diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WarningsMiddleware.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WarningsMiddleware.kt index 52c93db66e..528d4a0f43 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WarningsMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WarningsMiddleware.kt @@ -81,7 +81,7 @@ class WarningsMiddleware { preferencesStorage.appRatingLaunchObserver.foundWalletWithFunds() } if (preferencesStorage.appRatingLaunchObserver.isReadyToShow()) { - store.state.globalState.analyticsHandler?.triggerEvent(AnalyticsEvent.APP_RATING_DISPLAYED) + store.state.globalState.analyticsHandler?.handleAnalyticsEvent(AnalyticsEvent.APP_RATING_DISPLAYED) addWarningMessage(WarningMessagesManager.appRatingWarning(), true) } } diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/adapters/WarningMessagesAdapter.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/adapters/WarningMessagesAdapter.kt index 14cd39d812..4a5e240442 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/adapters/WarningMessagesAdapter.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/adapters/WarningMessagesAdapter.kt @@ -109,12 +109,12 @@ class WarningMessageVH(val binding: LayoutWarningCardActionBinding) : RecyclerVi binding.btnClose.show() val analyticsHandler = store.state.globalState.analyticsHandler binding.btnClose.setOnClickListener { - analyticsHandler?.triggerEvent(AnalyticsEvent.APP_RATING_DISMISS) + analyticsHandler?.handleAnalyticsEvent(AnalyticsEvent.APP_RATING_DISMISS) store.dispatch(GlobalAction.HideWarningMessage(warning)) store.dispatch(WalletAction.Warnings.AppRating.RemindLater) } binding.btnCanBeBetter.setOnClickListener { - analyticsHandler?.triggerEvent(AnalyticsEvent.APP_RATING_NEGATIVE) + analyticsHandler?.handleAnalyticsEvent(AnalyticsEvent.APP_RATING_NEGATIVE) store.dispatch(WalletAction.Warnings.AppRating.SetNeverToShow) store.dispatch(GlobalAction.HideWarningMessage(warning)) store.dispatch(GlobalAction.SendEmail(RateCanBeBetterEmail())) @@ -122,7 +122,7 @@ class WarningMessageVH(val binding: LayoutWarningCardActionBinding) : RecyclerVi binding.btnReallyCool.setOnClickListener { val activity = binding.root.context.getActivity() ?: return@setOnClickListener - analyticsHandler?.triggerEvent(AnalyticsEvent.APP_RATING_POSITIVE) + analyticsHandler?.handleAnalyticsEvent(AnalyticsEvent.APP_RATING_POSITIVE) store.dispatch(WalletAction.Warnings.AppRating.SetNeverToShow) val reviewManager = ReviewManagerFactory.create(activity) val task = reviewManager.requestReviewFlow() diff --git a/common/src/main/java/com/tangem/common/Converter.kt b/common/src/main/java/com/tangem/common/Converter.kt new file mode 100644 index 0000000000..699f548ac3 --- /dev/null +++ b/common/src/main/java/com/tangem/common/Converter.kt @@ -0,0 +1,8 @@ +package com.tangem.common + +/** +[REDACTED_AUTHOR] + */ +interface Converter { + fun convert(value: I): O +} \ No newline at end of file diff --git a/domain/src/main/java/com/tangem/domain/common/LogConfig.kt b/domain/src/main/java/com/tangem/domain/common/LogConfig.kt index 56c2950141..0325c6910d 100644 --- a/domain/src/main/java/com/tangem/domain/common/LogConfig.kt +++ b/domain/src/main/java/com/tangem/domain/common/LogConfig.kt @@ -7,6 +7,7 @@ object LogConfig { val storeAction: Boolean = BuildConfig.DEBUG val zendesk: Boolean = false val network: NetworkLogConfig = NetworkLogConfig + val analyticsHandlers: AnalyticsHandlersLogConfig = AnalyticsHandlersLogConfig } object NetworkLogConfig { @@ -14,4 +15,10 @@ object NetworkLogConfig { val moonPayService: Boolean = false val tangemTechService: Boolean = BuildConfig.DEBUG val blockchainSdkNetwork: Boolean = BuildConfig.DEBUG +} + +object AnalyticsHandlersLogConfig { + val firebase: Boolean = false + val appsFlyer: Boolean = false + val amplitude: Boolean = false } \ No newline at end of file From e97cb41fdbc8b684c5d7da753d4661ecfc2b98bb Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 23 Sep 2022 02:14:22 +0300 Subject: [PATCH 7/8] Updated on 2026-08-14 --- .../java/com/tangem/tap/TapApplication.kt | 4 +- .../analytics/AnalyticsEventHandlerBuilder.kt | 66 --------- .../common/analytics/AnalyticsEventsLogger.kt | 26 ++++ .../common/analytics/AnalyticsInterfaces.kt | 3 + .../GlobalAnalyticsEventHandlerBuilder.kt | 79 +++++++++++ .../analytics/GlobalAnalyticsHandler.kt | 27 ++-- .../tap/common/analytics/api/ClientApi.kt | 18 +++ .../EventHandlerApi.kt} | 34 ++--- .../converters/SdkErrorConverters.kt | 88 ++++++++++++ .../amplitude/AmplitudeAnalyticsHandler.kt | 28 +--- .../handlers/amplitude/AmplitudeClient.kt | 36 +++++ .../handlers/amplitude/AmplitudeLogClient.kt | 15 +++ .../appsFlyer/AppsFlyerAnalyticsHandler.kt | 49 +++---- .../handlers/appsFlyer/AppsFlyerClient.kt | 27 ++++ .../handlers/appsFlyer/AppsFlyerLogClient.kt | 15 +++ .../firebase/FirebaseAnalyticsHandler.kt | 126 +++++++----------- .../handlers/firebase/FirebaseClient.kt | 36 +++++ .../handlers/firebase/FirebaseLogClient.kt | 19 +++ 18 files changed, 468 insertions(+), 228 deletions(-) delete mode 100644 app/src/main/java/com/tangem/tap/common/analytics/AnalyticsEventHandlerBuilder.kt create mode 100644 app/src/main/java/com/tangem/tap/common/analytics/AnalyticsEventsLogger.kt create mode 100644 app/src/main/java/com/tangem/tap/common/analytics/AnalyticsInterfaces.kt create mode 100644 app/src/main/java/com/tangem/tap/common/analytics/GlobalAnalyticsEventHandlerBuilder.kt create mode 100644 app/src/main/java/com/tangem/tap/common/analytics/api/ClientApi.kt rename app/src/main/java/com/tangem/tap/common/analytics/{AnalyticsEventHandler.kt => api/EventHandlerApi.kt} (73%) create mode 100644 app/src/main/java/com/tangem/tap/common/analytics/converters/SdkErrorConverters.kt create mode 100644 app/src/main/java/com/tangem/tap/common/analytics/handlers/amplitude/AmplitudeClient.kt create mode 100644 app/src/main/java/com/tangem/tap/common/analytics/handlers/amplitude/AmplitudeLogClient.kt create mode 100644 app/src/main/java/com/tangem/tap/common/analytics/handlers/appsFlyer/AppsFlyerClient.kt create mode 100644 app/src/main/java/com/tangem/tap/common/analytics/handlers/appsFlyer/AppsFlyerLogClient.kt create mode 100644 app/src/main/java/com/tangem/tap/common/analytics/handlers/firebase/FirebaseClient.kt create mode 100644 app/src/main/java/com/tangem/tap/common/analytics/handlers/firebase/FirebaseLogClient.kt diff --git a/app/src/main/java/com/tangem/tap/TapApplication.kt b/app/src/main/java/com/tangem/tap/TapApplication.kt index 8a15017fe3..b8acdce7b5 100644 --- a/app/src/main/java/com/tangem/tap/TapApplication.kt +++ b/app/src/main/java/com/tangem/tap/TapApplication.kt @@ -14,7 +14,7 @@ import com.tangem.domain.common.LogConfig import com.tangem.network.common.MoshiConverter import com.tangem.tap.common.AndroidAssetReader import com.tangem.tap.common.AssetReader -import com.tangem.tap.common.analytics.AnalyticsEventHandlerBuilder +import com.tangem.tap.common.analytics.GlobalAnalyticsEventHandlerBuilder import com.tangem.tap.common.feedback.AdditionalFeedbackInfo import com.tangem.tap.common.feedback.FeedbackManager import com.tangem.tap.common.images.createCoilImageLoader @@ -100,7 +100,7 @@ class TapApplication : Application(), ImageLoaderFactory { } private fun initAnalytics(application: Application, config: Config) { - val globalAnalyticsHandler = AnalyticsEventHandlerBuilder( + val globalAnalyticsHandler = GlobalAnalyticsEventHandlerBuilder( application = application, config = config, isDebug = BuildConfig.DEBUG, diff --git a/app/src/main/java/com/tangem/tap/common/analytics/AnalyticsEventHandlerBuilder.kt b/app/src/main/java/com/tangem/tap/common/analytics/AnalyticsEventHandlerBuilder.kt deleted file mode 100644 index e5a2ec3283..0000000000 --- a/app/src/main/java/com/tangem/tap/common/analytics/AnalyticsEventHandlerBuilder.kt +++ /dev/null @@ -1,66 +0,0 @@ -package com.tangem.tap.common.analytics - -import android.app.Application -import com.tangem.common.json.MoshiJsonConverter -import com.tangem.domain.common.AnalyticsHandlersLogConfig -import com.tangem.tap.common.analytics.handlers.amplitude.AmplitudeAnalyticsHandler -import com.tangem.tap.common.analytics.handlers.appsFlyer.AppsFlyerAnalyticsHandler -import com.tangem.tap.common.analytics.handlers.firebase.FirebaseAnalyticsHandler -import com.tangem.tap.domain.configurable.config.Config - -/** -[REDACTED_AUTHOR] - */ -class AnalyticsEventHandlerBuilder( - private val application: Application, - private val config: Config, - private val isDebug: Boolean, - private val logConfig: AnalyticsHandlersLogConfig, - private val jsonConverter: MoshiJsonConverter, -) { - - private val analyticHandlers = mutableListOf() - - fun build(): GlobalAnalyticsEventHandler { - return GlobalAnalyticsHandler(analyticHandlers.toList()) - } - - fun default(): GlobalAnalyticsEventHandler { - addFirebaseAnalytics() - addAppsFlyer() - addAmplitude() - - return GlobalAnalyticsHandler(analyticHandlers.toList()) - } - - fun addFirebaseAnalytics(): AnalyticsEventHandlerBuilder { - if (isDebug) { - addEventLogger(logConfig.firebase, FirebaseAnalyticsHandler::class.java.simpleName) - } else { - analyticHandlers.add(FirebaseAnalyticsHandler()) - } - return this - } - - fun addAppsFlyer(): AnalyticsEventHandlerBuilder { - if (isDebug) { - addEventLogger(logConfig.appsFlyer, AppsFlyerAnalyticsHandler::class.java.simpleName) - } else { - analyticHandlers.add(AppsFlyerAnalyticsHandler(application, config.appsFlyerDevKey)) - } - return this - } - - fun addAmplitude(): AnalyticsEventHandlerBuilder { - if (isDebug) { - addEventLogger(logConfig.amplitude, AmplitudeAnalyticsHandler::class.java.simpleName) - } else { - analyticHandlers.add(AmplitudeAnalyticsHandler(application, config.amplitudeApiKey)) - } - return this - } - - private fun addEventLogger(logsEnabled: Boolean, name: String) { - // if (logsEnabled) analyticHandlers.add(AnalyticsLogsPrinter(name, jsonConverter)) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/analytics/AnalyticsEventsLogger.kt b/app/src/main/java/com/tangem/tap/common/analytics/AnalyticsEventsLogger.kt new file mode 100644 index 0000000000..0865d2aa81 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/common/analytics/AnalyticsEventsLogger.kt @@ -0,0 +1,26 @@ +package com.tangem.tap.common.analytics + +import com.tangem.common.json.MoshiJsonConverter +import com.tangem.tap.common.analytics.api.ErrorEventLogger +import com.tangem.tap.common.analytics.api.EventLogger +import timber.log.Timber + +class AnalyticsEventsLogger( + private val name: String, + private val jsonConverter: MoshiJsonConverter, +) : EventLogger, ErrorEventLogger { + + override fun logEvent(event: String, params: Map) { + Timber.d(jsonConverter.prettyPrint(PrintEventModel(name, event, params))) + } + + override fun logErrorEvent(error: Throwable, params: Map) { + Timber.e(error, jsonConverter.prettyPrint(PrintEventModel(name, "error", params))) + } +} + +private data class PrintEventModel( + val client: String, + val event: String, + val params: Map, +) \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/analytics/AnalyticsInterfaces.kt b/app/src/main/java/com/tangem/tap/common/analytics/AnalyticsInterfaces.kt new file mode 100644 index 0000000000..6fd6fc7dc8 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/common/analytics/AnalyticsInterfaces.kt @@ -0,0 +1,3 @@ +package com.tangem.tap.common.analytics + + diff --git a/app/src/main/java/com/tangem/tap/common/analytics/GlobalAnalyticsEventHandlerBuilder.kt b/app/src/main/java/com/tangem/tap/common/analytics/GlobalAnalyticsEventHandlerBuilder.kt new file mode 100644 index 0000000000..8e2b439e08 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/common/analytics/GlobalAnalyticsEventHandlerBuilder.kt @@ -0,0 +1,79 @@ +package com.tangem.tap.common.analytics + +import android.app.Application +import com.tangem.common.json.MoshiJsonConverter +import com.tangem.domain.common.AnalyticsHandlersLogConfig +import com.tangem.tap.common.analytics.api.AnalyticsEventHandler +import com.tangem.tap.common.analytics.handlers.amplitude.AmplitudeAnalyticsHandler +import com.tangem.tap.common.analytics.handlers.amplitude.AmplitudeClient +import com.tangem.tap.common.analytics.handlers.amplitude.AmplitudeLogClient +import com.tangem.tap.common.analytics.handlers.appsFlyer.AppsFlyerAnalyticsHandler +import com.tangem.tap.common.analytics.handlers.appsFlyer.AppsFlyerClient +import com.tangem.tap.common.analytics.handlers.appsFlyer.AppsFlyerLogClient +import com.tangem.tap.common.analytics.handlers.firebase.FirebaseAnalyticsHandler +import com.tangem.tap.common.analytics.handlers.firebase.FirebaseClient +import com.tangem.tap.common.analytics.handlers.firebase.FirebaseLogClient +import com.tangem.tap.domain.configurable.config.Config + +/** +[REDACTED_AUTHOR] + */ +class GlobalAnalyticsEventHandlerBuilder( + val application: Application, + val config: Config, + val isDebug: Boolean, + val logConfig: AnalyticsHandlersLogConfig, + val jsonConverter: MoshiJsonConverter, +) { + + private val analyticHandlers = mutableListOf() + + fun build(): GlobalAnalyticsEventHandler { + return GlobalAnalyticsHandler(analyticHandlers.toList()) + } + + fun default(): GlobalAnalyticsEventHandler { + addFirebaseAnalytics() + addAppsFlyer() + addAmplitude() + + return GlobalAnalyticsHandler(analyticHandlers.toList()) + } + + fun addFirebaseAnalytics(): GlobalAnalyticsEventHandlerBuilder { + if (isDebug) { + if (logConfig.firebase) FirebaseLogClient(createLogger("FirebaseAnalyticsHandler")) else null + } else { + FirebaseClient() + }?.let { + analyticHandlers.add(FirebaseAnalyticsHandler(it)) + } + return this + } + + fun addAppsFlyer(): GlobalAnalyticsEventHandlerBuilder { + if (isDebug) { + if (logConfig.appsFlyer) AppsFlyerLogClient(createLogger("AppsFlyerAnalyticsHandler")) else null + } else { + AppsFlyerClient(application, config.appsFlyerDevKey) + }?.let { + analyticHandlers.add(AppsFlyerAnalyticsHandler(it)) + } + return this + } + + fun addAmplitude(): GlobalAnalyticsEventHandlerBuilder { + if (isDebug) { + if (logConfig.firebase) AmplitudeLogClient(createLogger("AmplitudeAnalyticsHandler")) else null + } else { + AmplitudeClient(application, config.amplitudeApiKey) + }?.let { + analyticHandlers.add(AmplitudeAnalyticsHandler(it)) + } + return this + } + + private fun createLogger(name: String): AnalyticsEventsLogger { + return AnalyticsEventsLogger(name, jsonConverter) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/analytics/GlobalAnalyticsHandler.kt b/app/src/main/java/com/tangem/tap/common/analytics/GlobalAnalyticsHandler.kt index cd28857ae7..660664c1f7 100644 --- a/app/src/main/java/com/tangem/tap/common/analytics/GlobalAnalyticsHandler.kt +++ b/app/src/main/java/com/tangem/tap/common/analytics/GlobalAnalyticsHandler.kt @@ -4,12 +4,19 @@ import com.shopify.buy3.Storefront import com.tangem.blockchain.common.BlockchainError import com.tangem.common.card.Card import com.tangem.common.core.TangemSdkError +import com.tangem.tap.common.analytics.api.AnalyticsEventHandler +import com.tangem.tap.common.analytics.api.BlockchainSdkErrorEventHandler +import com.tangem.tap.common.analytics.api.CardSdkErrorEventHandler +import com.tangem.tap.common.analytics.api.ErrorEventHandler +import com.tangem.tap.common.analytics.api.ErrorEventLogger +import com.tangem.tap.common.analytics.api.SdkErrorEventHandler +import com.tangem.tap.common.analytics.api.ShopifyOrderEventHandler import com.tangem.tap.common.extensions.filterNotNull interface GlobalAnalyticsEventHandler : AnalyticsEventHandler, - ShopifyOrderEventHandler, ErrorEventHandler, - SdkErrorEventHandler + SdkErrorEventHandler, + ShopifyOrderEventHandler class GlobalAnalyticsHandler( private val analyticsHandlers: List, @@ -28,15 +35,9 @@ class GlobalAnalyticsHandler( analyticsHandlers.forEach { it.handleAnalyticsEvent(event, params, card, blockchain) } } - override fun handleShopifyOrderEvent(order: Storefront.Order) { - analyticsHandlers.filterIsInstance().forEach { - it.handleShopifyOrderEvent(order) - } - } - override fun handleErrorEvent(error: Throwable, params: Map) { - analyticsHandlers.filterIsInstance().forEach { - it.handleErrorEvent(error, params) + analyticsHandlers.filterIsInstance().forEach { + it.logErrorEvent(error, params) } } @@ -61,6 +62,12 @@ class GlobalAnalyticsHandler( it.handleBlockchainSdkErrorEvent(error, action, params, card) } } + + override fun handleShopifyOrderEvent(order: Storefront.Order) { + analyticsHandlers.filterIsInstance().forEach { + it.handleShopifyOrderEvent(order) + } + } } fun GlobalAnalyticsEventHandler.logWcEvent(event: Analytics.WcAnalyticsEvent) { diff --git a/app/src/main/java/com/tangem/tap/common/analytics/api/ClientApi.kt b/app/src/main/java/com/tangem/tap/common/analytics/api/ClientApi.kt new file mode 100644 index 0000000000..3b5b0720f2 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/common/analytics/api/ClientApi.kt @@ -0,0 +1,18 @@ +package com.tangem.tap.common.analytics.api + +/** +[REDACTED_AUTHOR] + */ +interface EventLogger { + fun logEvent( + event: String, + params: Map = emptyMap(), + ) +} + +interface ErrorEventLogger { + fun logErrorEvent( + error: Throwable, + params: Map = emptyMap(), + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/analytics/AnalyticsEventHandler.kt b/app/src/main/java/com/tangem/tap/common/analytics/api/EventHandlerApi.kt similarity index 73% rename from app/src/main/java/com/tangem/tap/common/analytics/AnalyticsEventHandler.kt rename to app/src/main/java/com/tangem/tap/common/analytics/api/EventHandlerApi.kt index d973b4fa2c..00cb845770 100644 --- a/app/src/main/java/com/tangem/tap/common/analytics/AnalyticsEventHandler.kt +++ b/app/src/main/java/com/tangem/tap/common/analytics/api/EventHandlerApi.kt @@ -1,19 +1,23 @@ -package com.tangem.tap.common.analytics +package com.tangem.tap.common.analytics.api import com.shopify.buy3.Storefront import com.tangem.blockchain.common.BlockchainError import com.tangem.common.card.Card import com.tangem.common.core.TangemSdkError +import com.tangem.tap.common.analytics.Analytics +import com.tangem.tap.common.analytics.AnalyticsEvent +import com.tangem.tap.common.analytics.AnalyticsParam import com.tangem.tap.common.extensions.filterNotNull -interface EventHandler { +/** +[REDACTED_AUTHOR] + */ +interface AnalyticsEventHandler { fun handleEvent( event: String, params: Map = emptyMap(), ) -} -interface AnalyticsEventHandler : EventHandler { fun handleAnalyticsEvent( event: AnalyticsEvent, params: Map = emptyMap(), @@ -25,17 +29,11 @@ interface AnalyticsEventHandler : EventHandler { card: Card?, blockchain: String? = null, params: Map = emptyMap(), - ): Map { - return mapOf( - AnalyticsParam.FIRMWARE.param to card?.firmwareVersion?.stringValue, - AnalyticsParam.BATCH_ID.param to card?.batchId, - AnalyticsParam.BLOCKCHAIN.param to blockchain, - ).filterNotNull() + params - } -} - -interface ShopifyOrderEventHandler { - fun handleShopifyOrderEvent(order: Storefront.Order) + ): Map = mapOf( + AnalyticsParam.FIRMWARE.param to card?.firmwareVersion?.stringValue, + AnalyticsParam.BATCH_ID.param to card?.batchId, + AnalyticsParam.BLOCKCHAIN.param to blockchain, + ).filterNotNull() + params } interface ErrorEventHandler { @@ -63,4 +61,8 @@ interface BlockchainSdkErrorEventHandler { params: Map = mapOf(), card: Card? = null, ) -} \ No newline at end of file +} + +interface ShopifyOrderEventHandler { + fun handleShopifyOrderEvent(order: Storefront.Order) +} diff --git a/app/src/main/java/com/tangem/tap/common/analytics/converters/SdkErrorConverters.kt b/app/src/main/java/com/tangem/tap/common/analytics/converters/SdkErrorConverters.kt new file mode 100644 index 0000000000..470933771a --- /dev/null +++ b/app/src/main/java/com/tangem/tap/common/analytics/converters/SdkErrorConverters.kt @@ -0,0 +1,88 @@ +package com.tangem.tap.common.analytics.converters + +import com.tangem.blockchain.common.BlockchainError +import com.tangem.blockchain.common.BlockchainSdkError +import com.tangem.common.Converter +import com.tangem.common.core.TangemSdkError +import com.tangem.tap.common.analytics.Analytics +import com.tangem.tap.common.analytics.AnalyticsParam +import com.tangem.tap.common.analytics.TangemSdk +import com.tangem.tap.features.demo.DemoTransactionSender + +/** +[REDACTED_AUTHOR] + */ +internal data class ErrorEvent( + val throwable: Throwable, + val params: Map, +) + +internal class CardSdkErrorConverter : Converter { + + override fun convert(value: Model): ErrorEvent? { + if (value.error is TangemSdkError.UserCancelled) return null + + val mutableParams = value.params.convertToParamValue().toMutableMap().apply { + putAll(value.cardParams) + } + mutableParams[AnalyticsParam.ACTION.param] = value.action.key + mutableParams[AnalyticsParam.ERROR_CODE.param] = value.error.code.toString() + mutableParams[AnalyticsParam.ERROR_DESCRIPTION.param] = value.error.javaClass.simpleName + mutableParams[AnalyticsParam.ERROR_KEY.param] = "TangemSdkError" + + return ErrorEvent(TangemSdk.map(value.error), mutableParams) + } + + internal data class Model( + val error: TangemSdkError, + val action: Analytics.ActionToLog, + val params: Map, + val cardParams: Map, + ) +} + +internal class BlockchainSdkErrorConverter( + private val cardSdkErrorConverter: CardSdkErrorConverter, +) : Converter { + + override fun convert(value: Model): ErrorEvent? { + val error = (value.error as? BlockchainSdkError) ?: return null + + if (error is BlockchainSdkError.WrappedTangemError) { + val tangemSdkError = (error.tangemError as? TangemSdkError) ?: return null + + val cardSdkModel = CardSdkErrorConverter.Model( + error = tangemSdkError, + action = value.action, + params = value.params, + cardParams = value.cardParams, + ) + return cardSdkErrorConverter.convert(cardSdkModel) + } + + if (error.customMessage.contains(DemoTransactionSender.ID)) return null + + val mutableParams = value.params.convertToParamValue().toMutableMap() + mutableParams[AnalyticsParam.ACTION.param] = value.action.key + mutableParams[AnalyticsParam.ERROR_CODE.param] = error.code.toString() + mutableParams[AnalyticsParam.ERROR_DESCRIPTION.param] = "${error.javaClass.simpleName}: ${error.customMessage}" + mutableParams[AnalyticsParam.ERROR_KEY.param] = "BlockchainSdkError" + + return ErrorEvent(value.error, mutableParams) + } + + data class Model( + val error: BlockchainError, + val action: Analytics.ActionToLog, + val params: Map, + val cardParams: Map, + ) +} + +internal fun Map.convertToParamValue(): Map { + return mutableMapOf().also { newMap -> + this.forEach { + newMap[it.key.param] = it.value + } + } +} \ No newline at end of file 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 22baff0a2f..ab530b5829 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 @@ -1,28 +1,15 @@ package com.tangem.tap.common.analytics.handlers.amplitude -import android.app.Application -import com.amplitude.api.Amplitude -import com.amplitude.api.AmplitudeClient -import com.tangem.common.Converter import com.tangem.common.card.Card import com.tangem.tap.common.analytics.AnalyticsEvent -import com.tangem.tap.common.analytics.AnalyticsEventHandler -import org.json.JSONObject +import com.tangem.tap.common.analytics.api.AnalyticsEventHandler class AmplitudeAnalyticsHandler( - application: Application, - key: String, + private val client: AmplitudeAnalyticsClient, ) : AnalyticsEventHandler { - private val client: AmplitudeClient = Amplitude.getInstance() - - init { - client.initialize(application, key) - client.enableForegroundTracking(application) - } - override fun handleEvent(event: String, params: Map) { - client.logEvent(event, ParamsJSONObjectConverter().convert(params)) + client.logEvent(event, params) } override fun handleAnalyticsEvent( @@ -33,11 +20,4 @@ class AmplitudeAnalyticsHandler( ) { handleEvent(event.event, prepareParams(card, blockchain, params)) } - - class ParamsJSONObjectConverter : Converter, JSONObject> { - override fun convert(value: Map): JSONObject = JSONObject().apply { - value.forEach { this.put(it.key, it.value) } - } - } -} - +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/analytics/handlers/amplitude/AmplitudeClient.kt b/app/src/main/java/com/tangem/tap/common/analytics/handlers/amplitude/AmplitudeClient.kt new file mode 100644 index 0000000000..d90ed084b5 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/common/analytics/handlers/amplitude/AmplitudeClient.kt @@ -0,0 +1,36 @@ +package com.tangem.tap.common.analytics.handlers.amplitude + +import android.app.Application +import com.amplitude.api.Amplitude +import com.amplitude.api.AmplitudeClient +import com.tangem.common.Converter +import com.tangem.tap.common.analytics.api.EventLogger +import org.json.JSONObject + +/** +[REDACTED_AUTHOR] + */ +interface AmplitudeAnalyticsClient : EventLogger + +internal class AmplitudeClient( + application: Application, + key: String, +) : AmplitudeAnalyticsClient { + + private val client: AmplitudeClient = Amplitude.getInstance() + + init { + client.initialize(application, key) + client.enableForegroundTracking(application) + } + + override fun logEvent(event: String, params: Map) { + client.logEvent(event, ParamsToJSONObjectConverter().convert(params)) + } +} + +private class ParamsToJSONObjectConverter : Converter, JSONObject> { + override fun convert(value: Map): JSONObject = JSONObject().apply { + value.forEach { this.put(it.key, it.value) } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/analytics/handlers/amplitude/AmplitudeLogClient.kt b/app/src/main/java/com/tangem/tap/common/analytics/handlers/amplitude/AmplitudeLogClient.kt new file mode 100644 index 0000000000..b800259b5f --- /dev/null +++ b/app/src/main/java/com/tangem/tap/common/analytics/handlers/amplitude/AmplitudeLogClient.kt @@ -0,0 +1,15 @@ +package com.tangem.tap.common.analytics.handlers.amplitude + +import com.tangem.tap.common.analytics.AnalyticsEventsLogger + +/** +[REDACTED_AUTHOR] + */ +internal class AmplitudeLogClient( + private val logger: AnalyticsEventsLogger, +) : AmplitudeAnalyticsClient { + + override fun logEvent(event: String, params: Map) { + logger.logEvent(event, 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 8b14fc2eba..a5ca83d4d5 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,30 +1,20 @@ package com.tangem.tap.common.analytics.handlers.appsFlyer -import android.content.Context import com.appsflyer.AFInAppEventParameterName import com.appsflyer.AFInAppEventType -import com.appsflyer.AppsFlyerLib import com.shopify.buy3.Storefront import com.tangem.common.Converter import com.tangem.common.card.Card import com.tangem.tap.common.analytics.AnalyticsEvent -import com.tangem.tap.common.analytics.AnalyticsEventHandler -import com.tangem.tap.common.analytics.ShopifyOrderEventHandler +import com.tangem.tap.common.analytics.api.AnalyticsEventHandler +import com.tangem.tap.common.analytics.api.ShopifyOrderEventHandler class AppsFlyerAnalyticsHandler( - private val context: Context, - key: String, + private val client: AppsFlyerAnalyticsClient, ) : AnalyticsEventHandler, ShopifyOrderEventHandler { - private val appsFlyerLib: AppsFlyerLib = AppsFlyerLib.getInstance() - - init { - appsFlyerLib.init(key, null, context) - appsFlyerLib.start(context) - } - override fun handleEvent(event: String, params: Map) { - appsFlyerLib.logEvent(context, event, params) + client.logEvent(event, params) } override fun handleAnalyticsEvent( @@ -43,25 +33,26 @@ class AppsFlyerAnalyticsHandler( companion object { const val ORDER_EVENT = AFInAppEventType.PURCHASE } +} - class OrderToParamsConverter : Converter> { - override fun convert(value: Storefront.Order): Map { - val sku = value.lineItems.edges.firstOrNull()?.node?.variant?.sku ?: "unknown" +private class OrderToParamsConverter : Converter> { - val discountCode = - (value.discountApplications.edges.firstOrNull()?.node as? Storefront.DiscountCodeApplication)?.code + override fun convert(value: Storefront.Order): Map { + val sku = value.lineItems.edges.firstOrNull()?.node?.variant?.sku ?: "unknown" - val discountParams = if (discountCode != null) { - mapOf(AFInAppEventParameterName.COUPON_CODE to discountCode) - } else { - mapOf() - } + val discountCode = + (value.discountApplications.edges.firstOrNull()?.node as? Storefront.DiscountCodeApplication)?.code - return mapOf( - AFInAppEventParameterName.CONTENT_ID to sku, - AFInAppEventParameterName.REVENUE to value.totalPriceV2.amount, - AFInAppEventParameterName.CURRENCY to value.currencyCode.name, - ) + discountParams + val discountParams = if (discountCode != null) { + mapOf(AFInAppEventParameterName.COUPON_CODE to discountCode) + } else { + mapOf() } + + return mapOf( + AFInAppEventParameterName.CONTENT_ID to sku, + AFInAppEventParameterName.REVENUE to value.totalPriceV2.amount, + AFInAppEventParameterName.CURRENCY to value.currencyCode.name, + ) + discountParams } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/analytics/handlers/appsFlyer/AppsFlyerClient.kt b/app/src/main/java/com/tangem/tap/common/analytics/handlers/appsFlyer/AppsFlyerClient.kt new file mode 100644 index 0000000000..6a7aa7cfed --- /dev/null +++ b/app/src/main/java/com/tangem/tap/common/analytics/handlers/appsFlyer/AppsFlyerClient.kt @@ -0,0 +1,27 @@ +package com.tangem.tap.common.analytics.handlers.appsFlyer + +import android.content.Context +import com.appsflyer.AppsFlyerLib +import com.tangem.tap.common.analytics.api.EventLogger + +/** +[REDACTED_AUTHOR] + */ +interface AppsFlyerAnalyticsClient : EventLogger + +internal class AppsFlyerClient( + private val context: Context, + key: String, +) : AppsFlyerAnalyticsClient { + + private val appsFlyerLib: AppsFlyerLib = AppsFlyerLib.getInstance() + + init { + appsFlyerLib.init(key, null, context) + appsFlyerLib.start(context) + } + + override fun logEvent(event: String, params: Map) { + appsFlyerLib.logEvent(context, event, params) + } +} \ 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 new file mode 100644 index 0000000000..0d3737a161 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/common/analytics/handlers/appsFlyer/AppsFlyerLogClient.kt @@ -0,0 +1,15 @@ +package com.tangem.tap.common.analytics.handlers.appsFlyer + +import com.tangem.tap.common.analytics.AnalyticsEventsLogger + +/** +[REDACTED_AUTHOR] + */ +internal class AppsFlyerLogClient( + private val logger: AnalyticsEventsLogger, +) : AppsFlyerAnalyticsClient { + + override fun logEvent(event: String, params: Map) { + logger.logEvent(event, params) + } +} \ 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 b2be460eb5..aba653613e 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,35 +1,27 @@ package com.tangem.tap.common.analytics.handlers.firebase -import android.os.Bundle -import androidx.core.os.bundleOf import com.google.firebase.analytics.FirebaseAnalytics -import com.google.firebase.analytics.ktx.analytics -import com.google.firebase.crashlytics.ktx.crashlytics -import com.google.firebase.ktx.Firebase import com.shopify.buy3.Storefront import com.tangem.blockchain.common.BlockchainError -import com.tangem.blockchain.common.BlockchainSdkError import com.tangem.common.Converter import com.tangem.common.card.Card import com.tangem.common.core.TangemSdkError import com.tangem.tap.common.analytics.Analytics import com.tangem.tap.common.analytics.AnalyticsEvent -import com.tangem.tap.common.analytics.AnalyticsEventHandler import com.tangem.tap.common.analytics.AnalyticsParam -import com.tangem.tap.common.analytics.ErrorEventHandler -import com.tangem.tap.common.analytics.SdkErrorEventHandler -import com.tangem.tap.common.analytics.ShopifyOrderEventHandler -import com.tangem.tap.common.analytics.TangemSdk -import com.tangem.tap.features.demo.DemoTransactionSender +import com.tangem.tap.common.analytics.api.AnalyticsEventHandler +import com.tangem.tap.common.analytics.api.ErrorEventHandler +import com.tangem.tap.common.analytics.api.SdkErrorEventHandler +import com.tangem.tap.common.analytics.api.ShopifyOrderEventHandler +import com.tangem.tap.common.analytics.converters.BlockchainSdkErrorConverter +import com.tangem.tap.common.analytics.converters.CardSdkErrorConverter -class FirebaseAnalyticsHandler : AnalyticsEventHandler, ShopifyOrderEventHandler, ErrorEventHandler, - SdkErrorEventHandler { - - private val fbAnalytics = Firebase.analytics - private val fbCrashlytics = Firebase.crashlytics +class FirebaseAnalyticsHandler( + private val client: FirebaseAnalyticsClient, +) : AnalyticsEventHandler, ErrorEventHandler, SdkErrorEventHandler, ShopifyOrderEventHandler { override fun handleEvent(event: String, params: Map) { - fbAnalytics.logEvent(event, params.toBundle()) + client.logEvent(event, params) } override fun handleAnalyticsEvent( @@ -38,16 +30,11 @@ class FirebaseAnalyticsHandler : AnalyticsEventHandler, ShopifyOrderEventHandler card: Card?, blockchain: String?, ) { - handleEvent(event.event, prepareParams(card, blockchain, params)) - } - - override fun handleShopifyOrderEvent(order: Storefront.Order) { - handleEvent(ORDER_EVENT, OrderToParamsConverter().convert(order)) + handleEvent(event.name, prepareParams(card, blockchain, params)) } override fun handleErrorEvent(error: Throwable, params: Map) { - params.forEach { (key, value) -> fbCrashlytics.setCustomKey(key, value) } - fbCrashlytics.recordException(error) + client.logErrorEvent(error, params) } override fun handleCardSdkErrorEvent( @@ -56,18 +43,11 @@ class FirebaseAnalyticsHandler : AnalyticsEventHandler, ShopifyOrderEventHandler params: Map, card: Card?, ) { - if (error is TangemSdkError.UserCancelled) return - - val mutableParams = params.toMutableMap() - if (card != null) mutableParams + prepareParams(card) - - mutableParams[AnalyticsParam.ACTION] = action.key - mutableParams[AnalyticsParam.ERROR_CODE] = error.code.toString() - mutableParams[AnalyticsParam.ERROR_DESCRIPTION] = error.javaClass.simpleName - mutableParams[AnalyticsParam.ERROR_KEY] = "TangemSdkError" - - mutableParams.forEach { fbCrashlytics.setCustomKey(it.key.param, it.value) } - fbCrashlytics.recordException(TangemSdk.map(error)) + val model = CardSdkErrorConverter.Model(error, action, params, prepareParams(card)) + val converter = CardSdkErrorConverter() + converter.convert(model)?.let { + handleErrorEvent(it.throwable, it.params) + } } override fun handleBlockchainSdkErrorEvent( @@ -76,52 +56,36 @@ class FirebaseAnalyticsHandler : AnalyticsEventHandler, ShopifyOrderEventHandler params: Map, card: Card?, ) { - val blockchainSdkError = (error as? BlockchainSdkError) ?: return - if (blockchainSdkError is BlockchainSdkError.WrappedTangemError) { - val tangemSdkError = (blockchainSdkError.tangemError as? TangemSdkError) ?: return - handleCardSdkErrorEvent(tangemSdkError, action, params, card) - return + val model = BlockchainSdkErrorConverter.Model(error, action, params, prepareParams(card)) + val converter = BlockchainSdkErrorConverter(CardSdkErrorConverter()) + converter.convert(model)?.let { + handleErrorEvent(it.throwable, it.params) + } + } + + override fun handleShopifyOrderEvent(order: Storefront.Order) { + handleEvent(FirebaseClient.ORDER_EVENT, OrderToParamsConverter().convert(order)) + } +} + +private class OrderToParamsConverter : Converter> { + override fun convert(value: Storefront.Order): Map { + val sku = value.lineItems.edges.firstOrNull()?.node?.variant?.sku ?: "unknown" + + val discountCode = + (value.discountApplications.edges.firstOrNull()?.node as? Storefront.DiscountCodeApplication)?.code + + val discountParams = if (discountCode != null) { + mapOf(FirebaseAnalytics.Param.DISCOUNT to discountCode) + } else { + mapOf() } - if (blockchainSdkError.customMessage.contains(DemoTransactionSender.ID)) return - - val mutableParams = params.toMutableMap() - mutableParams[AnalyticsParam.ACTION] = action.key - mutableParams[AnalyticsParam.ERROR_CODE] = error.code.toString() - mutableParams[AnalyticsParam.ERROR_DESCRIPTION] = "${error.javaClass.simpleName}: ${error.customMessage}" - mutableParams[AnalyticsParam.ERROR_KEY] = "BlockchainSdkError" - - mutableParams.forEach { fbCrashlytics.setCustomKey(it.key.param, it.value) } - fbCrashlytics.recordException(error) - } - - private fun Map.toBundle(): Bundle { - return bundleOf(*this.toList().toTypedArray()) - } - - companion object { - const val ORDER_EVENT = FirebaseAnalytics.Event.PURCHASE - } - - class OrderToParamsConverter : Converter> { - override fun convert(value: Storefront.Order): Map { - val sku = value.lineItems.edges.firstOrNull()?.node?.variant?.sku ?: "unknown" - - val discountCode = - (value.discountApplications.edges.firstOrNull()?.node as? Storefront.DiscountCodeApplication)?.code - - val discountParams = if (discountCode != null) { - mapOf(FirebaseAnalytics.Param.DISCOUNT to discountCode) - } else { - mapOf() - } - - return mapOf( - FirebaseAnalytics.Param.ITEM_ID to sku, - FirebaseAnalytics.Param.VALUE to value.totalPriceV2.amount, - FirebaseAnalytics.Param.CURRENCY to value.currencyCode.name, - ) + discountParams - } + return mapOf( + FirebaseAnalytics.Param.ITEM_ID to sku, + FirebaseAnalytics.Param.VALUE to value.totalPriceV2.amount, + FirebaseAnalytics.Param.CURRENCY to value.currencyCode.name, + ) + discountParams } } \ No newline at end of file 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 new file mode 100644 index 0000000000..07ed4c4551 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/common/analytics/handlers/firebase/FirebaseClient.kt @@ -0,0 +1,36 @@ +package com.tangem.tap.common.analytics.handlers.firebase + +import android.os.Bundle +import androidx.core.os.bundleOf +import com.google.firebase.analytics.FirebaseAnalytics +import com.google.firebase.analytics.ktx.analytics +import com.google.firebase.crashlytics.ktx.crashlytics +import com.google.firebase.ktx.Firebase +import com.tangem.tap.common.analytics.api.ErrorEventLogger +import com.tangem.tap.common.analytics.api.EventLogger + +/** +[REDACTED_AUTHOR] + */ +interface FirebaseAnalyticsClient : EventLogger, ErrorEventLogger + +internal class FirebaseClient : FirebaseAnalyticsClient { + + private val fbAnalytics = Firebase.analytics + private val fbCrashlytics = Firebase.crashlytics + + override fun logEvent(event: String, params: Map) { + fbAnalytics.logEvent(event, params.toBundle()) + } + + override fun logErrorEvent(error: Throwable, params: Map) { + params.forEach { fbCrashlytics.setCustomKey(it.key, it.value) } + fbCrashlytics.recordException(error) + } + + private fun Map.toBundle(): Bundle = bundleOf(*this.toList().toTypedArray()) + + companion object { + const val ORDER_EVENT = FirebaseAnalytics.Event.PURCHASE + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/analytics/handlers/firebase/FirebaseLogClient.kt b/app/src/main/java/com/tangem/tap/common/analytics/handlers/firebase/FirebaseLogClient.kt new file mode 100644 index 0000000000..ccacc09f9e --- /dev/null +++ b/app/src/main/java/com/tangem/tap/common/analytics/handlers/firebase/FirebaseLogClient.kt @@ -0,0 +1,19 @@ +package com.tangem.tap.common.analytics.handlers.firebase + +import com.tangem.tap.common.analytics.AnalyticsEventsLogger + +/** +[REDACTED_AUTHOR] + */ +internal class FirebaseLogClient( + private val logger: AnalyticsEventsLogger, +) : FirebaseAnalyticsClient { + + override fun logEvent(event: String, params: Map) { + logger.logEvent(event, params) + } + + override fun logErrorEvent(error: Throwable, params: Map) { + logger.logErrorEvent(error, params) + } +} \ No newline at end of file From ea3b575143db84c51a86eba668a576865118c953 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 25 Oct 2022 16:48:17 +0500 Subject: [PATCH 8/8] Updated on 2026-08-14 --- .../java/com/tangem/tap/features/home/redux/HomeMiddleware.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/tangem/tap/features/home/redux/HomeMiddleware.kt b/app/src/main/java/com/tangem/tap/features/home/redux/HomeMiddleware.kt index ad67172568..4255a371f7 100644 --- a/app/src/main/java/com/tangem/tap/features/home/redux/HomeMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/home/redux/HomeMiddleware.kt @@ -91,7 +91,7 @@ private fun handleHomeAction(appState: () -> AppState?, action: Action, dispatch RUSSIA_COUNTRY_CODE, BELARUS_COUNTRY_CODE -> store.dispatchOpenUrl(BUY_WALLET_URL) else -> store.dispatch(NavigationAction.NavigateTo(AppScreen.Shop)) } - store.state.globalState.analyticsHandlers?.triggerEvent( + store.state.globalState.analyticsHandler?.handleAnalyticsEvent( event = AnalyticsEvent.GET_CARD, params = mapOf(AnalyticsParam.SOURCE.param to GetCardSourceParams.WELCOME.param), )