diff --git a/app/build.gradle b/app/build.gradle index f1fa3b08ef..70b6edcebd 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -122,12 +122,12 @@ 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' - // 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' diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 4422e037f9..874783a15d 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -15,6 +15,7 @@ + diff --git a/app/src/main/java/com/tangem/tap/TapApplication.kt b/app/src/main/java/com/tangem/tap/TapApplication.kt index 8fa11bf17a..c0d6e566b4 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.GlobalAnalyticsEventHandlerBuilder import com.tangem.tap.common.feedback.AdditionalFeedbackInfo import com.tangem.tap.common.feedback.FeedbackManager import com.tangem.tap.common.images.createCoilImageLoader @@ -97,15 +97,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.SetAnanlyticHandlers(analyticsHandler)) + private fun initAnalytics(application: Application, config: Config) { + val globalAnalyticsHandler = GlobalAnalyticsEventHandlerBuilder( + 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/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/AnalyticsHandler.kt b/app/src/main/java/com/tangem/tap/common/analytics/AnalyticsHandler.kt deleted file mode 100644 index 424f06d9ce..0000000000 --- a/app/src/main/java/com/tangem/tap/common/analytics/AnalyticsHandler.kt +++ /dev/null @@ -1,89 +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): Map { - return mapOf( - AnalyticsParam.FIRMWARE.param to card?.firmwareVersion?.stringValue, - AnalyticsParam.BATCH_ID.param to card?.batchId, - AnalyticsParam.BLOCKCHAIN.param to blockchain, - ).filterNotNull() - } - - 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/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/AppsFlyerAnalyticsHandler.kt b/app/src/main/java/com/tangem/tap/common/analytics/AppsFlyerAnalyticsHandler.kt deleted file mode 100644 index 16fa1644ab..0000000000 --- a/app/src/main/java/com/tangem/tap/common/analytics/AppsFlyerAnalyticsHandler.kt +++ /dev/null @@ -1,61 +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)) - } - - 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 4e0371206a..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/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 197f396621..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 @@ -1,53 +1,109 @@ package com.tangem.tap.common.analytics -import android.content.Context 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 -class GlobalAnalyticsHandler(val analyticsHandlers: List) : - AnalyticsHandler() { - override fun triggerEvent( +interface GlobalAnalyticsEventHandler : AnalyticsEventHandler, + ErrorEventHandler, + SdkErrorEventHandler, + ShopifyOrderEventHandler + +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 handleErrorEvent(error: Throwable, params: Map) { + analyticsHandlers.filterIsInstance().forEach { + it.logErrorEvent(error, params) + } } - override fun logCardSdkError( + 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 handleShopifyOrderEvent(order: Storefront.Order) { + analyticsHandlers.filterIsInstance().forEach { + it.handleShopifyOrderEvent(order) + } } +} - override fun getOrderParams(order: Storefront.Order): Map { - return emptyMap() - } - - companion object { - fun createDefaultAnalyticHandlers(context: Context): GlobalAnalyticsHandler { - return GlobalAnalyticsHandler( - listOf( - FirebaseAnalyticsHandler, - AppsFlyerAnalyticsHandler(context) - ) +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, + ), + ) + } + 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) + } + 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(), ) } } 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/api/EventHandlerApi.kt b/app/src/main/java/com/tangem/tap/common/analytics/api/EventHandlerApi.kt new file mode 100644 index 0000000000..00cb845770 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/common/analytics/api/EventHandlerApi.kt @@ -0,0 +1,68 @@ +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 + +/** +[REDACTED_AUTHOR] + */ +interface AnalyticsEventHandler { + fun handleEvent( + event: String, + params: Map = emptyMap(), + ) + + fun handleAnalyticsEvent( + event: AnalyticsEvent, + params: Map = emptyMap(), + card: Card? = null, + blockchain: String? = null, + ) + + fun prepareParams( + card: Card?, + blockchain: String? = null, + params: Map = emptyMap(), + ): 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 { + 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, + ) +} + +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 new file mode 100644 index 0000000000..ab530b5829 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/common/analytics/handlers/amplitude/AmplitudeAnalyticsHandler.kt @@ -0,0 +1,23 @@ +package com.tangem.tap.common.analytics.handlers.amplitude + +import com.tangem.common.card.Card +import com.tangem.tap.common.analytics.AnalyticsEvent +import com.tangem.tap.common.analytics.api.AnalyticsEventHandler + +class AmplitudeAnalyticsHandler( + private val client: AmplitudeAnalyticsClient, +) : AnalyticsEventHandler { + + override fun handleEvent(event: String, params: Map) { + client.logEvent(event, params) + } + + override fun handleAnalyticsEvent( + event: AnalyticsEvent, + params: Map, + card: Card?, + blockchain: String?, + ) { + handleEvent(event.event, prepareParams(card, blockchain, params)) + } +} \ 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 new file mode 100644 index 0000000000..a5ca83d4d5 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/common/analytics/handlers/appsFlyer/AppsFlyerAnalyticsHandler.kt @@ -0,0 +1,58 @@ +package com.tangem.tap.common.analytics.handlers.appsFlyer + +import com.appsflyer.AFInAppEventParameterName +import com.appsflyer.AFInAppEventType +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.api.AnalyticsEventHandler +import com.tangem.tap.common.analytics.api.ShopifyOrderEventHandler + +class AppsFlyerAnalyticsHandler( + private val client: AppsFlyerAnalyticsClient, +) : AnalyticsEventHandler, ShopifyOrderEventHandler { + + override fun handleEvent(event: String, params: Map) { + client.logEvent(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 + } +} + +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(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 new file mode 100644 index 0000000000..aba653613e --- /dev/null +++ b/app/src/main/java/com/tangem/tap/common/analytics/handlers/firebase/FirebaseAnalyticsHandler.kt @@ -0,0 +1,91 @@ +package com.tangem.tap.common.analytics.handlers.firebase + +import com.google.firebase.analytics.FirebaseAnalytics +import com.shopify.buy3.Storefront +import com.tangem.blockchain.common.BlockchainError +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.AnalyticsParam +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( + private val client: FirebaseAnalyticsClient, +) : AnalyticsEventHandler, ErrorEventHandler, SdkErrorEventHandler, ShopifyOrderEventHandler { + + override fun handleEvent(event: String, params: Map) { + client.logEvent(event, params) + } + + override fun handleAnalyticsEvent( + event: AnalyticsEvent, + params: Map, + card: Card?, + blockchain: String?, + ) { + handleEvent(event.name, prepareParams(card, blockchain, params)) + } + + override fun handleErrorEvent(error: Throwable, params: Map) { + client.logErrorEvent(error, params) + } + + override fun handleCardSdkErrorEvent( + error: TangemSdkError, + action: Analytics.ActionToLog, + params: Map, + card: Card?, + ) { + val model = CardSdkErrorConverter.Model(error, action, params, prepareParams(card)) + val converter = CardSdkErrorConverter() + converter.convert(model)?.let { + handleErrorEvent(it.throwable, it.params) + } + } + + override fun handleBlockchainSdkErrorEvent( + error: BlockchainError, + action: Analytics.ActionToLog, + params: Map, + card: Card?, + ) { + + 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() + } + + 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 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 a3aba839df..b226be3401 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 @@ -89,7 +89,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: GlobalAnalyticsEventHandler) : GlobalAction() data class SendEmail(val feedbackData: FeedbackData) : GlobalAction() data class OpenChat(val feedbackData: FeedbackData, val zendeskConfig: ZendeskConfig? = null) : 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 67e326564d..a805bbd798 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 @@ -163,7 +163,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 d1f9043ca2..b0cefc6d9e 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 @@ -48,8 +48,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..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.AnalyticsHandler +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 analyticsHandlers: AnalyticsHandler? = 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 f5281cc796..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 @@ -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.GlobalAnalyticsEventHandler 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: GlobalAnalyticsEventHandler?) { 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.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 98694f525b..77d70bf042 100644 --- a/app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt @@ -28,8 +28,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.GlobalAnalyticsEventHandler import com.tangem.tap.domain.tasks.CreateWalletAndRescanTask import com.tangem.tap.domain.tasks.product.ResetToFactorySettingsTask import com.tangem.tap.domain.tasks.product.ScanProductTask @@ -45,12 +45,12 @@ import kotlin.coroutines.suspendCoroutine class TangemSdkManager(private val tangemSdk: TangemSdk, private val context: Context) { suspend fun scanProduct( - analyticsHandler: AnalyticsHandler?, + 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( @@ -70,30 +70,30 @@ class TangemSdkManager(private val tangemSdk: TangemSdk, private val context: Co } private fun sendScanResultsToAnalytics( - analyticsHandler: AnalyticsHandler?, + 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), ) } } is CompletionResult.Failure -> (result.error as? TangemSdkError)?.let { error -> - analyticsHandler?.logCardSdkError( + analyticsHandler?.handleCardSdkErrorEvent( error = error, - actionToLog = Analytics.ActionToLog.Scan, + action = Analytics.ActionToLog.Scan, ) } } @@ -101,7 +101,8 @@ class TangemSdkManager(private val tangemSdk: TangemSdk, private val context: Co suspend fun createWallet(cardId: String?): CompletionResult { return runTaskAsyncReturnOnMain( - CreateWalletAndRescanTask(), cardId, + CreateWalletAndRescanTask(), + cardId, initialMessage = Message(context.getString(R.string.initial_message_create_wallet_body)), ) } 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 9d0ae5a366..bd530b0796 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 @@ -16,6 +16,7 @@ data class Config( val mercuryoWidgetId: String = "", val mercuryoSecret: String = "", val appsFlyerDevKey: String = "", + val amplitudeApiKey: String = "", val blockchainSdkConfig: BlockchainSdkConfig = BlockchainSdkConfig(), val isSendingToPayIdEnabled: Boolean = true, val isTopUpEnabled: Boolean = false, @@ -90,6 +91,7 @@ class ConfigManager { tronGridApiKey = values.tronGridApiKey ), appsFlyerDevKey = values.appsFlyerDevKey, + amplitudeApiKey = values.amplitudeApiKey, shopify = values.shopifyShop, zendesk = values.zendesk, saltPayConfig = values.saltPay, @@ -107,6 +109,7 @@ class ConfigManager { infuraProjectId = values.infuraProjectId ), appsFlyerDevKey = values.appsFlyerDevKey, + amplitudeApiKey = values.amplitudeApiKey, shopify = values.shopifyShop, zendesk = values.zendesk, saltPayConfig = values.saltPay, 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 70c7ca73db..1c537e7909 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 @@ -30,9 +30,13 @@ class ConfigValueModel( val zendesk: ZendeskConfig?, val saltPay: SaltPayConfig, val tronGridApiKey: String, + val amplitudeApiKey: String, ) -class ConfigModel(val features: FeatureModel?, val configValues: ConfigValueModel?) { +class ConfigModel( + val features: FeatureModel?, + val configValues: ConfigValueModel?, +) { companion object { fun empty(): ConfigModel = ConfigModel(null, null) } 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..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.AnalyticsHandler +import com.tangem.tap.common.analytics.GlobalAnalyticsEventHandler import com.tangem.tap.tangemSdkManager class TwinCardsManager( private val card: Card, assetReader: AssetReader, - val analyticsHandler: AnalyticsHandler? + 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 de71029d2c..a3ec71fb94 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.domain.TapError @@ -222,7 +223,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 ) @@ -371,7 +372,7 @@ class WalletConnectManager { ), ) } - store.state.globalState.analyticsHandlers?.logWcEvent( + store.state.globalState.analyticsHandler?.logWcEvent( Analytics.WcAnalyticsEvent.Session( Analytics.WcSessionEvent.Connect, peer.url, ), @@ -386,7 +387,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 ) @@ -404,7 +405,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 ) @@ -422,7 +423,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 ) @@ -498,7 +499,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..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.analyticsHandlers?.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.analyticsHandlers?.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.analyticsHandlers?.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.analyticsHandlers?.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 cc9ccef1b6..12f4fee9e2 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 @@ -110,7 +110,7 @@ class DetailsMiddleware { } is CompletionResult.Failure -> { (result.error as? TangemSdkError)?.let { error -> - store.state.globalState.analyticsHandlers?.logCardSdkError( + store.state.globalState.analyticsHandler?.handleCardSdkErrorEvent( error, Analytics.ActionToLog.PurgeWallet, card = store.state.detailsState.scanResponse?.card, @@ -154,10 +154,10 @@ class DetailsMiddleware { } is CompletionResult.Failure -> { (result.error as? TangemSdkError)?.let { error -> - store.state.globalState.analyticsHandlers?.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 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), ) 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 defca803e3..3890f95d40 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.analyticsHandlers?.triggerEvent( + store.state.globalState.analyticsHandler?.handleAnalyticsEvent( event = AnalyticsEvent.TRANSACTION_IS_SENT, card = card, blockchain = walletManager.wallet.blockchain.currency, @@ -251,10 +250,10 @@ private fun sendTransaction( feeAmount = feeAmount, destinationAddress = destinationAddress, ) - store.state.globalState.analyticsHandlers?.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/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 19d4a51cf0..52a199ff60 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 @@ -186,7 +186,7 @@ class WalletMiddleware { } is CompletionResult.Failure -> { (result.error as? TangemSdkError)?.let { error -> - store.state.globalState.analyticsHandlers?.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 7aa45f997d..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 @@ -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?.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 dee8fef67c..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 @@ -107,14 +107,14 @@ 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) + 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 ff11351635..c4f96f4b55 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 { @@ -15,4 +16,10 @@ object NetworkLogConfig { val tangemTechService: Boolean = BuildConfig.DEBUG val paymentologyApiService: 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