Updated on 2026-08-14

This commit is contained in:
Tangem 2022-11-09 06:08:40 +05:00
parent fec0f14952
commit 5030c571c5
32 changed files with 298 additions and 658 deletions

View file

@ -15,7 +15,7 @@ import com.tangem.network.common.MoshiConverter
import com.tangem.tap.common.AndroidAssetReader
import com.tangem.tap.common.AssetReader
import com.tangem.tap.common.analytics.Analytics
import com.tangem.tap.common.analytics.AnalyticsHandlersFactory
import com.tangem.tap.common.analytics.AnalyticsFactory
import com.tangem.tap.common.analytics.api.AnalyticsHandlerBuilder
import com.tangem.tap.common.analytics.filters.BasicSignInFilter
import com.tangem.tap.common.analytics.filters.BasicTopUpFilter
@ -110,10 +110,14 @@ class TapApplication : Application(), ImageLoaderFactory {
}
private fun initAnalytics(application: Application, config: Config) {
val factory = AnalyticsHandlersFactory()
factory.addBuilder(AmplitudeAnalyticsHandler.Builder())
factory.addBuilder(AppsFlyerAnalyticsHandler.Builder())
factory.addBuilder(FirebaseAnalyticsHandler.Builder())
val factory = AnalyticsFactory()
factory.addHandlerBuilder(AmplitudeAnalyticsHandler.Builder())
factory.addHandlerBuilder(AppsFlyerAnalyticsHandler.Builder())
factory.addHandlerBuilder(FirebaseAnalyticsHandler.Builder())
factory.addFilter(ShopPurchasedEventFilter())
factory.addFilter(BasicSignInFilter())
factory.addFilter(BasicTopUpFilter(preferencesStorage.toppedUpWalletStorage))
val buildData = AnalyticsHandlerBuilder.Data(
application = application,
@ -122,14 +126,7 @@ class TapApplication : Application(), ImageLoaderFactory {
logConfig = LogConfig.analyticsHandlers,
jsonConverter = MoshiJsonConverter.INSTANCE,
)
factory.build(buildData).forEach {
Analytics.addHandler(it.id(), it)
}
listOf(
ShopPurchasedEventFilter(),
BasicSignInFilter(),
BasicTopUpFilter(preferencesStorage.toppedUpWalletStorage),
).forEach { Analytics.addFilter(it) }
factory.build(Analytics, buildData)
}
private fun initFeedbackManager(context: Context, preferencesStorage: PreferencesStorage, config: Config) {

View file

@ -1,22 +1,14 @@
package com.tangem.tap.common.analytics
import com.tangem.blockchain.common.BlockchainError
import com.tangem.common.card.Card
import com.tangem.common.core.TangemSdkError
import com.tangem.domain.common.FeatureCoroutineExceptionHandler
import com.tangem.tap.common.analytics.api.AnalyticsEventFilter
import com.tangem.tap.common.analytics.api.AnalyticsEventHandler
import com.tangem.tap.common.analytics.api.AnalyticsFilterHolder
import com.tangem.tap.common.analytics.api.AnalyticsHandler
import com.tangem.tap.common.analytics.api.AnalyticsHandlerHolder
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.ParamsInterceptor
import com.tangem.tap.common.analytics.api.ParamsInterceptorHolder
import com.tangem.tap.common.analytics.api.SdkErrorEventHandler
import com.tangem.tap.common.analytics.events.AnalyticsEvent
import com.tangem.tap.common.extensions.filterNotNull
import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
@ -28,33 +20,26 @@ import java.util.concurrent.Executors
[REDACTED_AUTHOR]
*/
interface GlobalAnalyticsEventHandler : AnalyticsEventHandler,
ErrorEventHandler,
SdkErrorEventHandler,
AnalyticsHandlerHolder,
AnalyticsFilterHolder,
ParamsInterceptorHolder {
fun send(event: AnalyticsEvent, card: Card? = null, blockchain: String? = null)
}
ParamsInterceptorHolder
object Analytics : GlobalAnalyticsEventHandler {
private val analyticsScope: CoroutineScope by lazy { createScope() }
private val handlers = mutableMapOf<String, AnalyticsEventHandler>()
private val analyticsFilters = mutableSetOf<AnalyticsEventFilter>()
private val handlers = mutableMapOf<String, AnalyticsHandler>()
private val paramsInterceptors = mutableMapOf<String, ParamsInterceptor>()
private val analyticsFilters = mutableSetOf<AnalyticsEventFilter>()
private val analyticsHandlers: List<AnalyticsEventHandler>
private val analyticsHandlers: List<AnalyticsHandler>
get() = handlers.values.toList()
override fun id(): String = analyticsHandlers.joinToString(", ") { it.id() }
override fun addHandler(name: String, handler: AnalyticsEventHandler) {
override fun addHandler(name: String, handler: AnalyticsHandler) {
handlers[name] = handler
}
override fun removeHandler(name: String): AnalyticsEventHandler? {
override fun removeHandler(name: String): AnalyticsHandler? {
return handlers.remove(name)
}
@ -74,84 +59,29 @@ object Analytics : GlobalAnalyticsEventHandler {
return paramsInterceptors.remove(interceptor.id())
}
override fun send(event: String, params: Map<String, String>) {
override fun send(event: AnalyticsEvent) {
analyticsScope.launch {
analyticsHandlers.forEach { it.send(event, params.interceptParams()) }
}
}
override fun send(event: AnalyticsEvent, card: Card?, blockchain: String?) {
analyticsScope.launch {
val eventString = prepareEventString(event.category, event.event)
val eventParams = prepareParams(card, blockchain, event.interceptParams())
event.params = applyParamsInterceptors(event)
val eventFilter = analyticsFilters.firstOrNull { it.canBeAppliedTo(event) }
when {
eventFilter == null -> analyticsHandlers.forEach { handler -> handler.send(eventString, eventParams) }
eventFilter == null -> analyticsHandlers.forEach { handler -> handler.send(event) }
eventFilter.canBeSent(event) -> {
analyticsHandlers
.filter { handler -> eventFilter.canBeConsumedByHandler(handler, event) }
.forEach { handler -> handler.send(eventString, eventParams) }
.forEach { handler -> handler.send(event) }
}
}
}
}
override fun handleAnalyticsEvent(
event: AnalyticsEventAnOld,
params: Map<String, String>,
card: Card?,
blockchain: String?,
) {
analyticsScope.launch {
analyticsHandlers.forEach {
it.handleAnalyticsEvent(event, params.interceptParams(), card, blockchain)
}
}
}
private fun applyParamsInterceptors(event: AnalyticsEvent): MutableMap<String, String> {
val interceptedParams = event.params.toMutableMap()
paramsInterceptors.values
.filter { it.canBeAppliedTo(event) }
.forEach { it.intercept(interceptedParams) }
override fun send(error: Throwable, params: Map<String, String>) {
analyticsScope.launch {
analyticsHandlers.filterIsInstance<ErrorEventLogger>().forEach {
it.logErrorEvent(error, params)
}
}
}
override fun send(
error: TangemSdkError,
action: AnalyticsAnOld.ActionToLog,
params: Map<AnalyticsParamAnOld, String>,
card: Card?,
) {
analyticsScope.launch {
analyticsHandlers.filterIsInstance<CardSdkErrorEventHandler>().forEach {
it.send(error, action, params, card)
}
}
}
override fun send(
error: BlockchainError,
action: AnalyticsAnOld.ActionToLog,
params: Map<AnalyticsParamAnOld, String>,
card: Card?,
) {
analyticsScope.launch {
analyticsHandlers.filterIsInstance<BlockchainSdkErrorEventHandler>().forEach {
it.send(error, action, params, card)
}
}
}
private fun AnalyticsEvent.interceptParams(): Map<String, String> = params.interceptParams()
private fun Map<String, String>.interceptParams(): Map<String, String> {
return this.toMutableMap().apply { interceptParams() }
}
private fun MutableMap<String, String>.interceptParams() {
paramsInterceptors.values.forEach { it.intercept(this) }
return interceptedParams
}
private fun createScope(): CoroutineScope {
@ -160,43 +90,4 @@ object Analytics : GlobalAnalyticsEventHandler {
val exHandler = FeatureCoroutineExceptionHandler.create(name)
return CoroutineScope(Job() + dispatcher + CoroutineName(name) + exHandler)
}
}
fun GlobalAnalyticsEventHandler.logWcEvent(event: AnalyticsAnOld.WcAnalyticsEvent) {
when (event) {
is AnalyticsAnOld.WcAnalyticsEvent.Action -> {
handleAnalyticsEvent(
event = AnalyticsEventAnOld.WC_SUCCESS_RESPONSE,
params = mapOf(
AnalyticsParamAnOld.WALLET_CONNECT_ACTION.param to event.action.name,
),
)
}
is AnalyticsAnOld.WcAnalyticsEvent.Error -> {
val params = mapOf(
AnalyticsParamAnOld.WALLET_CONNECT_ACTION.param to event.action?.name,
AnalyticsParamAnOld.ERROR_DESCRIPTION.param to event.error.message,
).filterNotNull()
send(event.error, params)
}
is AnalyticsAnOld.WcAnalyticsEvent.InvalidRequest ->
handleAnalyticsEvent(
event = AnalyticsEventAnOld.WC_INVALID_REQUEST,
params = mapOf(
AnalyticsParamAnOld.WALLET_CONNECT_REQUEST.param to event.json,
).filterNotNull(),
)
is AnalyticsAnOld.WcAnalyticsEvent.Session -> {
val analyticsEvent = when (event.event) {
AnalyticsAnOld.WcSessionEvent.Disconnect -> AnalyticsEventAnOld.WC_SESSION_DISCONNECTED
AnalyticsAnOld.WcSessionEvent.Connect -> AnalyticsEventAnOld.WC_NEW_SESSION
}
handleAnalyticsEvent(
event = analyticsEvent,
params = mapOf(
AnalyticsParamAnOld.WALLET_CONNECT_DAPP_URL.param to event.url,
).filterNotNull(),
)
}
}
}

View file

@ -1,71 +0,0 @@
package com.tangem.tap.common.analytics
enum class AnalyticsEventAnOld(val event: String) {
CARD_IS_SCANNED("card_is_scanned"),
TRANSACTION_IS_SENT("transaction_is_sent"),
READY_TO_SCAN("ready_to_scan"),
DEMO_MODE_ACTIVATED("demo_mode_activated"),
APP_RATING_DISPLAYED("rate_app_warning_displayed"),
APP_RATING_DISMISS("dismiss_rate_app_warning"),
APP_RATING_NEGATIVE("negative_rate_app_feedback"),
APP_RATING_POSITIVE("positive_rate_app_feedback"),
WC_SUCCESS_RESPONSE("wallet_connect_success_response"),
WC_INVALID_REQUEST("wallet_connect_invalid_request"),
WC_NEW_SESSION("wallet_connect_new_session"),
WC_SESSION_DISCONNECTED("wallet_connect_session_disconnected"),
GET_CARD("get_card"),
}
enum class AnalyticsParamAnOld(val param: String) {
BLOCKCHAIN("blockchain"),
CARD_ID("cardId"),
BATCH_ID("batch_id"),
FIRMWARE("firmware"),
ACTION("action"),
ERROR_DESCRIPTION("error_description"),
ERROR_CODE("error_code"),
NEW_SECURITY_OPTION("new_security_option"),
ERROR_KEY("Tangem SDK error key"),
WALLET_CONNECT_ACTION("wallet_connect_action"),
WALLET_CONNECT_REQUEST("wallet_connect_request"),
WALLET_CONNECT_DAPP_URL("wallet_connect_dapp_url"),
SOURCE("source"),
}
enum class GetCardSourceParamsAnOld(val param: String) {
WELCOME("welcome"),
ONBOARDING_BUY_MORE("wallet_onboaring_buy_more_cards"),
ONBOARDING("wallet_onboarding")
}
object AnalyticsAnOld {
enum class ActionToLog(val key: String) {
Scan("tap_scan_task"),
SendTransaction("send_transaction"),
WalletConnectSign("wallet_connect_personal_sign"),
WalletConnectTransaction("wallet_connect_tx_sign"),
ReadPinSettings("read_pin_settings"),
ChangeSecOptions("change_sec_options"),
CreateWallet("create_wallet"),
PurgeWallet("purge_wallet"),
WriteIssuerData("write_issuer_data"),
}
enum class WcSessionEvent { Disconnect, Connect }
enum class WcAction { PersonalSign, SignTransaction, SendTransaction }
sealed class WcAnalyticsEvent {
data class Error(val error: Throwable, val action: WcAction?) :
WcAnalyticsEvent()
data class Session(val event: WcSessionEvent, val url: String?) : WcAnalyticsEvent()
data class Action(val action: WcAction) : WcAnalyticsEvent()
data class InvalidRequest(val json: String?) : WcAnalyticsEvent()
}
}

View file

@ -0,0 +1,37 @@
package com.tangem.tap.common.analytics
import com.tangem.tap.common.analytics.api.AnalyticsEventFilter
import com.tangem.tap.common.analytics.api.AnalyticsHandlerBuilder
import com.tangem.tap.common.analytics.api.ParamsInterceptor
/**
[REDACTED_AUTHOR]
*/
class AnalyticsFactory {
private val builders = mutableListOf<AnalyticsHandlerBuilder>()
private val filters = mutableListOf<AnalyticsEventFilter>()
private val interceptors = mutableListOf<ParamsInterceptor>()
fun addHandlerBuilder(builder: AnalyticsHandlerBuilder) {
builders.add(builder)
}
fun addFilter(filter: AnalyticsEventFilter) {
filters.add(filter)
}
fun addParamsInterceptor(interceptor: ParamsInterceptor) {
interceptors.add(interceptor)
}
fun build(analytics: Analytics, data: AnalyticsHandlerBuilder.Data) {
builders.mapNotNull { it.build(data) }.forEach { analytics.addHandler(it.id(), it) }
filters.forEach { analytics.addFilter(it) }
interceptors.forEach { analytics.addParamsInterceptor(it) }
builders.clear()
filters.clear()
interceptors.clear()
}
}

View file

@ -1,23 +0,0 @@
package com.tangem.tap.common.analytics
import com.tangem.tap.common.analytics.api.AnalyticsEventHandler
import com.tangem.tap.common.analytics.api.AnalyticsHandlerBuilder
/**
[REDACTED_AUTHOR]
*/
class AnalyticsHandlersFactory {
private val builders = mutableListOf<AnalyticsHandlerBuilder>()
fun addBuilder(builder: AnalyticsHandlerBuilder) {
builders.add(builder)
}
fun build(data: AnalyticsHandlerBuilder.Data): List<AnalyticsEventHandler> {
val handlers = builders.mapNotNull { it.build(data) }
builders.clear()
return handlers
}
}

View file

@ -1,37 +0,0 @@
package com.tangem.tap.common.analytics.api
import com.tangem.blockchain.common.BlockchainError
import com.tangem.common.card.Card
import com.tangem.common.core.TangemSdkError
import com.tangem.tap.common.analytics.AnalyticsAnOld
import com.tangem.tap.common.analytics.AnalyticsParamAnOld
/**
[REDACTED_AUTHOR]
*/
interface ErrorEventHandler {
fun send(
error: Throwable,
params: Map<String, String> = emptyMap(),
)
}
interface SdkErrorEventHandler : CardSdkErrorEventHandler, BlockchainSdkErrorEventHandler
interface CardSdkErrorEventHandler {
fun send(
error: TangemSdkError,
action: AnalyticsAnOld.ActionToLog,
params: Map<AnalyticsParamAnOld, String> = emptyMap(),
card: Card? = null,
)
}
interface BlockchainSdkErrorEventHandler {
fun send(
error: BlockchainError,
action: AnalyticsAnOld.ActionToLog,
params: Map<AnalyticsParamAnOld, String> = mapOf(),
card: Card? = null,
)
}

View file

@ -21,7 +21,7 @@ interface AnalyticsEventFilter {
/**
* Performs a check to see if the event can be dispatched by a specific handler
*/
fun canBeConsumedByHandler(handler: AnalyticsEventHandler, event: AnalyticsEvent): Boolean
fun canBeConsumedByHandler(handler: AnalyticsHandler, event: AnalyticsEvent): Boolean
}
interface AnalyticsFilterHolder {

View file

@ -1,53 +1,44 @@
package com.tangem.tap.common.analytics.api
import android.app.Application
import com.tangem.common.card.Card
import com.tangem.common.json.MoshiJsonConverter
import com.tangem.domain.common.AnalyticsHandlersLogConfig
import com.tangem.tap.common.analytics.AnalyticsEventAnOld
import com.tangem.tap.common.analytics.events.AnalyticsParam
import com.tangem.tap.common.extensions.filterNotNull
import com.tangem.tap.common.analytics.events.AnalyticsEvent
import com.tangem.tap.domain.configurable.config.Config
/**
[REDACTED_AUTHOR]
*/
interface AnalyticsEventHandler {
fun send(event: AnalyticsEvent)
}
interface AnalyticsHandler : AnalyticsEventHandler {
fun id(): String
fun send(event: String, params: Map<String, String> = emptyMap())
@Deprecated("Migrate to AnalyticsEvent")
fun handleAnalyticsEvent(
event: AnalyticsEventAnOld,
params: Map<String, String> = emptyMap(),
card: Card? = null,
blockchain: String? = null,
) {
send(event.event, prepareParams(card, blockchain, params))
override fun send(event: AnalyticsEvent) {
send(prepareEventString(event), event.params)
}
fun prepareParams(
card: Card? = null,
blockchain: String? = null,
params: Map<String, String> = emptyMap(),
): Map<String, String> = mapOf(
AnalyticsParam.Firmware to card?.firmwareVersion?.stringValue,
AnalyticsParam.Blockchain to blockchain,
).filterNotNull() + params
fun prepareEventString(event: AnalyticsEvent): String = "[${event.category}] ${event.event}"
}
fun prepareEventString(category: String, event: String): String {
return "[$category] $event"
}
interface ErrorEventHandler {
fun send(
error: Throwable,
params: Map<String, String> = emptyMap(),
)
}
interface AnalyticsHandlerHolder {
fun addHandler(name: String, handler: AnalyticsEventHandler)
fun removeHandler(name: String): AnalyticsEventHandler?
fun addHandler(name: String, handler: AnalyticsHandler)
fun removeHandler(name: String): AnalyticsHandler?
}
interface AnalyticsHandlerBuilder {
fun build(data: Data): AnalyticsEventHandler?
fun build(data: Data): AnalyticsHandler?
data class Data(
val application: Application,

View file

@ -0,0 +1,70 @@
package com.tangem.tap.common.analytics.converters
import com.tangem.blockchain.common.BlockchainSdkError
import com.tangem.common.Converter
import com.tangem.common.core.TangemSdkError
import com.tangem.tap.common.analytics.events.AnalyticsParam
import com.tangem.tap.features.demo.DemoTransactionSender
/**
[REDACTED_AUTHOR]
*/
class AnalyticsErrorConverter : Converter<Throwable, Map<String, String>> {
fun canBeHandled(error: Throwable): Boolean {
return when (error) {
is TangemSdkError.UserCancelled -> false
is BlockchainSdkError -> !error.customMessage.contains(DemoTransactionSender.ID)
else -> true
}
}
override fun convert(value: Throwable): Map<String, String> {
return when (value) {
is BlockchainSdkError -> BlockchainSdkErrorConverter(CardSdkErrorConverter()).convert(value)
is TangemSdkError -> CardSdkErrorConverter().convert(value)
else -> ThrowableErrorConverter().convert(value)
}
}
}
private class ThrowableErrorConverter : Converter<Throwable, Map<String, String>> {
override fun convert(value: Throwable): Map<String, String> {
val unknown = "unknown"
return mapOf(
AnalyticsParam.ErrorKey to AnalyticsParam.Error.App.value,
AnalyticsParam.ErrorDescription to (value.message ?: value.cause?.message ?: unknown),
)
}
}
class CardSdkErrorConverter : Converter<TangemSdkError, Map<String, String>> {
override fun convert(value: TangemSdkError): Map<String, String> {
if (value is TangemSdkError.UserCancelled) return emptyMap()
return mapOf(
AnalyticsParam.ErrorKey to AnalyticsParam.Error.CardSdk.value,
AnalyticsParam.ErrorCode to value.code.toString(),
AnalyticsParam.ErrorDescription to value.toString(),
)
}
}
private class BlockchainSdkErrorConverter(
private val cardSdkErrorConverter: CardSdkErrorConverter,
) : Converter<BlockchainSdkError, Map<String, String>> {
override fun convert(value: BlockchainSdkError): Map<String, String> {
if (value.customMessage.contains(DemoTransactionSender.ID)) return emptyMap()
if (value is BlockchainSdkError.WrappedTangemError) {
return (value.tangemError as? TangemSdkError)?.let { cardSdkErrorConverter.convert(it) } ?: emptyMap()
}
return mapOf(
AnalyticsParam.ErrorKey to AnalyticsParam.Error.BlockchainSdk.value,
AnalyticsParam.ErrorCode to value.code.toString(),
AnalyticsParam.ErrorDescription to "${value.javaClass.simpleName}: ${value.customMessage}",
)
}
}

View file

@ -1,88 +0,0 @@
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.AnalyticsAnOld
import com.tangem.tap.common.analytics.AnalyticsParamAnOld
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[AnalyticsParamAnOld.ACTION.param] = value.action.key
mutableParams[AnalyticsParamAnOld.ERROR_CODE.param] = value.error.code.toString()
mutableParams[AnalyticsParamAnOld.ERROR_DESCRIPTION.param] = value.error.javaClass.simpleName
mutableParams[AnalyticsParamAnOld.ERROR_KEY.param] = "TangemSdkError"
return ErrorEvent(TangemSdk.map(value.error), mutableParams)
}
internal data class Model(
val error: TangemSdkError,
val action: AnalyticsAnOld.ActionToLog,
val params: Map<AnalyticsParamAnOld, 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[AnalyticsParamAnOld.ACTION.param] = value.action.key
mutableParams[AnalyticsParamAnOld.ERROR_CODE.param] = error.code.toString()
mutableParams[AnalyticsParamAnOld.ERROR_DESCRIPTION.param] = "${error.javaClass.simpleName}: ${error.customMessage}"
mutableParams[AnalyticsParamAnOld.ERROR_KEY.param] = "BlockchainSdkError"
return ErrorEvent(value.error, mutableParams)
}
data class Model(
val error: BlockchainError,
val action: AnalyticsAnOld.ActionToLog,
val params: Map<AnalyticsParamAnOld, String>,
val cardParams: Map<String, String>,
)
}
internal fun Map<AnalyticsParamAnOld, String>.convertToParamValue(): Map<String, String> {
return mutableMapOf<String, String>().also { newMap ->
this.forEach {
newMap[it.key.param] = it.value
}
}
}

View file

@ -6,6 +6,7 @@ package com.tangem.tap.common.analytics.events
sealed class AnalyticsEvent(
val category: String,
val event: String,
val params: Map<String, String> = mapOf(),
var params: Map<String, String> = mapOf(),
val error: Throwable? = null,
var filterData: Any? = null,
)

View file

@ -54,11 +54,16 @@ sealed class AnalyticsParam {
}
}
sealed class Error(val value: String) {
object App : Error("App Error")
object CardSdk : Error("Card Sdk Error")
object BlockchainSdk : Error("Blockchain Sdk Error")
}
companion object Key {
const val BatchId = "Batch"
// old keys
const val Blockchain = "blockchain"
const val Firmware = "firmware"
const val ErrorDescription = "Error Description"
const val ErrorCode = "Error Code"
const val ErrorKey = "Error Key"
}
}

View file

@ -6,7 +6,8 @@ package com.tangem.tap.common.analytics.events
sealed class Basic(
event: String,
params: Map<String, String> = mapOf(),
) : AnalyticsEvent("Basic", event, params) {
error: Throwable? = null,
) : AnalyticsEvent("Basic", event, params, error) {
class SignedIn(
state: AnalyticsParam.CardBalanceState,
@ -25,4 +26,9 @@ sealed class Basic(
event = "Topped up",
params = mapOf("Currency" to currency.value),
)
class ScanError(error: Throwable) : Basic(
event = "Scan",
error = error,
)
}

View file

@ -9,7 +9,8 @@ sealed class Settings(
category: String = "Settings",
event: String,
params: Map<String, String> = mapOf(),
) : AnalyticsEvent(category, event, params) {
error: Throwable? = null,
) : AnalyticsEvent(category, event, params, error) {
class ScreenOpened : Settings(event = "Settings Screen Opened")
class ButtonChat : Settings(event = "Button - Chat")
@ -28,10 +29,15 @@ sealed class Settings(
sealed class CardSettings(
event: String,
params: Map<String, String> = mapOf(),
) : Settings("Settings / Card Settings", event, params) {
error: Throwable? = null,
) : Settings("Settings / Card Settings", event, params, error) {
class ButtonFactoryReset : CardSettings("Button - Factory Reset")
class FactoryResetFinished : CardSettings("Factory Reset Finished")
class FactoryResetFinished(error: Throwable? = null) : CardSettings(
event = "Factory Reset Finished",
error = error,
)
class UserCodeChanged : CardSettings("User Code Changed")
class ButtonChangeSecurityMode : CardSettings("Button - Change Security Mode")
@ -40,9 +46,10 @@ sealed class Settings(
params = mapOf("Type" to type.value),
)
class SecurityModeChanged(mode: AnalyticsParam.SecurityMode) : CardSettings(
class SecurityModeChanged(mode: AnalyticsParam.SecurityMode, error: Throwable? = null) : CardSettings(
event = "Security Mode Changed",
params = mapOf("Mode" to mode.value),
error = error,
)
}

View file

@ -9,7 +9,8 @@ sealed class Token(
category: String,
event: String,
params: Map<String, String> = mapOf(),
) : AnalyticsEvent(category, event, params) {
error: Throwable? = null,
) : AnalyticsEvent(category, event, params, error) {
class Refreshed : Token("Token", "Refreshed")
class ButtonExplore : Token("Token", "Button - Explore")
@ -57,16 +58,18 @@ sealed class Token(
sealed class Send(
event: String,
params: Map<String, String> = mapOf(),
) : Token("Token / Send", event, params) {
error: Throwable? = null,
) : Token("Token / Send", event, params, error) {
class ScreenOpened : Send("Send Screen Opened")
class ButtonPaste : Send("Button - Paste")
class ButtonQRCode : Send("Button - QR Code")
class ButtonSwapCurrency : Send("Button - Swap Currency")
class TransactionSent(type: CurrencyType) : Send(
class TransactionSent(type: CurrencyType, error: Throwable? = null) : Send(
event = "Transaction Sent",
params = mapOf("Token" to type.value),
error = error,
)
}

View file

@ -6,9 +6,13 @@ package com.tangem.tap.common.analytics.events
sealed class WalletConnect(
event: String,
params: Map<String, String> = mapOf(),
) : AnalyticsEvent("Wallet Connect", event, params) {
error: Throwable? = null,
) : AnalyticsEvent("Wallet Connect", event, params, error) {
class NewSessionEstablished : WalletConnect("New Session Established")
class SessionDisconnected : WalletConnect("Session Disconnected")
class RequestSigned : WalletConnect("Request Signed")
class SignError(error: Throwable) : WalletConnect("Sign", error = error)
class TransactionError(error: Throwable) : WalletConnect("Transaction", error = error)
}

View file

@ -1,7 +1,7 @@
package com.tangem.tap.common.analytics.filters
import com.tangem.tap.common.analytics.api.AnalyticsEventFilter
import com.tangem.tap.common.analytics.api.AnalyticsEventHandler
import com.tangem.tap.common.analytics.api.AnalyticsHandler
import com.tangem.tap.common.analytics.events.AnalyticsEvent
import com.tangem.tap.common.analytics.events.Basic
@ -23,5 +23,5 @@ class BasicSignInFilter : AnalyticsEventFilter {
return canBeSent
}
override fun canBeConsumedByHandler(handler: AnalyticsEventHandler, event: AnalyticsEvent): Boolean = true
override fun canBeConsumedByHandler(handler: AnalyticsHandler, event: AnalyticsEvent): Boolean = true
}

View file

@ -2,7 +2,7 @@ package com.tangem.tap.common.analytics.filters
import com.tangem.common.extensions.guard
import com.tangem.tap.common.analytics.api.AnalyticsEventFilter
import com.tangem.tap.common.analytics.api.AnalyticsEventHandler
import com.tangem.tap.common.analytics.api.AnalyticsHandler
import com.tangem.tap.common.analytics.events.AnalyticsEvent
import com.tangem.tap.common.analytics.events.AnalyticsParam
import com.tangem.tap.common.analytics.events.Basic
@ -39,7 +39,7 @@ class BasicTopUpFilter(
}
}
override fun canBeConsumedByHandler(handler: AnalyticsEventHandler, event: AnalyticsEvent): Boolean = true
override fun canBeConsumedByHandler(handler: AnalyticsHandler, event: AnalyticsEvent): Boolean = true
data class Data(
val walletId: String,

View file

@ -1,7 +1,7 @@
package com.tangem.tap.common.analytics.filters
import com.tangem.tap.common.analytics.api.AnalyticsEventFilter
import com.tangem.tap.common.analytics.api.AnalyticsEventHandler
import com.tangem.tap.common.analytics.api.AnalyticsHandler
import com.tangem.tap.common.analytics.events.AnalyticsEvent
import com.tangem.tap.common.analytics.events.Shop
import com.tangem.tap.common.analytics.handlers.appsFlyer.AppsFlyerAnalyticsHandler
@ -16,7 +16,7 @@ class ShopPurchasedEventFilter : AnalyticsEventFilter {
override fun canBeSent(event: AnalyticsEvent): Boolean = true
override fun canBeConsumedByHandler(handler: AnalyticsEventHandler, event: AnalyticsEvent): Boolean {
override fun canBeConsumedByHandler(handler: AnalyticsHandler, event: AnalyticsEvent): Boolean {
return when (handler) {
is AppsFlyerAnalyticsHandler, is FirebaseAnalyticsHandler -> true
else -> false

View file

@ -1,11 +1,11 @@
package com.tangem.tap.common.analytics.handlers.amplitude
import com.tangem.tap.common.analytics.api.AnalyticsEventHandler
import com.tangem.tap.common.analytics.api.AnalyticsHandler
import com.tangem.tap.common.analytics.api.AnalyticsHandlerBuilder
class AmplitudeAnalyticsHandler(
private val client: AmplitudeAnalyticsClient,
) : AnalyticsEventHandler {
) : AnalyticsHandler {
override fun id(): String = ID
@ -18,7 +18,7 @@ class AmplitudeAnalyticsHandler(
}
class Builder : AnalyticsHandlerBuilder {
override fun build(data: AnalyticsHandlerBuilder.Data): AnalyticsEventHandler? = when {
override fun build(data: AnalyticsHandlerBuilder.Data): AnalyticsHandler? = when {
!data.isDebug -> AmplitudeClient(data.application, data.config.amplitudeApiKey)
data.isDebug && data.logConfig.amplitude -> AmplitudeLogClient(data.jsonConverter)
else -> null

View file

@ -1,11 +1,11 @@
package com.tangem.tap.common.analytics.handlers.appsFlyer
import com.tangem.tap.common.analytics.api.AnalyticsEventHandler
import com.tangem.tap.common.analytics.api.AnalyticsHandler
import com.tangem.tap.common.analytics.api.AnalyticsHandlerBuilder
class AppsFlyerAnalyticsHandler(
private val client: AppsFlyerAnalyticsClient,
) : AnalyticsEventHandler {
) : AnalyticsHandler {
override fun id(): String = ID
@ -18,7 +18,7 @@ class AppsFlyerAnalyticsHandler(
}
class Builder : AnalyticsHandlerBuilder {
override fun build(data: AnalyticsHandlerBuilder.Data): AnalyticsEventHandler? = when {
override fun build(data: AnalyticsHandlerBuilder.Data): AnalyticsHandler? = when {
!data.isDebug -> AppsFlyerClient(data.application, data.config.appsFlyerDevKey)
data.isDebug && data.logConfig.appsFlyer -> AppsFlyerLogClient(data.jsonConverter)
else -> null

View file

@ -1,20 +1,14 @@
package com.tangem.tap.common.analytics.handlers.firebase
import com.tangem.blockchain.common.BlockchainError
import com.tangem.common.card.Card
import com.tangem.common.core.TangemSdkError
import com.tangem.tap.common.analytics.AnalyticsAnOld
import com.tangem.tap.common.analytics.AnalyticsParamAnOld
import com.tangem.tap.common.analytics.api.AnalyticsEventHandler
import com.tangem.tap.common.analytics.api.AnalyticsHandler
import com.tangem.tap.common.analytics.api.AnalyticsHandlerBuilder
import com.tangem.tap.common.analytics.api.ErrorEventHandler
import com.tangem.tap.common.analytics.api.SdkErrorEventHandler
import com.tangem.tap.common.analytics.converters.BlockchainSdkErrorConverter
import com.tangem.tap.common.analytics.converters.CardSdkErrorConverter
import com.tangem.tap.common.analytics.converters.AnalyticsErrorConverter
import com.tangem.tap.common.analytics.events.AnalyticsEvent
class FirebaseAnalyticsHandler(
private val client: FirebaseAnalyticsClient,
) : AnalyticsEventHandler, ErrorEventHandler, SdkErrorEventHandler {
) : AnalyticsHandler, ErrorEventHandler {
override fun id(): String = ID
@ -22,43 +16,32 @@ class FirebaseAnalyticsHandler(
client.logEvent(event, params)
}
override fun send(event: AnalyticsEvent) {
when (event.error) {
null -> super.send(event)
else -> {
val errorConverter = AnalyticsErrorConverter()
if (!errorConverter.canBeHandled(event.error)) return
val errorParams = errorConverter.convert(event.error).toMutableMap()
errorParams["Category"] = event.category
errorParams["Event"] = event.event
errorParams.putAll(event.params)
send(event.error, errorParams)
}
}
}
override fun send(error: Throwable, params: Map<String, String>) {
client.logErrorEvent(error, params)
}
override fun send(
error: TangemSdkError,
action: AnalyticsAnOld.ActionToLog,
params: Map<AnalyticsParamAnOld, String>,
card: Card?,
) {
val model = CardSdkErrorConverter.Model(error, action, params, prepareParams(card))
val converter = CardSdkErrorConverter()
converter.convert(model)?.let {
send(it.throwable, it.params)
}
}
override fun send(
error: BlockchainError,
action: AnalyticsAnOld.ActionToLog,
params: Map<AnalyticsParamAnOld, String>,
card: Card?,
) {
val model = BlockchainSdkErrorConverter.Model(error, action, params, prepareParams(card))
val converter = BlockchainSdkErrorConverter(CardSdkErrorConverter())
converter.convert(model)?.let {
send(it.throwable, it.params)
}
}
companion object {
const val ID = "Firebase"
}
class Builder : AnalyticsHandlerBuilder {
override fun build(data: AnalyticsHandlerBuilder.Data): AnalyticsEventHandler? = when {
override fun build(data: AnalyticsHandlerBuilder.Data): AnalyticsHandler? = when {
!data.isDebug -> FirebaseClient()
data.isDebug && data.logConfig.firebase -> FirebaseLogClient(data.jsonConverter)
else -> null

View file

@ -27,14 +27,11 @@ import com.tangem.operations.pins.CheckUserCodesCommand
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.AnalyticsAnOld
import com.tangem.tap.common.analytics.AnalyticsEventAnOld
import com.tangem.tap.common.analytics.AnalyticsParamAnOld
import com.tangem.tap.common.analytics.events.Basic
import com.tangem.tap.domain.tasks.CreateWalletAndRescanTask
import com.tangem.tap.domain.tasks.product.ResetToFactorySettingsTask
import com.tangem.tap.domain.tasks.product.ScanProductTask
import com.tangem.tap.domain.tokens.UserTokensRepository
import com.tangem.tap.features.demo.DemoHelper
import com.tangem.wallet.R
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.isActive
@ -49,8 +46,6 @@ class TangemSdkManager(private val tangemSdk: TangemSdk, private val context: Co
additionalBlockchainsToDerive: Collection<Blockchain>? = null,
messageRes: Int? = null,
): CompletionResult<ScanResponse> {
Analytics.handleAnalyticsEvent(AnalyticsEventAnOld.READY_TO_SCAN, card = null)
val message = Message(context.getString(messageRes ?: R.string.initial_message_scan_header))
return runTaskAsyncReturnOnMain(
runnable = ScanProductTask(null, userTokensRepository, additionalBlockchainsToDerive),
@ -71,29 +66,10 @@ class TangemSdkManager(private val tangemSdk: TangemSdk, private val context: Co
private fun sendScanResultsToAnalytics(
result: CompletionResult<ScanResponse>,
) {
when (result) {
is CompletionResult.Success -> {
Analytics.handleAnalyticsEvent(
event = AnalyticsEventAnOld.CARD_IS_SCANNED,
card = result.data.card,
blockchain = result.data.walletData?.blockchain,
)
if (DemoHelper.isDemoCard(result.data)) {
Analytics.handleAnalyticsEvent(
event = AnalyticsEventAnOld.DEMO_MODE_ACTIVATED,
params = mapOf(AnalyticsParamAnOld.CARD_ID.param to result.data.card.cardId),
card = result.data.card,
blockchain = result.data.walletData?.blockchain,
)
}
if (result is CompletionResult.Failure) {
(result.error as? TangemSdkError)?.let { error ->
Analytics.send(Basic.ScanError(error))
}
is CompletionResult.Failure ->
(result.error as? TangemSdkError)?.let { error ->
Analytics.send(
error = error,
action = AnalyticsAnOld.ActionToLog.Scan,
)
}
}
}

View file

@ -7,19 +7,16 @@ import com.tangem.blockchain.extensions.Result
import com.tangem.common.CompletionResult
import com.tangem.common.KeyPair
import com.tangem.common.card.Card
import com.tangem.common.core.TangemSdkError
import com.tangem.common.extensions.hexToBytes
import com.tangem.common.extensions.toHexString
import com.tangem.domain.common.ScanResponse
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.AnalyticsAnOld
import com.tangem.tap.tangemSdkManager
class TwinCardsManager(
private val card: Card,
card: Card,
assetReader: AssetReader,
) {
@ -34,15 +31,7 @@ class TwinCardsManager(
val response = tangemSdkManager.runTaskAsync(CreateFirstTwinWalletTask(), currentCardId, message)
when (response) {
is CompletionResult.Success -> currentCardPublicKey = response.data.wallet.publicKey.toHexString()
is CompletionResult.Failure -> {
(response.error as? TangemSdkError)?.let { error ->
Analytics.send(
error,
AnalyticsAnOld.ActionToLog.CreateWallet,
card = card
)
}
}
is CompletionResult.Failure -> {}
}
return response
}
@ -57,22 +46,14 @@ class TwinCardsManager(
firstCardId = currentCardId,
issuerKeys = issuerKeyPair,
preparingMessage = preparingMessage,
creatingWalletMessage = creatingWalletMessage
creatingWalletMessage = creatingWalletMessage,
)
val response = tangemSdkManager.runTaskAsync(task, null, initialMessage)
when (response) {
is CompletionResult.Success -> {
secondCardPublicKey = response.data.wallet.publicKey.toHexString()
}
is CompletionResult.Failure -> {
(response.error as? TangemSdkError)?.let { error ->
Analytics.send(
error,
AnalyticsAnOld.ActionToLog.CreateWallet,
card = card
)
}
}
is CompletionResult.Failure -> {}
}
return response
}
@ -80,20 +61,11 @@ class TwinCardsManager(
suspend fun complete(message: Message): Result<ScanResponse> {
val response = tangemSdkManager.runTaskAsync(
FinalizeTwinTask(secondCardPublicKey!!.hexToBytes(), issuerKeyPair),
currentCardId, message
currentCardId, message,
)
return when (response) {
is CompletionResult.Success -> Result.Success(response.data)
is CompletionResult.Failure -> {
(response.error as? TangemSdkError)?.let { error ->
Analytics.send(
error,
AnalyticsAnOld.ActionToLog.WriteIssuerData,
card = card
)
}
Result.fromTangemSdkError(response.error)
}
is CompletionResult.Failure -> Result.fromTangemSdkError(response.error)
}
}
@ -102,13 +74,13 @@ class TwinCardsManager(
val issuer = getIssuers(reader).first { it.publicKey == publicKey }
return KeyPair(
publicKey = issuer.publicKey.hexToBytes(),
privateKey = issuer.privateKey.hexToBytes()
privateKey = issuer.privateKey.hexToBytes(),
)
}
private fun getAdapter(): JsonAdapter<List<Issuer>> {
return MoshiConverter.defaultMoshi().adapter(
Types.newParameterizedType(List::class.java, Issuer::class.java)
Types.newParameterizedType(List::class.java, Issuer::class.java),
)
}

View file

@ -5,9 +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.AnalyticsAnOld
import com.tangem.tap.common.analytics.events.WalletConnect
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
@ -226,11 +224,6 @@ class WalletConnectManager {
private fun onSessionClosed(session: WCSession) {
Analytics.send(WalletConnect.SessionDisconnected())
Analytics.logWcEvent(
AnalyticsAnOld.WcAnalyticsEvent.Session(
AnalyticsAnOld.WcSessionEvent.Disconnect, sessions[session]?.peerMeta?.url
)
)
sessions.remove(session)
walletConnectRepository.removeSession(session)
store.dispatchOnMain(WalletConnectAction.RemoveSession(session))
@ -376,11 +369,6 @@ class WalletConnectManager {
)
}
Analytics.send(WalletConnect.NewSessionEstablished())
Analytics.logWcEvent(
AnalyticsAnOld.WcAnalyticsEvent.Session(
AnalyticsAnOld.WcSessionEvent.Connect, peer.url,
),
)
}
}
}
@ -391,11 +379,11 @@ class WalletConnectManager {
}
client.onEthSendTransaction = { id: Long, transaction: WCEthereumTransaction ->
Timber.d("onEthSendTransaction: $transaction")
Analytics.logWcEvent(
AnalyticsAnOld.WcAnalyticsEvent.Action(
AnalyticsAnOld.WcAction.SendTransaction
)
)
// Analytics.logWcEvent(
// AnalyticsAnOld.WcAnalyticsEvent.Action(
// AnalyticsAnOld.WcAction.SendTransaction
// )
// )
sessions[client.session]?.toWalletConnectSession()?.let { sessionData ->
store.dispatchOnMain(
WalletConnectAction.HandleTransactionRequest(
@ -409,11 +397,11 @@ class WalletConnectManager {
}
client.onEthSignTransaction = { id: Long, transaction: WCEthereumTransaction ->
Timber.d("onEthSignTransaction: $transaction")
Analytics.logWcEvent(
AnalyticsAnOld.WcAnalyticsEvent.Action(
AnalyticsAnOld.WcAction.SignTransaction
)
)
// Analytics.logWcEvent(
// AnalyticsAnOld.WcAnalyticsEvent.Action(
// AnalyticsAnOld.WcAction.SignTransaction
// )
// )
sessions[client.session]?.toWalletConnectSession()?.let { sessionData ->
store.dispatchOnMain(
WalletConnectAction.HandleTransactionRequest(
@ -427,11 +415,11 @@ class WalletConnectManager {
}
client.onEthSign = { id: Long, message: WCEthereumSignMessage ->
Timber.d("onEthSign: $message")
Analytics.logWcEvent(
AnalyticsAnOld.WcAnalyticsEvent.Action(
AnalyticsAnOld.WcAction.PersonalSign
)
)
// Analytics.logWcEvent(
// AnalyticsAnOld.WcAnalyticsEvent.Action(
// AnalyticsAnOld.WcAction.PersonalSign
// )
// )
sessions[client.session]?.toWalletConnectSession()?.let { sessionData ->
store.dispatchOnMain(
WalletConnectAction.HandlePersonalSignRequest(
@ -503,11 +491,11 @@ class WalletConnectManager {
val message = EthSignHelper.tryToParseEthTypedMessage(request)
if (message != null) {
Timber.d("onEthSign_v4: $message")
Analytics.logWcEvent(
AnalyticsAnOld.WcAnalyticsEvent.Action(
AnalyticsAnOld.WcAction.PersonalSign
)
)
// Analytics.logWcEvent(
// AnalyticsAnOld.WcAnalyticsEvent.Action(
// AnalyticsAnOld.WcAction.PersonalSign
// )
// )
sessions[client.session]?.toWalletConnectSession()?.let { sessionData ->
store.dispatchOnMain(
WalletConnectAction.HandlePersonalSignRequest(

View file

@ -25,7 +25,7 @@ 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.analytics.AnalyticsAnOld
import com.tangem.tap.common.analytics.events.WalletConnect
import com.tangem.tap.common.extensions.safeUpdate
import com.tangem.tap.common.extensions.toFormattedString
import com.tangem.tap.features.details.redux.walletconnect.WalletConnectSession
@ -88,8 +88,8 @@ class WalletConnectSdkHelper {
extras = EthereumTransactionExtras(
data = transaction.data.removePrefix(HEX_PREFIX).hexToBytes(),
gasLimit = gas.toBigInteger(),
nonce = transaction.nonce?.hexToBigDecimal()?.toBigInteger()
)
nonce = transaction.nonce?.hexToBigDecimal()?.toBigInteger(),
),
)
val dialogData = TransactionRequestDialogData(
dAppName = session.peerMeta.name,
@ -101,7 +101,7 @@ class WalletConnectSdkHelper {
isEnoughFundsToSend = (balance - total) >= BigDecimal.ZERO,
session = session.session,
id = id,
type = type
type = type,
)
return WcTransactionData(
type = type,
@ -109,7 +109,7 @@ class WalletConnectSdkHelper {
session = session,
id = id,
walletManager = walletManager,
dialogData = dialogData
dialogData = dialogData,
)
}
@ -123,7 +123,7 @@ class WalletConnectSdkHelper {
val blockchain = session.wallet.getBlockchainForSession()
return factory.makeWalletManager(
blockchain = blockchain,
publicKey = publicKey
publicKey = publicKey,
)
}
@ -137,14 +137,14 @@ class WalletConnectSdkHelper {
private suspend fun sendTransaction(data: WcTransactionData): String? {
val result = (data.walletManager as TransactionSender).send(
transactionData = data.transaction,
signer = CommonSigner(tangemSdk)
signer = CommonSigner(tangemSdk),
)
return when (result) {
SimpleResult.Success -> {
HEX_PREFIX + data.walletManager.wallet.recentTransactions.last().hash
}
is SimpleResult.Failure -> {
Analytics.send(result.error, AnalyticsAnOld.ActionToLog.WalletConnectTransaction)
(result.error as? TangemSdkError)?.let { Analytics.send(WalletConnect.TransactionError(it)) }
Timber.e(result.error as BlockchainSdkError)
null
}
@ -156,13 +156,13 @@ class WalletConnectSdkHelper {
transactionData = data.transaction,
nonce = null,
blockchain = data.walletManager.wallet.blockchain,
gasLimit = null
gasLimit = null,
) ?: return null
val command = SignHashCommand(
hash = dataToSign.hash,
walletPublicKey = data.walletManager.wallet.publicKey.seedKey,
derivationPath = data.walletManager.wallet.publicKey.derivationPath
derivationPath = data.walletManager.wallet.publicKey.derivationPath,
)
val result = tangemSdkManager.runTaskAsync(command, initialMessage = Message())
return when (result) {
@ -170,9 +170,7 @@ class WalletConnectSdkHelper {
HEX_PREFIX + result.data
}
is CompletionResult.Failure -> {
(result.error as? TangemSdkError)?.let { error ->
Analytics.send(error, AnalyticsAnOld.ActionToLog.WalletConnectSign)
}
(result.error as? TangemSdkError)?.let { Analytics.send(WalletConnect.SignError(it)) }
Timber.e(result.error.customMessage)
null
}
@ -183,7 +181,7 @@ class WalletConnectSdkHelper {
val command = SignHashCommand(
hash = data,
walletPublicKey = session.wallet.walletPublicKey ?: return null,
derivationPath = session.wallet.derivationPath
derivationPath = session.wallet.derivationPath,
)
val result = tangemSdkManager.runTaskAsync(command, initialMessage = Message())
return when (result) {
@ -192,13 +190,11 @@ class WalletConnectSdkHelper {
?: session.wallet.walletPublicKey.toDecompressedPublicKey()
getBnbResultString(
key.toHexString(),
result.data.signature.toHexString()
result.data.signature.toHexString(),
)
}
is CompletionResult.Failure -> {
(result.error as? TangemSdkError)?.let { error ->
Analytics.send(error, AnalyticsAnOld.ActionToLog.WalletConnectTransaction)
}
(result.error as? TangemSdkError)?.let { Analytics.send(WalletConnect.TransactionError(it)) }
Timber.e(result.error.customMessage)
null
}
@ -223,18 +219,17 @@ class WalletConnectSdkHelper {
val prefixData = (ETH_MESSAGE_PREFIX + messageData.size.toString()).toByteArray()
val hashToSign = (prefixData + messageData).toKeccak()
val dialogData = PersonalSignDialogData(
dAppName = session.peerMeta.name,
message = messageString,
session = session.session,
id = id
id = id,
)
return WcPersonalSignData(
hash = hashToSign,
session = session,
id = id,
dialogData = dialogData
dialogData = dialogData,
)
}
@ -269,9 +264,7 @@ class WalletConnectSdkHelper {
)
}
is CompletionResult.Failure -> {
(result.error as? TangemSdkError)?.let { error ->
Analytics.send(error, AnalyticsAnOld.ActionToLog.WalletConnectSign)
}
(result.error as? TangemSdkError)?.let { Analytics.send(WalletConnect.SignError(it)) }
Timber.e(result.error.customMessage)
null
}

View file

@ -4,8 +4,6 @@ import com.tangem.common.CompletionResult
import com.tangem.common.core.TangemSdkError
import com.tangem.domain.common.TapWorkarounds.isTangemTwins
import com.tangem.tap.common.analytics.Analytics
import com.tangem.tap.common.analytics.AnalyticsAnOld
import com.tangem.tap.common.analytics.AnalyticsParamAnOld
import com.tangem.tap.common.analytics.events.AnalyticsParam
import com.tangem.tap.common.analytics.events.Settings
import com.tangem.tap.common.extensions.dispatchDialogShow
@ -129,11 +127,7 @@ class DetailsMiddleware {
}
is CompletionResult.Failure -> {
(result.error as? TangemSdkError)?.let { error ->
Analytics.send(
error,
AnalyticsAnOld.ActionToLog.PurgeWallet,
card = store.state.detailsState.scanResponse?.card,
)
Analytics.send(Settings.CardSettings.FactoryResetFinished(error))
}
}
}
@ -160,30 +154,20 @@ class DetailsMiddleware {
SecurityOption.LongTap -> tangemSdkManager.setLongTap(cardId)
SecurityOption.PassCode -> tangemSdkManager.setPasscode(cardId)
SecurityOption.AccessCode -> tangemSdkManager.setAccessCode(cardId)
else -> null
else -> return@launch
}
withContext(Dispatchers.Main) {
val paramValue = AnalyticsParam.SecurityMode.from(selectedOption)
when (result) {
is CompletionResult.Success -> {
selectedOption?.let {
val paramValue = AnalyticsParam.SecurityMode.from(it)
Analytics.send(Settings.CardSettings.SecurityModeChanged(paramValue))
store.dispatch(GlobalAction.UpdateSecurityOptions(it))
}
Analytics.send(Settings.CardSettings.SecurityModeChanged(paramValue))
store.dispatch(GlobalAction.UpdateSecurityOptions(selectedOption))
store.dispatch(NavigationAction.PopBackTo())
store.dispatch(DetailsAction.ManageSecurity.SaveChanges.Success)
}
is CompletionResult.Failure -> {
(result.error as? TangemSdkError)?.let { error ->
Analytics.send(
error = error,
action = AnalyticsAnOld.ActionToLog.ChangeSecOptions,
params = mapOf(
AnalyticsParamAnOld.NEW_SECURITY_OPTION to
(selectedOption?.name ?: ""),
),
card = store.state.detailsState.scanResponse?.card,
)
Analytics.send(Settings.CardSettings.SecurityModeChanged(paramValue, error))
}
store.dispatch(DetailsAction.ManageSecurity.SaveChanges.Failure)
}

View file

@ -10,9 +10,6 @@ import com.tangem.operations.backup.BackupService
import com.tangem.tap.DELAY_SDK_DIALOG_CLOSE
import com.tangem.tap.backupService
import com.tangem.tap.common.analytics.Analytics
import com.tangem.tap.common.analytics.AnalyticsEventAnOld
import com.tangem.tap.common.analytics.AnalyticsParamAnOld
import com.tangem.tap.common.analytics.GetCardSourceParamsAnOld
import com.tangem.tap.common.analytics.events.IntroductionProcess
import com.tangem.tap.common.analytics.paramsInterceptor.BatchIdParamsInterceptor
import com.tangem.tap.common.entities.IndeterminateProgressButton
@ -107,10 +104,6 @@ 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))
}
Analytics.handleAnalyticsEvent(
event = AnalyticsEventAnOld.GET_CARD,
params = mapOf(AnalyticsParamAnOld.SOURCE.param to GetCardSourceParamsAnOld.WELCOME.param),
)
}
}
}

View file

@ -18,9 +18,6 @@ import com.tangem.domain.common.TapWorkarounds.isStart2Coin
import com.tangem.domain.common.extensions.withMainContext
import com.tangem.tap.DELAY_SDK_DIALOG_CLOSE
import com.tangem.tap.common.analytics.Analytics
import com.tangem.tap.common.analytics.AnalyticsAnOld
import com.tangem.tap.common.analytics.AnalyticsEventAnOld
import com.tangem.tap.common.analytics.AnalyticsParamAnOld
import com.tangem.tap.common.analytics.events.AnalyticsParam
import com.tangem.tap.common.analytics.events.Token
import com.tangem.tap.common.extensions.dispatchDialogShow
@ -222,14 +219,10 @@ private fun sendTransaction(
dispatch(SendAction.ChangeSendButtonState(ButtonState.ENABLED))
tangemSdk.config.linkedTerminal = isLinkedTerminal
val currencyType = AnalyticsParam.CurrencyType.Amount(amountToSend)
when (sendResult) {
is SimpleResult.Success -> {
Analytics.send(Token.Send.TransactionSent(AnalyticsParam.CurrencyType.Amount(amountToSend)))
Analytics.handleAnalyticsEvent(
event = AnalyticsEventAnOld.TRANSACTION_IS_SENT,
card = card,
blockchain = walletManager.wallet.blockchain.currency,
)
Analytics.send(Token.Send.TransactionSent(currencyType))
dispatch(SendAction.SendSuccess)
if (externalTransactionData != null) {
@ -254,15 +247,10 @@ private fun sendTransaction(
feeAmount = feeAmount,
destinationAddress = destinationAddress,
)
Analytics.send(
error = sendResult.error,
action = AnalyticsAnOld.ActionToLog.SendTransaction,
params = mapOf(AnalyticsParamAnOld.BLOCKCHAIN to walletManager.wallet.blockchain.currency),
card = card,
)
val error = (sendResult.error as? BlockchainSdkError) ?: return@withMainContext
Analytics.send(Token.Send.TransactionSent(currencyType, error))
when (error) {
is BlockchainSdkError.WrappedTangemError -> {
val tangemSdkError = (error.tangemError as? TangemSdkError) ?: return@withMainContext

View file

@ -5,14 +5,12 @@ import com.tangem.blockchain.common.Amount
import com.tangem.blockchain.common.AmountType
import com.tangem.blockchain.common.WalletManager
import com.tangem.common.CompletionResult
import com.tangem.common.core.TangemSdkError
import com.tangem.common.extensions.isZero
import com.tangem.common.services.Result
import com.tangem.domain.common.extensions.withMainContext
import com.tangem.operations.attestation.Attestation
import com.tangem.operations.attestation.OnlineCardVerifier
import com.tangem.tap.common.analytics.Analytics
import com.tangem.tap.common.analytics.AnalyticsAnOld
import com.tangem.tap.common.analytics.events.Token
import com.tangem.tap.common.extensions.copyToClipboard
import com.tangem.tap.common.extensions.dispatchDebugErrorNotification
@ -189,15 +187,7 @@ class WalletMiddleware {
globalState.scanResponse?.copy(card = result.data)
scanNoteResponse?.let { store.onCardScanned(scanNoteResponse) }
}
is CompletionResult.Failure -> {
(result.error as? TangemSdkError)?.let { error ->
Analytics.send(
error,
AnalyticsAnOld.ActionToLog.CreateWallet,
card = store.state.detailsState.scanResponse?.card,
)
}
}
is CompletionResult.Failure -> {}
}
}
}

View file

@ -8,8 +8,6 @@ import com.tangem.common.card.Card
import com.tangem.common.card.FirmwareVersion
import com.tangem.domain.common.ScanResponse
import com.tangem.domain.common.TapWorkarounds.isTestCard
import com.tangem.tap.common.analytics.Analytics
import com.tangem.tap.common.analytics.AnalyticsEventAnOld
import com.tangem.tap.common.extensions.dispatchOnMain
import com.tangem.tap.common.extensions.isGreaterThan
import com.tangem.tap.common.redux.global.GlobalState
@ -55,14 +53,10 @@ class WarningsMiddleware {
if (action.remainingSignatures != null &&
action.remainingSignatures <= WarningMessagesManager.REMAINING_SIGNATURES_WARNING
) {
store.state.globalState.warningManager
?.removeWarnings(
messageRes = R.string.warning_low_signatures_format
)
store.state.globalState.warningManager?.removeWarnings(R.string.warning_low_signatures_format)
addWarningMessage(
warning =
WarningMessagesManager.remainingSignaturesNotEnough(action.remainingSignatures),
autoUpdate = true
warning = WarningMessagesManager.remainingSignaturesNotEnough(action.remainingSignatures),
autoUpdate = true,
)
}
}
@ -70,7 +64,8 @@ class WarningsMiddleware {
is WalletAction.Warnings.CheckHashesCount,
is WalletAction.Warnings.CheckHashesCount.ConfirmHashesCount,
is WalletAction.Warnings.CheckHashesCount.NeedToCheckHashesCountOnline,
is WalletAction.Warnings.Set -> Unit
is WalletAction.Warnings.Set,
-> Unit
}
}
@ -82,7 +77,6 @@ class WarningsMiddleware {
preferencesStorage.appRatingLaunchObserver.foundWalletWithFunds()
}
if (preferencesStorage.appRatingLaunchObserver.isReadyToShow()) {
Analytics.handleAnalyticsEvent(AnalyticsEventAnOld.APP_RATING_DISPLAYED)
addWarningMessage(WarningMessagesManager.appRatingWarning(), true)
}
}
@ -119,11 +113,7 @@ class WarningsMiddleware {
if (remainingSignatures != null &&
remainingSignatures <= WarningMessagesManager.REMAINING_SIGNATURES_WARNING
) {
addWarningMessage(
WarningMessagesManager.remainingSignaturesNotEnough(
remainingSignatures
)
)
addWarningMessage(WarningMessagesManager.remainingSignaturesNotEnough(remainingSignatures))
}
}
@ -179,15 +169,9 @@ class WarningsMiddleware {
}
is SimpleResult.Failure ->
if (result.error is BlockchainSdkError.SignatureCountNotMatched) {
addWarningMessage(
WarningMessagesManager.alreadySignedHashesWarning(),
true
)
addWarningMessage(WarningMessagesManager.alreadySignedHashesWarning(), true)
} else if (signedHashes > 0) {
addWarningMessage(
WarningMessagesManager.alreadySignedHashesWarning(),
true
)
addWarningMessage(WarningMessagesManager.alreadySignedHashesWarning(), true)
}
null -> Unit
}
@ -208,7 +192,7 @@ class WarningsMiddleware {
val warningManager = store.state.globalState.warningManager ?: return emptyList()
return warningManager.getWarnings(
WarningMessage.Location.MainScreen,
store.state.walletState.blockchains
store.state.walletState.blockchains,
)
}
}

View file

@ -8,7 +8,6 @@ import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import com.google.android.play.core.review.ReviewManagerFactory
import com.tangem.tap.common.analytics.Analytics
import com.tangem.tap.common.analytics.AnalyticsEventAnOld
import com.tangem.tap.common.analytics.events.AnalyticsParam
import com.tangem.tap.common.analytics.events.MainScreen
import com.tangem.tap.common.extensions.getActivity
@ -29,7 +28,7 @@ class WarningMessagesAdapter : ListAdapter<WarningMessage, WarningMessageVH>(Dif
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): WarningMessageVH {
val binding = LayoutWarningCardActionBinding.inflate(
LayoutInflater.from(parent.context), parent, false
LayoutInflater.from(parent.context), parent, false,
)
return WarningMessageVH(binding)
}
@ -63,7 +62,7 @@ class WarningMessageVH(val binding: LayoutWarningCardActionBinding) : RecyclerVi
tvMessage.text = getString(
resId = warning.messageResId,
default = warning.message,
formatArgs = warning.messageFormatArg
formatArgs = warning.messageFormatArg,
)
}
@ -101,7 +100,7 @@ class WarningMessageVH(val binding: LayoutWarningCardActionBinding) : RecyclerVi
}
}
val buttonTitle = binding.root.getString(
warning.buttonTextId ?: R.string.how_to_got_it_button
warning.buttonTextId ?: R.string.how_to_got_it_button,
)
binding.btnGotIt.setOnClickListener(buttonAction)
binding.btnGotIt.text = buttonTitle
@ -112,13 +111,11 @@ class WarningMessageVH(val binding: LayoutWarningCardActionBinding) : RecyclerVi
binding.btnClose.show()
binding.btnClose.setOnClickListener {
Analytics.send(MainScreen.NoticeRateAppButton(AnalyticsParam.RateApp.Closed))
Analytics.handleAnalyticsEvent(AnalyticsEventAnOld.APP_RATING_DISMISS)
store.dispatch(GlobalAction.HideWarningMessage(warning))
store.dispatch(WalletAction.Warnings.AppRating.RemindLater)
}
binding.btnCanBeBetter.setOnClickListener {
Analytics.send(MainScreen.NoticeRateAppButton(AnalyticsParam.RateApp.Disliked))
Analytics.handleAnalyticsEvent(AnalyticsEventAnOld.APP_RATING_NEGATIVE)
store.dispatch(WalletAction.Warnings.AppRating.SetNeverToShow)
store.dispatch(GlobalAction.HideWarningMessage(warning))
store.dispatch(GlobalAction.SendEmail(RateCanBeBetterEmail()))
@ -127,7 +124,6 @@ class WarningMessageVH(val binding: LayoutWarningCardActionBinding) : RecyclerVi
val activity = binding.root.context.getActivity() ?: return@setOnClickListener
Analytics.send(MainScreen.NoticeRateAppButton(AnalyticsParam.RateApp.Liked))
Analytics.handleAnalyticsEvent(AnalyticsEventAnOld.APP_RATING_POSITIVE)
store.dispatch(WalletAction.Warnings.AppRating.SetNeverToShow)
val reviewManager = ReviewManagerFactory.create(activity)
val task = reviewManager.requestReviewFlow()