Updated on 2026-08-14
This commit is contained in:
commit
5c8185de36
44 changed files with 779 additions and 388 deletions
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
<uses-permission android:name="android.permission.CAMERA" />
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
|
||||
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
|
||||
|
||||
<uses-feature android:name="android.hardware.camera" />
|
||||
<uses-feature android:name="android.hardware.camera.autofocus" />
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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<String, String>) {
|
||||
Timber.d(jsonConverter.prettyPrint(PrintEventModel(name, event, params)))
|
||||
}
|
||||
|
||||
override fun logErrorEvent(error: Throwable, params: Map<String, String>) {
|
||||
Timber.e(error, jsonConverter.prettyPrint(PrintEventModel(name, "error", params)))
|
||||
}
|
||||
}
|
||||
|
||||
private data class PrintEventModel(
|
||||
val client: String,
|
||||
val event: String,
|
||||
val params: Map<String, String>,
|
||||
)
|
||||
|
|
@ -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<String, String> = emptyMap()
|
||||
)
|
||||
|
||||
abstract fun triggerEvent(
|
||||
event: String,
|
||||
params: Map<String, String>
|
||||
)
|
||||
|
||||
abstract fun logCardSdkError(
|
||||
error: TangemSdkError,
|
||||
actionToLog: Analytics.ActionToLog,
|
||||
parameters: Map<AnalyticsParam, String>? = null,
|
||||
card: Card? = null
|
||||
)
|
||||
|
||||
abstract fun logError(
|
||||
error: Throwable,
|
||||
params: Map<String, String> = emptyMap()
|
||||
)
|
||||
|
||||
protected fun prepareParams(card: Card?, blockchain: String? = null): Map<String, String> {
|
||||
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<String, String>
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
package com.tangem.tap.common.analytics
|
||||
|
||||
|
||||
|
|
@ -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<String, String>
|
||||
) {
|
||||
AppsFlyerLib.getInstance().logEvent(context, event.event, prepareParams(card, blockchain))
|
||||
}
|
||||
|
||||
override fun logCardSdkError(
|
||||
error: TangemSdkError,
|
||||
actionToLog: Analytics.ActionToLog,
|
||||
parameters: Map<AnalyticsParam, String>?,
|
||||
card: Card?
|
||||
) {
|
||||
//TODO
|
||||
}
|
||||
|
||||
override fun logError(error: Throwable, params: Map<String, String>) {
|
||||
//TODO
|
||||
}
|
||||
|
||||
override fun getOrderEvent(): String = AFInAppEventType.PURCHASE
|
||||
|
||||
override fun getOrderParams(order: Storefront.Order): Map<String, String> {
|
||||
|
||||
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<String, String>) {
|
||||
AppsFlyerLib.getInstance().logEvent(context, event, params)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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<String, String>
|
||||
) {
|
||||
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<String, String>.toBundle(): Bundle {
|
||||
return bundleOf(*this.toList().toTypedArray())
|
||||
}
|
||||
|
||||
override fun logCardSdkError(
|
||||
error: TangemSdkError,
|
||||
actionToLog: Analytics.ActionToLog,
|
||||
parameters: Map<AnalyticsParam, String>?,
|
||||
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<String, String>) {
|
||||
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<String, String> {
|
||||
|
||||
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<String, String>) {
|
||||
Firebase.analytics.logEvent(event, params.toBundle())
|
||||
}
|
||||
}
|
||||
|
|
@ -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<AnalyticsEventHandler>()
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
|
@ -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>) :
|
||||
AnalyticsHandler() {
|
||||
override fun triggerEvent(
|
||||
interface GlobalAnalyticsEventHandler : AnalyticsEventHandler,
|
||||
ErrorEventHandler,
|
||||
SdkErrorEventHandler,
|
||||
ShopifyOrderEventHandler
|
||||
|
||||
class GlobalAnalyticsHandler(
|
||||
private val analyticsHandlers: List<AnalyticsEventHandler>,
|
||||
) : GlobalAnalyticsEventHandler {
|
||||
|
||||
override fun handleEvent(event: String, params: Map<String, String>) {
|
||||
analyticsHandlers.forEach { it.handleEvent(event, params) }
|
||||
}
|
||||
|
||||
override fun handleAnalyticsEvent(
|
||||
event: AnalyticsEvent,
|
||||
params: Map<String, String>,
|
||||
card: Card?,
|
||||
blockchain: String?,
|
||||
params: Map<String, String>
|
||||
) {
|
||||
analyticsHandlers.forEach { it.triggerEvent(event, card, blockchain, params) }
|
||||
analyticsHandlers.forEach { it.handleAnalyticsEvent(event, params, card, blockchain) }
|
||||
}
|
||||
|
||||
override fun triggerEvent(event: String, params: Map<String, String>) {
|
||||
analyticsHandlers.forEach { it.triggerEvent(event, params) }
|
||||
override fun handleErrorEvent(error: Throwable, params: Map<String, String>) {
|
||||
analyticsHandlers.filterIsInstance<ErrorEventLogger>().forEach {
|
||||
it.logErrorEvent(error, params)
|
||||
}
|
||||
}
|
||||
|
||||
override fun logCardSdkError(
|
||||
override fun handleCardSdkErrorEvent(
|
||||
error: TangemSdkError,
|
||||
actionToLog: Analytics.ActionToLog,
|
||||
parameters: Map<AnalyticsParam, String>?,
|
||||
card: Card?
|
||||
action: Analytics.ActionToLog,
|
||||
params: Map<AnalyticsParam, String>,
|
||||
card: Card?,
|
||||
) {
|
||||
analyticsHandlers.forEach { it.logCardSdkError(error, actionToLog, parameters, card) }
|
||||
analyticsHandlers.filterIsInstance<CardSdkErrorEventHandler>().forEach {
|
||||
it.handleCardSdkErrorEvent(error, action, params, card)
|
||||
}
|
||||
}
|
||||
|
||||
override fun logError(error: Throwable, params: Map<String, String>) {
|
||||
analyticsHandlers.forEach { it.logError(error, params) }
|
||||
override fun handleBlockchainSdkErrorEvent(
|
||||
error: BlockchainError,
|
||||
action: Analytics.ActionToLog,
|
||||
params: Map<AnalyticsParam, String>,
|
||||
card: Card?,
|
||||
) {
|
||||
analyticsHandlers.filterIsInstance<BlockchainSdkErrorEventHandler>().forEach {
|
||||
it.handleBlockchainSdkErrorEvent(error, action, params, card)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getOrderEvent(): String {
|
||||
return ""
|
||||
override fun handleShopifyOrderEvent(order: Storefront.Order) {
|
||||
analyticsHandlers.filterIsInstance<ShopifyOrderEventHandler>().forEach {
|
||||
it.handleShopifyOrderEvent(order)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getOrderParams(order: Storefront.Order): Map<String, String> {
|
||||
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(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,18 @@
|
|||
package com.tangem.tap.common.analytics.api
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
interface EventLogger {
|
||||
fun logEvent(
|
||||
event: String,
|
||||
params: Map<String, String> = emptyMap(),
|
||||
)
|
||||
}
|
||||
|
||||
interface ErrorEventLogger {
|
||||
fun logErrorEvent(
|
||||
error: Throwable,
|
||||
params: Map<String, String> = emptyMap(),
|
||||
)
|
||||
}
|
||||
|
|
@ -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<String, String> = emptyMap(),
|
||||
)
|
||||
|
||||
fun handleAnalyticsEvent(
|
||||
event: AnalyticsEvent,
|
||||
params: Map<String, String> = emptyMap(),
|
||||
card: Card? = null,
|
||||
blockchain: String? = null,
|
||||
)
|
||||
|
||||
fun prepareParams(
|
||||
card: Card?,
|
||||
blockchain: String? = null,
|
||||
params: Map<String, String> = emptyMap(),
|
||||
): Map<String, String> = 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<String, String> = emptyMap(),
|
||||
)
|
||||
}
|
||||
|
||||
interface SdkErrorEventHandler : CardSdkErrorEventHandler, BlockchainSdkErrorEventHandler
|
||||
|
||||
interface CardSdkErrorEventHandler {
|
||||
fun handleCardSdkErrorEvent(
|
||||
error: TangemSdkError,
|
||||
action: Analytics.ActionToLog,
|
||||
params: Map<AnalyticsParam, String> = emptyMap(),
|
||||
card: Card? = null,
|
||||
)
|
||||
}
|
||||
|
||||
interface BlockchainSdkErrorEventHandler {
|
||||
fun handleBlockchainSdkErrorEvent(
|
||||
error: BlockchainError,
|
||||
action: Analytics.ActionToLog,
|
||||
params: Map<AnalyticsParam, String> = mapOf(),
|
||||
card: Card? = null,
|
||||
)
|
||||
}
|
||||
|
||||
interface ShopifyOrderEventHandler {
|
||||
fun handleShopifyOrderEvent(order: Storefront.Order)
|
||||
}
|
||||
|
|
@ -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<String, String>,
|
||||
)
|
||||
|
||||
internal class CardSdkErrorConverter : Converter<CardSdkErrorConverter.Model, ErrorEvent?> {
|
||||
|
||||
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<AnalyticsParam, String>,
|
||||
val cardParams: Map<String, String>,
|
||||
)
|
||||
}
|
||||
|
||||
internal class BlockchainSdkErrorConverter(
|
||||
private val cardSdkErrorConverter: CardSdkErrorConverter,
|
||||
) : Converter<BlockchainSdkErrorConverter.Model, ErrorEvent?> {
|
||||
|
||||
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<AnalyticsParam, String>,
|
||||
val cardParams: Map<String, String>,
|
||||
)
|
||||
}
|
||||
|
||||
internal fun Map<AnalyticsParam, String>.convertToParamValue(): Map<String, String> {
|
||||
return mutableMapOf<String, String>().also { newMap ->
|
||||
this.forEach {
|
||||
newMap[it.key.param] = it.value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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<String, String>) {
|
||||
client.logEvent(event, params)
|
||||
}
|
||||
|
||||
override fun handleAnalyticsEvent(
|
||||
event: AnalyticsEvent,
|
||||
params: Map<String, String>,
|
||||
card: Card?,
|
||||
blockchain: String?,
|
||||
) {
|
||||
handleEvent(event.event, prepareParams(card, blockchain, params))
|
||||
}
|
||||
}
|
||||
|
|
@ -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<String, String>) {
|
||||
client.logEvent(event, ParamsToJSONObjectConverter().convert(params))
|
||||
}
|
||||
}
|
||||
|
||||
private class ParamsToJSONObjectConverter : Converter<Map<String, String>, JSONObject> {
|
||||
override fun convert(value: Map<String, String>): JSONObject = JSONObject().apply {
|
||||
value.forEach { this.put(it.key, it.value) }
|
||||
}
|
||||
}
|
||||
|
|
@ -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<String, String>) {
|
||||
logger.logEvent(event, params)
|
||||
}
|
||||
}
|
||||
|
|
@ -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<String, String>) {
|
||||
client.logEvent(event, params)
|
||||
}
|
||||
|
||||
override fun handleAnalyticsEvent(
|
||||
event: AnalyticsEvent,
|
||||
params: Map<String, String>,
|
||||
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<Storefront.Order, Map<String, String>> {
|
||||
|
||||
override fun convert(value: Storefront.Order): Map<String, String> {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
|
@ -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<String, String>) {
|
||||
appsFlyerLib.logEvent(context, event, params)
|
||||
}
|
||||
}
|
||||
|
|
@ -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<String, String>) {
|
||||
logger.logEvent(event, params)
|
||||
}
|
||||
}
|
||||
|
|
@ -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<String, String>) {
|
||||
client.logEvent(event, params)
|
||||
}
|
||||
|
||||
override fun handleAnalyticsEvent(
|
||||
event: AnalyticsEvent,
|
||||
params: Map<String, String>,
|
||||
card: Card?,
|
||||
blockchain: String?,
|
||||
) {
|
||||
handleEvent(event.name, prepareParams(card, blockchain, params))
|
||||
}
|
||||
|
||||
override fun handleErrorEvent(error: Throwable, params: Map<String, String>) {
|
||||
client.logErrorEvent(error, params)
|
||||
}
|
||||
|
||||
override fun handleCardSdkErrorEvent(
|
||||
error: TangemSdkError,
|
||||
action: Analytics.ActionToLog,
|
||||
params: Map<AnalyticsParam, String>,
|
||||
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<AnalyticsParam, String>,
|
||||
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<Storefront.Order, Map<String, String>> {
|
||||
override fun convert(value: Storefront.Order): Map<String, String> {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
|
@ -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<String, String>) {
|
||||
fbAnalytics.logEvent(event, params.toBundle())
|
||||
}
|
||||
|
||||
override fun logErrorEvent(error: Throwable, params: Map<String, String>) {
|
||||
params.forEach { fbCrashlytics.setCustomKey(it.key, it.value) }
|
||||
fbCrashlytics.recordException(error)
|
||||
}
|
||||
|
||||
private fun Map<String, String>.toBundle(): Bundle = bundleOf(*this.toList().toTypedArray())
|
||||
|
||||
companion object {
|
||||
const val ORDER_EVENT = FirebaseAnalytics.Event.PURCHASE
|
||||
}
|
||||
}
|
||||
|
|
@ -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<String, String>) {
|
||||
logger.logEvent(event, params)
|
||||
}
|
||||
|
||||
override fun logErrorEvent(error: Throwable, params: Map<String, String>) {
|
||||
logger.logErrorEvent(error, params)
|
||||
}
|
||||
}
|
||||
|
|
@ -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<AnalyticsParam, String>? = 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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) }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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<Blockchain>? = null,
|
||||
messageRes: Int? = null,
|
||||
): CompletionResult<ScanResponse> {
|
||||
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<ScanResponse>,
|
||||
) {
|
||||
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<Card> {
|
||||
return runTaskAsyncReturnOnMain(
|
||||
CreateWalletAndRescanTask(), cardId,
|
||||
CreateWalletAndRescanTask(),
|
||||
cardId,
|
||||
initialMessage = Message(context.getString(R.string.initial_message_create_wallet_body)),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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 ?: ""),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
8
common/src/main/java/com/tangem/common/Converter.kt
Normal file
8
common/src/main/java/com/tangem/common/Converter.kt
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
package com.tangem.common
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
interface Converter<I, O> {
|
||||
fun convert(value: I): O
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue