Updated on 2026-08-14

This commit is contained in:
Tangem 2025-01-24 18:12:45 +03:00
commit 59da34299c
26 changed files with 123 additions and 4 deletions

View file

@ -8,6 +8,7 @@ plugins {
alias(deps.plugins.google.services)
alias(deps.plugins.hilt.android)
alias(deps.plugins.firebase.crashlytics)
alias(deps.plugins.firebase.perf)
id("configuration")
}
@ -212,7 +213,10 @@ dependencies {
implementation(deps.firebase.analytics)
implementation(deps.firebase.crashlytics)
implementation(deps.firebase.messaging)
implementation(deps.firebase.perf) {
exclude(group = "com.google.firebase", module = "protolite-well-known-types")
exclude(group = "com.google.protobuf", module = "protobuf-javalite")
}
/** Tangem libraries */
implementation(deps.tangem.blockchain) {
exclude(module = "joda-time")

View file

@ -10,6 +10,7 @@ plugins {
alias(deps.plugins.hilt.android) apply false
alias(deps.plugins.google.services) apply false
alias(deps.plugins.firebase.crashlytics) apply false
alias(deps.plugins.firebase.perf) apply false
alias(deps.plugins.room) apply false
}

View file

@ -40,6 +40,10 @@ dependencies {
implementation(deps.tangem.card.core)
implementation(deps.tangem.blockchain)
implementation(deps.timber)
implementation(deps.firebase.perf) {
exclude(group = "com.google.firebase", module = "protolite-well-known-types")
exclude(group = "com.google.protobuf", module = "protobuf-javalite")
}
/** DI */
implementation(deps.hilt.android)

View file

@ -1,6 +1,8 @@
package com.tangem.feature.wallet.presentation.wallet.analytics.utils
import arrow.core.getOrElse
import com.google.firebase.perf.FirebasePerformance
import com.google.firebase.perf.metrics.Trace
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchainsdk.utils.fromNetworkId
import com.tangem.common.extensions.isZero
@ -12,6 +14,7 @@ import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.domain.tokens.model.TokenList
import com.tangem.domain.tokens.model.TotalFiatBalance
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.feature.wallet.presentation.wallet.analytics.WalletScreenAnalyticsEvent.Basic
import com.tangem.feature.wallet.presentation.wallet.analytics.WalletScreenAnalyticsEvent.MainScreen
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletState
@ -31,11 +34,20 @@ internal class TokenListAnalyticsSender @Inject constructor(
private val balanceWasSentMap = mutableMapOf<String, Boolean>()
private val mutex = Mutex()
private val loadingTraces = mutableMapOf<UserWalletId, Trace>()
suspend fun send(displayedUiState: WalletState?, userWallet: UserWallet, tokenList: TokenList) {
if (screenLifecycleProvider.isBackgroundState.value) return
if (displayedUiState == null || displayedUiState.pullToRefreshConfig.isRefreshing) return
if (tokenList.totalFiatBalance is TotalFiatBalance.Loading) return
if (tokenList.totalFiatBalance is TotalFiatBalance.Loading) {
startLoadingTraceIfNeeded(userWallet.walletId, tokenList)
return
}
if (isTerminalState(tokenList.totalFiatBalance)) {
stopLoadingTraceIfNeeded(userWallet.walletId, tokenList.totalFiatBalance)
}
val currenciesStatuses = tokenList.flattenCurrencies()
@ -45,6 +57,35 @@ internal class TokenListAnalyticsSender @Inject constructor(
sendTokenBalancesIfNeeded(currenciesStatuses)
}
private suspend fun startLoadingTraceIfNeeded(userWalletId: UserWalletId, tokenList: TokenList) {
mutex.withLock {
if (!loadingTraces.containsKey(userWalletId)) {
val trace = FirebasePerformance.getInstance().newTrace(BALANCE_LOADED_TRACE_NAME)
trace.start()
trace.putAttribute(TOKENS_COUNT, tokenList.flattenCurrencies().size.toString())
loadingTraces[userWalletId] = trace
}
}
}
private suspend fun stopLoadingTraceIfNeeded(userWalletId: UserWalletId, totalFiatBalance: TotalFiatBalance) {
mutex.withLock {
loadingTraces[userWalletId]?.apply {
when (totalFiatBalance) {
is TotalFiatBalance.Loaded -> putAttribute(HAS_ERROR, "No")
is TotalFiatBalance.Failed -> putAttribute(HAS_ERROR, "Yes")
else -> { /* Intentionally do nothing */ }
}
stop()
loadingTraces.remove(userWalletId)
}
}
}
private fun isTerminalState(balance: TotalFiatBalance): Boolean {
return balance is TotalFiatBalance.Failed || balance is TotalFiatBalance.Loaded
}
private fun sendBalanceLoadedEventIfNeeded(
fiatBalance: TotalFiatBalance,
currenciesStatuses: List<CryptoCurrencyStatus>,
@ -179,4 +220,10 @@ internal class TokenListAnalyticsSender @Inject constructor(
analyticsEventHandler.send(MainScreen.NetworksUnreachable(unreachableCurrencies))
}
}
companion object {
const val BALANCE_LOADED_TRACE_NAME = "Total_balance_loaded"
const val HAS_ERROR = "has_error"
const val TOKENS_COUNT = "tokens_count"
}
}

View file

@ -55,6 +55,18 @@ internal enum class Wallet2CobrandImage(
batchIds = setOf("AF97"),
),
CashClubGold(
cards2ResId = R.drawable.ill_cashclubgold_card2_120_106,
cards3ResId = R.drawable.ill_cashclubgold_card3_120_106,
batchIds = setOf("BB000004"),
),
Changenow(
cards2ResId = R.drawable.ill_changenow_card2_120_106,
cards3ResId = R.drawable.ill_changenow_card3_120_106,
batchIds = setOf("BB000013"),
),
CoinMetrica(
cards2ResId = R.drawable.ill_coin_metrica_card2_120_106,
cards3ResId = R.drawable.ill_coin_metrica_card3_120_106,
@ -97,6 +109,12 @@ internal enum class Wallet2CobrandImage(
batchIds = setOf("AF13"),
),
Hodl(
cards2ResId = R.drawable.ill_hodl_card2_120_106,
cards3ResId = R.drawable.ill_hodl_card3_120_106,
batchIds = setOf("BB000009"),
),
Jr(
cards2ResId = R.drawable.ill_jr_card2_120_106,
cards3ResId = R.drawable.ill_jr_card3_120_106,
@ -145,12 +163,24 @@ internal enum class Wallet2CobrandImage(
batchIds = setOf("AF52"),
),
Kango(
cards2ResId = R.drawable.ill_kango_card2_120_106,
cards3ResId = R.drawable.ill_kango_card3_120_106,
batchIds = setOf("BB000006"),
),
Konan(
cards2ResId = R.drawable.ill_konan_card2_120_106,
cards3ResId = R.drawable.ill_konan_card3_120_106,
batchIds = setOf("AF93"),
),
Kroak(
cards2ResId = R.drawable.ill_kroak_card2_120_106,
cards3ResId = R.drawable.ill_kroak_card3_120_106,
batchIds = setOf("BB000011"),
),
Neiro(
cards2ResId = R.drawable.ill_neiro_card2_120_106,
cards3ResId = R.drawable.ill_neiro_card3_120_106,
@ -163,6 +193,12 @@ internal enum class Wallet2CobrandImage(
batchIds = setOf("AF26"),
),
PassimPay(
cards2ResId = R.drawable.ill_passimpay_cards2_120_106,
cards3ResId = R.drawable.ill_passimpay_cards3_120_106,
batchIds = setOf("BB000007"),
),
// for multicolored cards use image of 3 cards in all cases
Pastel(
cards2ResId = R.drawable.ill_pastel_cards3_120_106,
@ -176,12 +212,24 @@ internal enum class Wallet2CobrandImage(
batchIds = setOf("AF34"),
),
Rizo(
cards2ResId = R.drawable.ill_rizo_card2_120_106,
cards3ResId = R.drawable.ill_rizo_card3_120_106,
batchIds = setOf("BB000012"),
),
SatoshiFriends(
cards2ResId = R.drawable.ill_satoshi_card2_120_106,
cards3ResId = R.drawable.ill_satoshi_card3_120_106,
batchIds = setOf("AF19"),
),
SinCity(
cards2ResId = R.drawable.ill_sincity_card2_120_106,
cards3ResId = R.drawable.ill_sincity_card3_120_106,
batchIds = setOf("BB000010"),
),
StealthCard(
cards2ResId = R.drawable.ill_stealth_cards2_120_106,
cards3ResId = R.drawable.ill_stealth_cards3_120_106,
@ -219,6 +267,12 @@ internal enum class Wallet2CobrandImage(
batchIds = setOf("AF40", "AF41", "AF42", "AF75", "AF76", "AF77"),
),
Vnish(
cards2ResId = R.drawable.ill_vnish_card2_120_106,
cards3ResId = R.drawable.ill_vnish_card3_120_106,
batchIds = setOf("BB000005"),
),
VoltInu(
cards2ResId = R.drawable.ill_volt_inu_card2_120_106,
cards3ResId = R.drawable.ill_volt_inu_card3_120_106,
@ -231,6 +285,12 @@ internal enum class Wallet2CobrandImage(
batchIds = setOf("AF15"),
),
WildGoat(
cards2ResId = R.drawable.ill_wildgoat_card2_120_106,
cards3ResId = R.drawable.ill_wildgoat_card3_120_106,
batchIds = setOf("BB000001"),
),
Winter(
cards2ResId = R.drawable.ill_winter_card2_120_106,
cards3ResId = R.drawable.ill_winter_card3_120_106,

Binary file not shown.

After

Width:  |  Height:  |  Size: 9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

View file

@ -7,6 +7,7 @@ androidGradlePlugin = "8.2.2"
firebaseCrashlytics = "3.0.1"
googleServices = "4.4.1"
kotlin = "1.9.22"
firebasePerf = "1.4.2"
# endregion Classpath
# region AndroidX
@ -45,7 +46,7 @@ coil = "2.1.0"
compose-shimmer = "1.0.3"
coroutine = "1.7.2"
desugarJdkLibs = "1.1.5"
firebase = "33.1.0"
firebase = "33.7.0"
googleMaterialComponent = "1.6.1"
googlePlayReview = "2.0.1"
googlePlayReviewKtx = "2.0.1"
@ -88,7 +89,7 @@ markdownComposeView = "0.5.4"
# endregion Other libraries
# region Tangem
tangemBlockchainSdk = "release-app_5.20-915"
tangemBlockchainSdk = "release-app_5.20-916"
#tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds
tangemCardSdk = "release-app_5.20-423"
#tangemCardSdk = "0.0.1" # Keep it! - used for local builds ^
@ -122,6 +123,7 @@ kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", versi
android-application = { id = "com.android.application", version.ref = "androidGradlePlugin" }
android-library = { id = "com.android.library", version.ref = "androidGradlePlugin" }
firebase-crashlytics = { id = "com.google.firebase.crashlytics", version.ref = "firebaseCrashlytics" }
firebase-perf = { id = "com.google.firebase.firebase-perf", version.ref = "firebasePerf" }
google-services = { id = "com.google.gms.google-services", version.ref = "googleServices" }
hilt-android = { id = "com.google.dagger.hilt.android", version.ref = "hilt" }
detekt = { id = "io.gitlab.arturbosch.detekt", version.ref = "detekt" }
@ -182,6 +184,7 @@ firebase-bom = { module = "com.google.firebase:firebase-bom", version.ref = "fir
firebase-analytics = { module = "com.google.firebase:firebase-analytics-ktx" }
firebase-crashlytics = { module = "com.google.firebase:firebase-crashlytics-ktx" }
firebase-messaging = { module = "com.google.firebase:firebase-messaging-ktx" }
firebase-perf = { module = "com.google.firebase:firebase-perf" }
# endregion Firebase
# region Tangem