Updated on 2026-08-14

This commit is contained in:
Tangem 2023-01-10 15:12:44 +03:00
commit b7ce5f50d0
128 changed files with 806 additions and 378 deletions

View file

@ -109,6 +109,7 @@ dependencies {
implementation(fileTree(mapOf("dir" to "libs", "include" to listOf("*.aar"))))
implementation(project(":domain"))
implementation(project(":common"))
implementation(project(":core:analytics"))
implementation(project(":core:res"))
implementation(project(":core:ui"))
implementation(project(":core:datasource"))

@ -1 +1 @@
Subproject commit 897f0f7c402fd53c5ee1fa4ab601ecd4e43ff389
Subproject commit 8453faa078220801858166081947972639f166db

View file

@ -10,7 +10,7 @@ import androidx.core.view.WindowInsetsControllerCompat
import by.kirich1409.viewbindingdelegate.viewBinding
import com.google.android.material.snackbar.Snackbar
import com.tangem.TangemSdk
import com.tangem.domain.common.FeatureCoroutineExceptionHandler
import com.tangem.utils.coroutines.FeatureCoroutineExceptionHandler
import com.tangem.operations.backup.BackupService
import com.tangem.tangem_sdk_new.extensions.init
import com.tangem.tap.common.ActivityResultCallbackHolder

View file

@ -17,7 +17,7 @@ import com.tangem.domain.common.LogConfig
import com.tangem.tap.common.AndroidAssetReader
import com.tangem.tap.common.AssetReader
import com.tangem.tap.common.IntentHandler
import com.tangem.tap.common.analytics.Analytics
import com.tangem.core.analytics.Analytics
import com.tangem.tap.common.analytics.AnalyticsFactory
import com.tangem.tap.common.analytics.api.AnalyticsHandlerBuilder
import com.tangem.tap.common.analytics.filters.BasicSignInFilter

View file

@ -1,8 +1,8 @@
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 com.tangem.analytics.api.ErrorEventLogger
import com.tangem.analytics.api.EventLogger
import timber.log.Timber
class AnalyticsEventsLogger(

View file

@ -1,8 +1,9 @@
package com.tangem.tap.common.analytics
import com.tangem.tap.common.analytics.api.AnalyticsEventFilter
import com.tangem.core.analytics.Analytics
import com.tangem.core.analytics.api.AnalyticsEventFilter
import com.tangem.core.analytics.api.ParamsInterceptor
import com.tangem.tap.common.analytics.api.AnalyticsHandlerBuilder
import com.tangem.tap.common.analytics.api.ParamsInterceptor
/**
[REDACTED_AUTHOR]

View file

@ -0,0 +1,19 @@
package com.tangem.tap.common.analytics.api
import android.app.Application
import com.tangem.common.json.MoshiJsonConverter
import com.tangem.core.analytics.api.AnalyticsHandler
import com.tangem.domain.common.AnalyticsHandlersLogConfig
import com.tangem.tap.domain.configurable.config.Config
interface AnalyticsHandlerBuilder {
fun build(data: Data): AnalyticsHandler?
data class Data(
val application: Application,
val config: Config,
val isDebug: Boolean,
val logConfig: AnalyticsHandlersLogConfig,
val jsonConverter: MoshiJsonConverter,
)
}

View file

@ -1,5 +1,7 @@
package com.tangem.tap.common.analytics.events
import com.tangem.core.analytics.AnalyticsEvent
/**
[REDACTED_AUTHOR]
*/

View file

@ -1,5 +1,7 @@
package com.tangem.tap.common.analytics.events
import com.tangem.core.analytics.AnalyticsEvent
/**
[REDACTED_AUTHOR]
*/

View file

@ -1,5 +1,7 @@
package com.tangem.tap.common.analytics.events
import com.tangem.core.analytics.AnalyticsEvent
/**
[REDACTED_AUTHOR]
*/

View file

@ -1,5 +1,7 @@
package com.tangem.tap.common.analytics.events
import com.tangem.core.analytics.AnalyticsEvent
/**
[REDACTED_AUTHOR]
*/

View file

@ -1,5 +1,7 @@
package com.tangem.tap.common.analytics.events
import com.tangem.core.analytics.AnalyticsEvent
/**
[REDACTED_AUTHOR]
*/

View file

@ -1,5 +1,6 @@
package com.tangem.tap.common.analytics.events
import com.tangem.core.analytics.AnalyticsEvent
import com.tangem.domain.common.extensions.toNetworkId
import com.tangem.domain.features.addCustomToken.CustomCurrency
import com.tangem.tap.common.extensions.filterNotNull

View file

@ -1,5 +1,7 @@
package com.tangem.tap.common.analytics.events
import com.tangem.core.analytics.AnalyticsEvent
sealed class MyWallets(
event: String,
params: Map<String, String> = mapOf(),

View file

@ -1,5 +1,7 @@
package com.tangem.tap.common.analytics.events
import com.tangem.core.analytics.AnalyticsEvent
/**
[REDACTED_AUTHOR]
*/

View file

@ -1,5 +1,7 @@
package com.tangem.tap.common.analytics.events
import com.tangem.core.analytics.AnalyticsEvent
/**
[REDACTED_AUTHOR]
*/

View file

@ -1,5 +1,6 @@
package com.tangem.tap.common.analytics.events
import com.tangem.core.analytics.AnalyticsEvent
import com.tangem.tap.features.details.ui.details.SocialNetwork
/**

View file

@ -1,5 +1,6 @@
package com.tangem.tap.common.analytics.events
import com.tangem.core.analytics.AnalyticsEvent
import com.tangem.tap.common.extensions.filterNotNull
/**

View file

@ -1,5 +1,6 @@
package com.tangem.tap.common.analytics.events
import com.tangem.core.analytics.AnalyticsEvent
import com.tangem.tap.common.analytics.events.AnalyticsParam.CurrencyType
/**

View file

@ -1,5 +1,7 @@
package com.tangem.tap.common.analytics.events
import com.tangem.core.analytics.AnalyticsEvent
/**
[REDACTED_AUTHOR]
*/

View file

@ -1,8 +1,8 @@
package com.tangem.tap.common.analytics.filters
import com.tangem.tap.common.analytics.api.AnalyticsEventFilter
import com.tangem.tap.common.analytics.api.AnalyticsHandler
import com.tangem.tap.common.analytics.events.AnalyticsEvent
import com.tangem.core.analytics.api.AnalyticsEventFilter
import com.tangem.core.analytics.api.AnalyticsHandler
import com.tangem.core.analytics.AnalyticsEvent
import com.tangem.tap.common.analytics.events.Basic
/**

View file

@ -1,9 +1,9 @@
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.AnalyticsHandler
import com.tangem.tap.common.analytics.events.AnalyticsEvent
import com.tangem.core.analytics.api.AnalyticsEventFilter
import com.tangem.core.analytics.api.AnalyticsHandler
import com.tangem.core.analytics.AnalyticsEvent
import com.tangem.tap.common.analytics.events.AnalyticsParam
import com.tangem.tap.common.analytics.events.Basic
import com.tangem.tap.persistence.ToppedUpWalletStorage

View file

@ -1,8 +1,8 @@
package com.tangem.tap.common.analytics.filters
import com.tangem.tap.common.analytics.api.AnalyticsEventFilter
import com.tangem.tap.common.analytics.api.AnalyticsHandler
import com.tangem.tap.common.analytics.events.AnalyticsEvent
import com.tangem.core.analytics.api.AnalyticsEventFilter
import com.tangem.core.analytics.api.AnalyticsHandler
import com.tangem.core.analytics.AnalyticsEvent
import com.tangem.tap.common.analytics.events.Shop
import com.tangem.tap.common.analytics.handlers.appsFlyer.AppsFlyerAnalyticsHandler
import com.tangem.tap.common.analytics.handlers.firebase.FirebaseAnalyticsHandler

View file

@ -1,6 +1,6 @@
package com.tangem.tap.common.analytics.handlers.amplitude
import com.tangem.tap.common.analytics.api.AnalyticsHandler
import com.tangem.core.analytics.api.AnalyticsHandler
import com.tangem.tap.common.analytics.api.AnalyticsHandlerBuilder
class AmplitudeAnalyticsHandler(

View file

@ -4,7 +4,7 @@ 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 com.tangem.analytics.api.EventLogger
import org.json.JSONObject
/**

View file

@ -1,6 +1,6 @@
package com.tangem.tap.common.analytics.handlers.appsFlyer
import com.tangem.tap.common.analytics.api.AnalyticsHandler
import com.tangem.core.analytics.api.AnalyticsHandler
import com.tangem.tap.common.analytics.api.AnalyticsHandlerBuilder
class AppsFlyerAnalyticsHandler(

View file

@ -2,7 +2,7 @@ package com.tangem.tap.common.analytics.handlers.appsFlyer
import android.content.Context
import com.appsflyer.AppsFlyerLib
import com.tangem.tap.common.analytics.api.EventLogger
import com.tangem.analytics.api.EventLogger
/**
[REDACTED_AUTHOR]

View file

@ -1,10 +1,10 @@
package com.tangem.tap.common.analytics.handlers.firebase
import com.tangem.tap.common.analytics.api.AnalyticsHandler
import com.tangem.core.analytics.AnalyticsEvent
import com.tangem.core.analytics.api.AnalyticsHandler
import com.tangem.core.analytics.api.ErrorEventHandler
import com.tangem.tap.common.analytics.api.AnalyticsHandlerBuilder
import com.tangem.tap.common.analytics.api.ErrorEventHandler
import com.tangem.tap.common.analytics.converters.AnalyticsErrorConverter
import com.tangem.tap.common.analytics.events.AnalyticsEvent
class FirebaseAnalyticsHandler(
private val client: FirebaseAnalyticsClient,
@ -17,18 +17,18 @@ class FirebaseAnalyticsHandler(
}
override fun send(event: AnalyticsEvent) {
when (event.error) {
null -> super.send(event)
else -> {
val errorConverter = AnalyticsErrorConverter()
if (!errorConverter.canBeHandled(event.error)) return
val error = event.error
if (error == null) {
super.send(event)
} else {
val errorConverter = AnalyticsErrorConverter()
if (!errorConverter.canBeHandled(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)
}
val errorParams = errorConverter.convert(error).toMutableMap()
errorParams["Category"] = event.category
errorParams["Event"] = event.event
errorParams.putAll(event.params)
send(error, errorParams)
}
}

View file

@ -6,8 +6,8 @@ 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
import com.tangem.analytics.api.ErrorEventLogger
import com.tangem.analytics.api.EventLogger
/**
[REDACTED_AUTHOR]

View file

@ -1,7 +1,7 @@
package com.tangem.tap.common.analytics.paramsInterceptor
import com.tangem.tap.common.analytics.api.ParamsInterceptor
import com.tangem.tap.common.analytics.events.AnalyticsEvent
import com.tangem.core.analytics.api.ParamsInterceptor
import com.tangem.core.analytics.AnalyticsEvent
import com.tangem.tap.common.analytics.events.AnalyticsParam
/**

View file

@ -4,7 +4,7 @@ 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.Analytics
import com.tangem.core.analytics.Analytics
import com.tangem.tap.common.analytics.converters.ShopOrderToEventConverter
import com.tangem.tap.common.extensions.filterNotNull
import com.tangem.tap.common.shop.data.ProductType

View file

@ -30,7 +30,7 @@ import com.tangem.operations.derivation.DeriveMultipleWalletPublicKeysTask
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.core.analytics.Analytics
import com.tangem.tap.common.analytics.events.Basic
import com.tangem.tap.domain.tasks.CreateWalletAndRescanTask
import com.tangem.tap.domain.tasks.product.CreateProductWalletTask

View file

@ -1,6 +1,8 @@
package com.tangem.tap.domain.configurable.config
import com.tangem.blockchain.common.BlockchainSdkConfig
import com.tangem.blockchain.common.BlockchairCredentials
import com.tangem.blockchain.common.QuickNodeCredentials
import com.tangem.tap.common.shop.shopify.ShopifyShop
import com.tangem.tap.common.zendesk.ZendeskConfig
import com.tangem.tap.domain.configurable.Loader
@ -66,12 +68,12 @@ class ConfigManager {
config = config.copy(
isTopUpEnabled = model.isTopUpEnabled,
isSendingToPayIdEnabled = model.isSendingToPayIdEnabled,
isCreatingTwinCardsAllowed = model.isCreatingTwinCardsAllowed
isCreatingTwinCardsAllowed = model.isCreatingTwinCardsAllowed,
)
defaultConfig = defaultConfig.copy(
isTopUpEnabled = model.isTopUpEnabled,
isSendingToPayIdEnabled = model.isSendingToPayIdEnabled,
isCreatingTwinCardsAllowed = model.isCreatingTwinCardsAllowed
isCreatingTwinCardsAllowed = model.isCreatingTwinCardsAllowed,
)
}
@ -84,11 +86,21 @@ class ConfigManager {
mercuryoWidgetId = values.mercuryoWidgetId,
mercuryoSecret = values.mercuryoSecret,
blockchainSdkConfig = BlockchainSdkConfig(
blockchairApiKey = values.blockchairApiKey,
blockchairAuthorizationToken = values.blockchairAuthorizationToken,
blockchairCredentials = BlockchairCredentials(
apiKey = values.blockchairApiKeys,
authToken = values.blockchairAuthorizationToken,
),
blockcypherTokens = values.blockcypherTokens,
quickNodeCredentials = QuickNodeCredentials(
apiKey = values.quiknodeApiKey,
subdomain = values.quiknodeSubdomain,
),
bscQuickNodeCredentials = QuickNodeCredentials(
apiKey = values.bscQuiknodeApiKey,
subdomain = values.bscQuiknodeSubdomain,
),
infuraProjectId = values.infuraProjectId,
tronGridApiKey = values.tronGridApiKey
tronGridApiKey = values.tronGridApiKey,
),
appsFlyerDevKey = values.appsFlyerDevKey,
amplitudeApiKey = values.amplitudeApiKey,
@ -103,9 +115,19 @@ class ConfigManager {
mercuryoWidgetId = values.mercuryoWidgetId,
mercuryoSecret = values.mercuryoSecret,
blockchainSdkConfig = BlockchainSdkConfig(
blockchairApiKey = values.blockchairApiKey,
blockchairAuthorizationToken = values.blockchairAuthorizationToken,
blockchairCredentials = BlockchairCredentials(
apiKey = values.blockchairApiKeys,
authToken = values.blockchairAuthorizationToken,
),
blockcypherTokens = values.blockcypherTokens,
quickNodeCredentials = QuickNodeCredentials(
apiKey = values.quiknodeApiKey,
subdomain = values.quiknodeSubdomain,
),
bscQuickNodeCredentials = QuickNodeCredentials(
apiKey = values.bscQuiknodeApiKey,
subdomain = values.bscQuiknodeSubdomain,
),
infuraProjectId = values.infuraProjectId,
),
appsFlyerDevKey = values.appsFlyerDevKey,

View file

@ -21,8 +21,12 @@ class ConfigValueModel(
val mercuryoSecret: String,
val moonPayApiKey: String,
val moonPayApiSecretKey: String,
val blockchairApiKey: String?,
val blockchairApiKeys: List<String>,
val blockchairAuthorizationToken: String?,
val quiknodeSubdomain: String,
val quiknodeApiKey: String,
val bscQuiknodeSubdomain: String,
val bscQuiknodeApiKey: String,
val blockcypherTokens: Set<String>?,
val infuraProjectId: String?,
val appsFlyerDevKey: String,

View file

@ -143,7 +143,7 @@ fun WalletManagerFactory.makeSaltPayWalletManager(
scanResponse: ScanResponse,
): EthereumWalletManager {
val blockchain = scanResponse.getBlockchain()
if (blockchain != Blockchain.SaltPay && blockchain != Blockchain.SaltPayTestnet)
if (blockchain != Blockchain.SaltPay)
throw IllegalArgumentException()
val token = SaltPayWorkaround.tokenFrom(blockchain)

View file

@ -29,9 +29,9 @@ class UserWalletBuilder(
ProductType.Note -> "Note"
ProductType.Twins -> "Twin"
ProductType.SaltPay -> "SaltPay"
ProductType.Start2Coin -> "Start2Coin"
ProductType.Wallet -> when {
card.isBackupNotAllowed -> "Tangem card"
card.isStart2Coin -> "Start2Coin"
else -> "Wallet"
}
}
@ -41,6 +41,7 @@ class UserWalletBuilder(
ProductType.Note -> false
ProductType.Twins -> false
ProductType.SaltPay -> false
ProductType.Start2Coin -> false
ProductType.Wallet -> when {
card.isStart2Coin -> false
card.firmwareVersion >= FirmwareVersion.MultiWalletAvailable -> true

View file

@ -57,6 +57,7 @@ class UserWalletIdBuilder private constructor(
ProductType.Note,
ProductType.Wallet,
ProductType.SaltPay,
ProductType.Start2Coin,
-> null
},
)

View file

@ -11,7 +11,7 @@ import com.tangem.domain.common.extensions.withMainContext
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.core.analytics.Analytics
import com.tangem.tap.common.analytics.events.IntroductionProcess
import com.tangem.tap.common.analytics.paramsInterceptor.BatchIdParamsInterceptor
import com.tangem.tap.common.extensions.dispatchDialogShow
@ -21,9 +21,10 @@ import com.tangem.tap.common.redux.AppDialog
import com.tangem.tap.common.redux.global.GlobalAction
import com.tangem.tap.common.redux.navigation.AppScreen
import com.tangem.tap.common.redux.navigation.NavigationAction
import com.tangem.tap.features.disclaimer.DisclaimerType
import com.tangem.tap.features.disclaimer.createDisclaimer
import com.tangem.tap.features.disclaimer.redux.DisclaimerAction
import com.tangem.tap.features.disclaimer.redux.DisclaimerType
import com.tangem.tap.features.disclaimer.redux.isAccepted
import com.tangem.tap.features.disclaimer.redux.DisclaimerCallback
import com.tangem.tap.features.onboarding.OnboardingHelper
import com.tangem.tap.features.onboarding.OnboardingSaltPayHelper
import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsAction
@ -48,7 +49,8 @@ object ScanCardProcessor {
cardId: String? = null,
onProgressStateChange: suspend (showProgress: Boolean) -> Unit = {},
onScanStateChange: suspend (scanInProgress: Boolean) -> Unit = {},
onWalletNotCreated: suspend (() -> Unit) = {},
onWalletNotCreated: suspend () -> Unit = {},
disclaimerWillShow: () -> Unit = {},
onFailure: suspend (error: TangemError) -> Unit = {},
onSuccess: suspend (scanResponse: ScanResponse) -> Unit = {},
) = withMainContext {
@ -83,6 +85,8 @@ object ScanCardProcessor {
showDisclaimerIfNeed(
scanResponse = scanResponse1,
onProgressStateChange = onProgressStateChange,
disclaimerWillShow = disclaimerWillShow,
onFailure = onFailure,
nextHandler = { scanResponse2 ->
onScanSuccess(
scanResponse = scanResponse2,
@ -128,28 +132,35 @@ object ScanCardProcessor {
private suspend inline fun showDisclaimerIfNeed(
scanResponse: ScanResponse,
crossinline disclaimerWillShow: () -> Unit = {},
crossinline onProgressStateChange: suspend (showProgress: Boolean) -> Unit,
crossinline nextHandler: suspend (ScanResponse) -> Unit,
crossinline onFailure: suspend (error: TangemError) -> Unit,
) {
val disclaimerType = DisclaimerType.get(scanResponse)
store.dispatchOnMain(DisclaimerAction.SetDisclaimerType(disclaimerType))
val disclaimer = DisclaimerType.get(scanResponse.card).createDisclaimer(scanResponse.card)
store.dispatchOnMain(DisclaimerAction.SetDisclaimer(disclaimer))
if (disclaimerType.isAccepted()) {
if (disclaimer.isAccepted()) {
nextHandler((scanResponse))
} else scope.launch(Dispatchers.Main) {
} else scope.launch {
delay(DELAY_SDK_DIALOG_CLOSE)
store.dispatchOnMain(
disclaimerWillShow()
dispatchOnMain(
DisclaimerAction.Show(
onAcceptCallback = {
scope.launch(Dispatchers.Main) {
nextHandler(scanResponse)
}
},
onDismissCallback = {
scope.launch(Dispatchers.Main) {
onProgressStateChange(false)
}
},
fromScreen = AppScreen.Home,
callback = DisclaimerCallback(
onAccept = {
scope.launch(Dispatchers.Main) {
nextHandler(scanResponse)
}
},
onDismiss = {
scope.launch(Dispatchers.Main) {
onProgressStateChange(false)
onFailure(TangemSdkError.UserCancelled())
}
},
),
),
)
}

View file

@ -19,6 +19,7 @@ import com.tangem.domain.common.ScanResponse
import com.tangem.domain.common.TapWorkarounds.isExcluded
import com.tangem.domain.common.TapWorkarounds.isNotSupportedInThatRelease
import com.tangem.domain.common.TapWorkarounds.isSaltPay
import com.tangem.domain.common.TapWorkarounds.isStart2Coin
import com.tangem.domain.common.TapWorkarounds.isTangemNote
import com.tangem.domain.common.TapWorkarounds.isTangemTwins
import com.tangem.domain.common.TapWorkarounds.useOldStyleDerivation
@ -193,8 +194,9 @@ private class ScanWalletProcessor(
session: CardSession,
callback: (result: CompletionResult<ScanResponse>) -> Unit,
) {
val productType = when (card.isSaltPay) {
true -> ProductType.SaltPay
val productType = when {
card.isSaltPay -> ProductType.SaltPay
card.isStart2Coin -> ProductType.Start2Coin
else -> ProductType.Wallet
}
scope.launch {
@ -264,12 +266,7 @@ private class ScanWalletProcessor(
}
if (additionalBlockchainsToDerive != null) {
blockchainsToDerive.addAll(
additionalBlockchainsToDerive.map {
BlockchainNetwork(
blockchain = it,
card = card,
)
},
additionalBlockchainsToDerive.map { BlockchainNetwork(it, card) }
)
}
if (!card.useOldStyleDerivation) {
@ -280,12 +277,7 @@ private class ScanWalletProcessor(
Blockchain.RSK,
Blockchain.Fantom, Blockchain.FantomTestnet,
Blockchain.Avalanche, Blockchain.AvalancheTestnet,
).map {
BlockchainNetwork(
blockchain = it,
card = card,
)
},
).map { BlockchainNetwork(it, card) },
)
}
return blockchainsToDerive.distinct()

View file

@ -1,47 +0,0 @@
package com.tangem.tap.domain.termsOfUse
import android.content.res.Resources
import android.net.Uri
import androidx.core.os.ConfigurationCompat
import com.tangem.domain.common.CardDTO
import java.util.*
/**
[REDACTED_AUTHOR]
*/
class CardTou {
private val locale: Locale =
ConfigurationCompat.getLocales(Resources.getSystem().configuration).get(0)!!
fun getUrl(card: CardDTO): Uri? {
val issuerName = card.issuer.name
if (issuerName.lowercase(Locale.getDefault()) != "start2coin") return null
val baseUrl = "https://app.tangem.com/tou/"
val regionCode = regionCode(card.cardId) ?: "fr"
val filename = filename(locale.language, regionCode)
return Uri.parse(baseUrl + filename)
}
private fun filename(languageCode: String, regionCode: String): String {
return when {
languageCode == "fr" && regionCode == "ch" -> "Start2Coin-fr-ch-tangem.pdf"
languageCode == "de" && regionCode == "ch" -> "Start2Coin-de-ch-tangem.pdf"
languageCode == "en" && regionCode == "ch" -> "Start2Coin-en-ch-tangem.pdf"
languageCode == "it" && regionCode == "ch" -> "Start2Coin-it-ch-tangem.pdf"
languageCode == "fr" && regionCode == "fr" -> "Start2Coin-fr-fr-atangem.pdf"
languageCode == "de" && regionCode == "at" -> "Start2Coin-de-at-tangem.pdf"
regionCode == "fr" -> "Start2Coin-fr-fr-atangem.pdf"
regionCode == "ch" -> "Start2Coin-en-ch-tangem.pdf"
regionCode == "at" -> "Start2Coin-de-at-tangem.pdf"
else -> "Start2Coin-fr-fr-atangem.pdf"
}
}
private fun regionCode(cardId: String): String? = when (cardId[1]) {
'0' -> "fr"
'1' -> "ch"
'2' -> "at"
else -> null
}
}

View file

@ -4,7 +4,7 @@ import com.tangem.blockchain.common.Blockchain
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.core.analytics.Analytics
import com.tangem.tap.common.analytics.events.WalletConnect
import com.tangem.tap.common.extensions.dispatchOnMain
import com.tangem.tap.common.redux.global.GlobalAction

View file

@ -24,7 +24,7 @@ import com.tangem.common.extensions.toDecompressedPublicKey
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.core.analytics.Analytics
import com.tangem.tap.common.analytics.events.WalletConnect
import com.tangem.tap.common.extensions.safeUpdate
import com.tangem.tap.common.extensions.toFormattedString

View file

@ -4,7 +4,6 @@ import com.tangem.blockchain.common.Wallet
import com.tangem.domain.common.CardDTO
import com.tangem.domain.common.ScanResponse
import com.tangem.tap.common.entities.FiatCurrency
import com.tangem.tap.domain.termsOfUse.CardTou
import org.rekotlin.Action
sealed class DetailsAction : Action {
@ -12,10 +11,8 @@ sealed class DetailsAction : Action {
data class PrepareScreen(
val scanResponse: ScanResponse,
val wallets: List<Wallet>,
val cardTou: CardTou,
) : DetailsAction()
object ShowDisclaimer : DetailsAction()
object ReCreateTwinsWallet : DetailsAction()
sealed class ResetToFactory : DetailsAction() {

View file

@ -6,7 +6,7 @@ import com.tangem.common.doOnFailure
import com.tangem.common.doOnSuccess
import com.tangem.common.flatMap
import com.tangem.domain.common.TapWorkarounds.isTangemTwins
import com.tangem.tap.common.analytics.Analytics
import com.tangem.core.analytics.Analytics
import com.tangem.tap.common.analytics.events.AnalyticsParam
import com.tangem.tap.common.analytics.events.Settings
import com.tangem.tap.common.extensions.dispatchDialogShow
@ -61,12 +61,6 @@ class DetailsMiddleware {
is DetailsAction.ResetToFactory -> eraseWalletMiddleware.handle(action)
is DetailsAction.ManageSecurity -> manageSecurityMiddleware.handle(action)
is DetailsAction.AppSettings -> managePrivacyMiddleware.handle(state, action)
is DetailsAction.ShowDisclaimer -> {
val uri = state.cardTermsOfUseUrl
if (uri != null) {
store.dispatch(NavigationAction.OpenDocument(uri))
}
}
is DetailsAction.ReCreateTwinsWallet -> {
store.dispatch(TwinCardsAction.SetMode(CreateTwinWalletMode.RecreateWallet))
store.dispatch(NavigationAction.NavigateTo(AppScreen.OnboardingTwins))

View file

@ -52,7 +52,6 @@ private fun handlePrepareScreen(
return DetailsState(
scanResponse = action.scanResponse,
wallets = action.wallets,
cardTermsOfUseUrl = action.cardTou.getUrl(action.scanResponse.card),
createBackupAllowed = action.scanResponse.card.backupStatus == CardDTO.BackupStatus.NoBackup,
appCurrency = store.state.globalState.appCurrency,
isBiometricsAvailable = tangemSdkManager.canUseBiometry,

View file

@ -1,6 +1,5 @@
package com.tangem.tap.features.details.redux
import android.net.Uri
import com.tangem.blockchain.common.Wallet
import com.tangem.domain.common.CardDTO
import com.tangem.domain.common.ScanResponse
@ -16,7 +15,6 @@ data class DetailsState(
val scanResponse: ScanResponse? = null,
val wallets: List<Wallet> = emptyList(),
val cardSettingsState: CardSettingsState? = null,
val cardTermsOfUseUrl: Uri? = null,
val privacyPolicyUrl: String? = null,
val createBackupAllowed: Boolean = false,
val appCurrency: FiatCurrency = FiatCurrency.Default,

View file

@ -2,7 +2,7 @@ package com.tangem.tap.features.details.ui.cardsettings
import com.tangem.domain.common.TapWorkarounds.isTangemTwins
import com.tangem.domain.common.getTwinCardIdForUser
import com.tangem.tap.common.analytics.Analytics
import com.tangem.core.analytics.Analytics
import com.tangem.tap.common.analytics.events.AnalyticsParam
import com.tangem.tap.common.analytics.events.Settings
import com.tangem.tap.common.redux.AppState

View file

@ -10,7 +10,7 @@ import androidx.compose.ui.platform.ComposeView
import androidx.fragment.app.Fragment
import androidx.transition.TransitionInflater
import com.tangem.core.ui.res.TangemTheme
import com.tangem.tap.common.analytics.Analytics
import com.tangem.core.analytics.Analytics
import com.tangem.tap.common.analytics.events.Settings
import com.tangem.tap.common.redux.navigation.NavigationAction
import com.tangem.tap.features.details.redux.DetailsState

View file

@ -29,7 +29,6 @@ enum class SettingsElement(
AppSettings(R.drawable.ic_settings, R.string.app_settings_title),
LinkMoreCards(R.drawable.ic_more_cards, R.string.details_row_title_create_backup),
TermsOfService(R.drawable.ic_text, R.string.disclaimer_title), // General Terms of Service of the App
TermsOfUse(R.drawable.ic_text, R.string.details_row_title_card_tou), // Terms of Use for S2C cards only
PrivacyPolicy(R.drawable.ic_lock_24, R.string.details_row_privacy_policy);
}

View file

@ -2,7 +2,7 @@ package com.tangem.tap.features.details.ui.details
import com.tangem.domain.common.TapWorkarounds.isSaltPay
import com.tangem.domain.common.TapWorkarounds.isStart2Coin
import com.tangem.tap.common.analytics.Analytics
import com.tangem.core.analytics.Analytics
import com.tangem.tap.common.analytics.events.Settings
import com.tangem.tap.common.feedback.FeedbackEmail
import com.tangem.tap.common.feedback.SupportInfo
@ -11,7 +11,6 @@ import com.tangem.tap.common.redux.global.GlobalAction
import com.tangem.tap.common.redux.navigation.AppScreen
import com.tangem.tap.common.redux.navigation.NavigationAction
import com.tangem.tap.domain.extensions.isMultiwalletAllowed
import com.tangem.tap.domain.extensions.isTangemWallet
import com.tangem.tap.features.details.redux.DetailsAction
import com.tangem.tap.features.details.redux.DetailsState
import com.tangem.tap.features.disclaimer.redux.DisclaimerAction
@ -39,7 +38,6 @@ class DetailsViewModel(private val store: Store<AppState>) {
}
SettingsElement.AppSettings -> if (state.isBiometricsAvailable) it else null
SettingsElement.AppCurrency -> if (state.scanResponse?.card?.isMultiwalletAllowed != true) it else null
SettingsElement.TermsOfUse -> if (state.scanResponse?.card?.isStart2Coin == true) it else null
// SettingsElement.ReferralProgram -> if (state.scanResponse?.card?.isTangemWallet == true) it else null
else -> it
}
@ -89,10 +87,7 @@ class DetailsViewModel(private val store: Store<AppState>) {
store.dispatch(DetailsAction.CreateBackup)
}
SettingsElement.TermsOfService -> {
store.dispatch(DisclaimerAction.Show())
}
SettingsElement.TermsOfUse -> {
store.dispatch(DetailsAction.ShowDisclaimer)
store.dispatch(DisclaimerAction.Show(AppScreen.Details))
}
SettingsElement.PrivacyPolicy -> {
// TODO: To be available later

View file

@ -30,7 +30,7 @@ import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.tangem.tap.common.analytics.Analytics
import com.tangem.core.analytics.Analytics
import com.tangem.tap.common.analytics.events.Settings
import com.tangem.tap.common.compose.TangemTypography
import com.tangem.tap.common.extensions.getFromClipboard

View file

@ -0,0 +1,80 @@
package com.tangem.tap.features.disclaimer
import android.net.Uri
/**
[REDACTED_AUTHOR]
*/
interface Disclaimer {
fun type(): DisclaimerType
fun getUri(): Uri
fun accept()
fun isAccepted(): Boolean
}
abstract class BaseDisclaimer(
protected val dataProvider: DisclaimerDataProvider,
) : Disclaimer {
val baseUrl = "https://tangem.com"
override fun accept() {
dataProvider.storage().accept(getPreferenceKey())
}
override fun isAccepted(): Boolean = dataProvider.storage().isAccepted(getPreferenceKey())
protected open fun getPreferenceKey(): String = type().name
}
class DummyDisclaimer : Disclaimer {
override fun type(): DisclaimerType = DisclaimerType.Tangem
override fun getUri(): Uri = Uri.parse("https://tangem.com/tangem_tos.html")
override fun accept() {}
override fun isAccepted(): Boolean = false
}
class TangemDisclaimer(dataProvider: DisclaimerDataProvider) : BaseDisclaimer(dataProvider) {
override fun type(): DisclaimerType = DisclaimerType.Tangem
override fun getUri(): Uri = Uri.parse("$baseUrl/tangem_tos.html")
override fun getPreferenceKey(): String = "tangem_tos_accepted"
}
class SaltPayDisclaimer(dataProvider: DisclaimerDataProvider) : BaseDisclaimer(dataProvider) {
override fun type(): DisclaimerType = DisclaimerType.SaltPay
override fun getUri(): Uri = Uri.parse("$baseUrl/soltpay_tos.html")
override fun getPreferenceKey(): String = "saltPay_tos_accepted"
}
class Start2CoinDisclaimer(dataProvider: DisclaimerDataProvider) : BaseDisclaimer(dataProvider) {
override fun type(): DisclaimerType = DisclaimerType.Start2Coin
override fun getUri(): Uri = Uri.parse("$baseUrl/" + filename(dataProvider.getLanguage(), getRegion()))
override fun getPreferenceKey(): String = "start2Coin_tos_accepted_${getRegion()}"
private fun filename(languageCode: String, regionCode: String?): String {
return when {
languageCode == "fr" && regionCode == "ch" -> "Start2Coin-fr-ch-tangem.html"
languageCode == "de" && regionCode == "ch" -> "Start2Coin-de-ch-tangem.html"
languageCode == "en" && regionCode == "ch" -> "Start2Coin-en-ch-tangem.html"
languageCode == "it" && regionCode == "ch" -> "Start2Coin-it-ch-tangem.html"
languageCode == "fr" && regionCode == "fr" -> "Start2Coin-fr-fr-atangem.html"
languageCode == "de" && regionCode == "at" -> "Start2Coin-de-at-tangem.html"
regionCode == "fr" -> "Start2Coin-fr-fr-atangem.html"
regionCode == "ch" -> "Start2Coin-en-ch-tangem.html"
regionCode == "at" -> "Start2Coin-de-at-tangem.html"
else -> "Start2Coin-fr-fr-atangem.html"
}
}
private fun getRegion(): String? {
val cardId = dataProvider.getCardId()
if (cardId.isEmpty()) return null
return when (cardId[1]) {
'0' -> "fr"
'1' -> "ch"
'2' -> "at"
else -> null
}
}
}

View file

@ -0,0 +1,12 @@
package com.tangem.tap.features.disclaimer
import com.tangem.tap.persistence.DisclaimerPrefStorage
/**
[REDACTED_AUTHOR]
*/
interface DisclaimerDataProvider {
fun getLanguage(): String
fun getCardId(): String
fun storage(): DisclaimerPrefStorage
}

View file

@ -0,0 +1,44 @@
package com.tangem.tap.features.disclaimer
import com.tangem.domain.common.CardDTO
import com.tangem.domain.common.TapWorkarounds.isSaltPay
import com.tangem.domain.common.TapWorkarounds.isStart2Coin
import com.tangem.tap.persistence.DisclaimerPrefStorage
import com.tangem.tap.preferencesStorage
import java.util.*
/**
[REDACTED_AUTHOR]
*/
enum class DisclaimerType {
Tangem,
SaltPay,
Start2Coin;
companion object {
fun get(cardDTO: CardDTO): DisclaimerType {
return when {
cardDTO.isSaltPay -> SaltPay
cardDTO.isStart2Coin -> Start2Coin
else -> Tangem
}
}
}
}
fun DisclaimerType.createDisclaimer(cardDTO: CardDTO): Disclaimer {
val dataProvider = provideDisclaimerDataProvider(cardDTO.cardId)
return when (this) {
DisclaimerType.Tangem -> TangemDisclaimer(dataProvider)
DisclaimerType.SaltPay -> SaltPayDisclaimer(dataProvider)
DisclaimerType.Start2Coin -> Start2CoinDisclaimer(dataProvider)
}
}
private fun provideDisclaimerDataProvider(cardId: String): DisclaimerDataProvider {
return object : DisclaimerDataProvider {
override fun getLanguage(): String = Locale.getDefault().language
override fun getCardId(): String = cardId
override fun storage(): DisclaimerPrefStorage = preferencesStorage.disclaimerPrefStorage
}
}

View file

@ -1,27 +1,21 @@
package com.tangem.tap.features.disclaimer.redux
import com.tangem.common.extensions.VoidCallback
import com.tangem.tap.common.redux.navigation.AppScreen
import com.tangem.tap.features.disclaimer.Disclaimer
import com.tangem.tap.features.wallet.redux.ProgressState
import org.rekotlin.Action
sealed class DisclaimerAction : Action {
data class SetDisclaimerType(
val type: DisclaimerType,
) : DisclaimerAction()
data class SetDisclaimer(val disclaimer: Disclaimer) : DisclaimerAction()
data class Show(
val onAcceptCallback: VoidCallback? = null,
val onDismissCallback: VoidCallback? = null,
) : DisclaimerAction()
data class AcceptDisclaimer(val type: DisclaimerType) : DisclaimerAction()
internal data class UpdateState(val type: DisclaimerType, val accepted: Boolean) : DisclaimerAction()
internal data class SetCallbacks(
val onAcceptCallback: VoidCallback? = null,
val onDismissCallback: VoidCallback? = null,
val fromScreen: AppScreen,
val callback: DisclaimerCallback? = null,
) : DisclaimerAction()
object AcceptDisclaimer : DisclaimerAction()
object OnBackPressed : DisclaimerAction()
data class OnProgressStateChanged(val state: ProgressState?) : DisclaimerAction()
}

View file

@ -3,7 +3,6 @@ package com.tangem.tap.features.disclaimer.redux
import com.tangem.tap.common.redux.AppState
import com.tangem.tap.common.redux.navigation.AppScreen
import com.tangem.tap.common.redux.navigation.NavigationAction
import com.tangem.tap.preferencesStorage
import com.tangem.tap.store
import org.rekotlin.Action
import org.rekotlin.Middleware
@ -23,49 +22,17 @@ private fun handleDisclaimerMiddleware(action: Action, appState: AppState) {
val state = appState.disclaimerState
when (action) {
is DisclaimerAction.SetDisclaimerType -> {
handleUpdateState = action.type.createUpdateState()
}
is DisclaimerAction.Show -> {
handleUpdateState = state.type.createUpdateState()
store.dispatch(DisclaimerAction.SetCallbacks(action.onAcceptCallback, action.onDismissCallback))
store.dispatch(NavigationAction.NavigateTo(AppScreen.Disclaimer))
}
is DisclaimerAction.AcceptDisclaimer -> {
action.type.accept()
handleUpdateState = action.type.createUpdateState()
state.disclaimer.accept()
store.dispatch(NavigationAction.PopBackTo())
state.onAcceptCallback?.invoke()
store.dispatch(DisclaimerAction.SetCallbacks(null, null))
state.callback?.onAccept?.invoke()
}
is DisclaimerAction.OnBackPressed -> {
state.onDismissCallback?.invoke()
store.dispatch(DisclaimerAction.SetCallbacks(null, null))
store.dispatch(NavigationAction.PopBackTo())
state.callback?.onDismiss?.invoke()
}
}
}
private fun DisclaimerType.accept() = when (this) {
DisclaimerType.SaltPay -> preferencesStorage.disclaimerPrefStorage.hasSaltPayTosAccepted = true
DisclaimerType.Tangem -> preferencesStorage.disclaimerPrefStorage.hasTangemTosAccepted = true
}
fun DisclaimerType.isAccepted(): Boolean = when (this) {
DisclaimerType.SaltPay -> preferencesStorage.disclaimerPrefStorage.hasSaltPayTosAccepted
DisclaimerType.Tangem -> preferencesStorage.disclaimerPrefStorage.hasTangemTosAccepted
}
private fun DisclaimerType.createUpdateState(): DisclaimerAction.UpdateState {
return DisclaimerAction.UpdateState(this, this.isAccepted())
}
private var handleUpdateState: DisclaimerAction.UpdateState = DisclaimerAction.UpdateState(
type = DisclaimerType.Tangem,
accepted = false,
)
set(value) {
field = value
store.dispatch(value)
}
}

View file

@ -15,13 +15,19 @@ private fun internalReduce(action: Action, state: AppState): DisclaimerState {
val disclaimerState = state.disclaimerState
return when (action) {
is DisclaimerAction.UpdateState -> disclaimerState.copy(
type = action.type,
accepted = action.accepted,
is DisclaimerAction.SetDisclaimer -> disclaimerState.copy(
disclaimer = action.disclaimer,
)
is DisclaimerAction.SetCallbacks -> disclaimerState.copy(
onAcceptCallback = action.onAcceptCallback,
onDismissCallback = action.onDismissCallback,
is DisclaimerAction.Show -> disclaimerState.copy(
showedFromScreen = action.fromScreen,
callback = action.callback,
)
is DisclaimerAction.AcceptDisclaimer, is DisclaimerAction.OnBackPressed -> disclaimerState.copy(
callback = null,
progressState = null,
)
is DisclaimerAction.OnProgressStateChanged -> disclaimerState.copy(
progressState = action.state,
)
else -> disclaimerState
}

View file

@ -1,31 +1,21 @@
package com.tangem.tap.features.disclaimer.redux
import android.net.Uri
import com.tangem.common.extensions.VoidCallback
import com.tangem.domain.common.CardDTO
import com.tangem.domain.common.ScanResponse
import com.tangem.domain.common.TapWorkarounds.isSaltPay
import com.tangem.tap.common.redux.navigation.AppScreen
import com.tangem.tap.features.disclaimer.Disclaimer
import com.tangem.tap.features.disclaimer.DummyDisclaimer
import com.tangem.tap.features.wallet.redux.ProgressState
import org.rekotlin.StateType
data class DisclaimerState(
val accepted: Boolean = false,
val type: DisclaimerType = DisclaimerType.Tangem,
val onAcceptCallback: VoidCallback? = null,
val onDismissCallback: VoidCallback? = null,
) : StateType
val disclaimer: Disclaimer = DummyDisclaimer(),
val showedFromScreen: AppScreen = AppScreen.Home,
val callback: DisclaimerCallback? = null,
val progressState: ProgressState? = null,
) : StateType {
}
sealed class DisclaimerType(
val uri: Uri,
) {
object Tangem : DisclaimerType(Uri.parse("https://tangem.com/tangem_tos.html"))
object SaltPay : DisclaimerType(Uri.parse("https://tangem.com/soltpay_tos.html"))
companion object {
fun get(scanResponse: ScanResponse): DisclaimerType = get(scanResponse.card)
fun get(card: CardDTO): DisclaimerType = when {
card.isSaltPay -> SaltPay
else -> Tangem
}
}
}
data class DisclaimerCallback(
val onAccept: VoidCallback? = null,
val onDismiss: VoidCallback? = null,
)

View file

@ -5,13 +5,17 @@ import android.view.View
import androidx.transition.TransitionInflater
import by.kirich1409.viewbindingdelegate.viewBinding
import com.tangem.core.ui.fragments.setStatusBarColor
import com.tangem.tap.common.extensions.configureSettings
import com.tangem.tap.common.extensions.beginDelayedTransition
import com.tangem.tap.common.extensions.hide
import com.tangem.tap.common.extensions.show
import com.tangem.tap.common.redux.AppState
import com.tangem.tap.common.redux.navigation.AppScreen
import com.tangem.tap.features.BaseFragment
import com.tangem.tap.features.addBackPressHandler
import com.tangem.tap.features.disclaimer.Disclaimer
import com.tangem.tap.features.disclaimer.redux.DisclaimerAction
import com.tangem.tap.features.disclaimer.redux.DisclaimerState
import com.tangem.tap.features.wallet.redux.ProgressState
import com.tangem.tap.store
import com.tangem.wallet.R
import com.tangem.wallet.databinding.FragmentDisclaimerBinding
@ -20,17 +24,52 @@ import org.rekotlin.StoreSubscriber
class DisclaimerFragment : BaseFragment(R.layout.fragment_disclaimer), StoreSubscriber<DisclaimerState> {
private val binding: FragmentDisclaimerBinding by viewBinding(FragmentDisclaimerBinding::bind)
private val webViewClient = DisclaimerWebViewClient()
override fun configureTransitions() {
val inflater = TransitionInflater.from(requireContext())
when (store.state.disclaimerState.showedFromScreen) {
AppScreen.Home -> {
enterTransition = inflater.inflateTransition(android.R.transition.slide_bottom)
exitTransition = inflater.inflateTransition(android.R.transition.slide_top)
}
AppScreen.Details -> {
enterTransition = inflater.inflateTransition(android.R.transition.fade)
exitTransition = inflater.inflateTransition(android.R.transition.fade)
}
}
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
addBackPressHandler(handler = this)
binding.toolbar.setNavigationOnClickListener { handleOnBackPressed() }
binding.webView.configureSettings()
binding.apply {
toolbar.setNavigationOnClickListener { handleOnBackPressed() }
webView.apply {
settings.allowFileAccess = false
settings.javaScriptEnabled = false
webViewClient = this@DisclaimerFragment.webViewClient
}
webView.hide()
groupError.hide()
groupAccept.hide()
groupLoading.hide()
btnAccept.setOnClickListener {
store.dispatch(DisclaimerAction.AcceptDisclaimer)
}
btnRepeat.setOnClickListener {
webViewClient.reset()
}
}
}
override fun onStart() {
super.onStart()
setStatusBarColor(R.color.backgroundLightGray)
webViewClient.onProgressStateChanged = { store.dispatch(DisclaimerAction.OnProgressStateChanged(it)) }
store.subscribe(subscriber = this) { state ->
state
.skipRepeats { oldState, newState -> oldState.disclaimerState == newState.disclaimerState }
@ -39,30 +78,43 @@ class DisclaimerFragment : BaseFragment(R.layout.fragment_disclaimer), StoreSubs
}
override fun onStop() {
super.onStop()
webViewClient.onProgressStateChanged = null
store.unsubscribe(this)
}
override fun configureTransitions() {
super.configureTransitions()
val inflater = TransitionInflater.from(requireContext())
enterTransition = inflater.inflateTransition(android.R.transition.fade)
exitTransition = inflater.inflateTransition(android.R.transition.fade)
super.onStop()
}
override fun newState(state: DisclaimerState) = with(binding) {
if (activity == null || view == null) return
if (state.accepted) {
halfTransparentOverlay.hide()
btnAccept.hide()
}
updateUiVisibility(state.disclaimer, state.progressState)
binding.webView.loadUrl(state.disclaimer.getUri().toString())
}
btnAccept.setOnClickListener {
store.dispatch(DisclaimerAction.AcceptDisclaimer(state.type))
private fun updateUiVisibility(disclaimer: Disclaimer, progressState: ProgressState?) = with(binding) {
when (progressState) {
ProgressState.Loading -> {
root.beginDelayedTransition()
webView.hide()
groupError.hide()
groupAccept.hide()
groupLoading.show()
}
ProgressState.Done -> {
root.beginDelayedTransition()
groupError.hide()
groupLoading.hide()
webView.show()
groupAccept.show(!disclaimer.isAccepted())
}
ProgressState.Error -> {
root.beginDelayedTransition()
webView.hide()
groupAccept.hide()
groupLoading.hide()
groupError.show()
}
else -> {}
}
webView.loadUrl(state.type.uri.toString())
}
override fun handleOnBackPressed() {

View file

@ -0,0 +1,68 @@
package com.tangem.tap.features.disclaimer.ui
import android.graphics.Bitmap
import android.webkit.WebResourceError
import android.webkit.WebResourceRequest
import android.webkit.WebResourceResponse
import android.webkit.WebView
import android.webkit.WebViewClient
import com.tangem.common.extensions.ifNotNull
import com.tangem.tap.features.wallet.redux.ProgressState
class DisclaimerWebViewClient : WebViewClient() {
var onProgressStateChanged: ((ProgressState) -> Unit)? = null
private var loadingUrl: String? = null
private var loadedUrl: String? = null
private var progressState: ProgressState = ProgressState.Loading
set(value) {
field = value
onProgressStateChanged?.invoke(value)
}
fun reset() {
loadingUrl = null
loadedUrl = null
progressState = ProgressState.Loading
}
override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) {
super.onPageStarted(view, url, favicon)
if (loadingUrl != url) progressState = ProgressState.Loading
loadingUrl = url
}
override fun onPageFinished(view: WebView?, url: String?) {
super.onPageFinished(view, url)
if (loadedUrl != url) progressState = ProgressState.Done
loadedUrl = url
}
override fun onReceivedError(
view: WebView?,
resourceRequest: WebResourceRequest?,
error: WebResourceError?,
) {
super.onReceivedError(view, resourceRequest, error)
error?.let { progressState = ProgressState.Error }
}
override fun onReceivedHttpError(
view: WebView?,
resourceRequest: WebResourceRequest?,
errorResponse: WebResourceResponse?,
) {
super.onReceivedHttpError(view, resourceRequest, errorResponse)
ifNotNull(resourceRequest, errorResponse) { request, response ->
if (request.url?.toString() != loadingUrl || response.statusCode < 400) return
if (progressState != ProgressState.Done) return
progressState = ProgressState.Error
}
}
}

View file

@ -12,7 +12,7 @@ import androidx.compose.ui.platform.ComposeView
import androidx.core.view.WindowInsetsControllerCompat
import androidx.fragment.app.Fragment
import com.google.accompanist.appcompattheme.AppCompatTheme
import com.tangem.tap.common.analytics.Analytics
import com.tangem.core.analytics.Analytics
import com.tangem.tap.common.analytics.events.IntroductionProcess
import com.tangem.tap.common.redux.navigation.AppScreen
import com.tangem.tap.common.redux.navigation.NavigationAction

View file

@ -5,7 +5,7 @@ import android.os.Bundle
import android.view.LayoutInflater
import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.bottomsheet.BottomSheetDialog
import com.tangem.tap.common.analytics.Analytics
import com.tangem.core.analytics.Analytics
import com.tangem.tap.common.analytics.events.Token
import com.tangem.tap.common.extensions.copyToClipboard
import com.tangem.tap.common.extensions.dispatchDialogHide

View file

@ -46,6 +46,9 @@ class OnboardingHelper {
}
ProductType.Twins -> AppScreen.OnboardingTwins
ProductType.SaltPay -> AppScreen.OnboardingWallet
ProductType.Start2Coin -> throw java.lang.UnsupportedOperationException(
"Onboarding for Start2Coin cards is not supported"
)
}
}

View file

@ -9,7 +9,7 @@ import androidx.core.view.isVisible
import androidx.transition.TransitionManager
import coil.load
import com.tangem.blockchain.common.Blockchain
import com.tangem.tap.common.analytics.Analytics
import com.tangem.core.analytics.Analytics
import com.tangem.tap.common.analytics.events.Onboarding
import com.tangem.tap.common.extensions.getDrawableCompat
import com.tangem.tap.common.extensions.stripZeroPlainString

View file

@ -4,7 +4,7 @@ import com.tangem.common.CompletionResult
import com.tangem.common.extensions.guard
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.core.analytics.Analytics
import com.tangem.tap.common.analytics.events.AnalyticsParam
import com.tangem.tap.common.analytics.events.Onboarding
import com.tangem.tap.common.extensions.dispatchDebugErrorNotification

View file

@ -8,7 +8,7 @@ import androidx.constraintlayout.widget.ConstraintSet
import androidx.core.view.isVisible
import androidx.transition.TransitionManager
import coil.load
import com.tangem.tap.common.analytics.Analytics
import com.tangem.core.analytics.Analytics
import com.tangem.tap.common.analytics.events.Onboarding
import com.tangem.tap.common.extensions.getDrawableCompat
import com.tangem.tap.common.redux.navigation.ShareElement

View file

@ -4,7 +4,7 @@ import com.tangem.blockchain.common.Blockchain
import com.tangem.common.CompletionResult
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.core.analytics.Analytics
import com.tangem.tap.common.analytics.events.Onboarding
import com.tangem.tap.common.postUi
import com.tangem.tap.common.redux.AppState

View file

@ -7,7 +7,7 @@ import com.tangem.common.extensions.guard
import com.tangem.domain.common.ScanResponse
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.core.analytics.Analytics
import com.tangem.tap.common.analytics.events.AnalyticsParam
import com.tangem.tap.common.analytics.events.Onboarding
import com.tangem.tap.common.extensions.dispatchDialogShow

View file

@ -18,7 +18,7 @@ import com.tangem.domain.common.ScanResponse
import com.tangem.domain.common.TwinCardNumber
import com.tangem.tangem_sdk_new.ui.widget.leapfrogWidget.LeapfrogWidget
import com.tangem.tap.common.AndroidAssetReader
import com.tangem.tap.common.analytics.Analytics
import com.tangem.core.analytics.Analytics
import com.tangem.tap.common.analytics.events.Onboarding
import com.tangem.tap.common.extensions.beginDelayedTransition
import com.tangem.tap.common.extensions.getDrawableCompat

View file

@ -9,7 +9,7 @@ import com.tangem.domain.common.TapWorkarounds.isSaltPay
import com.tangem.domain.common.extensions.withMainContext
import com.tangem.operations.backup.BackupService
import com.tangem.tap.backupService
import com.tangem.tap.common.analytics.Analytics
import com.tangem.core.analytics.Analytics
import com.tangem.tap.common.analytics.events.Onboarding
import com.tangem.tap.common.extensions.dispatchDialogShow
import com.tangem.tap.common.extensions.dispatchOnMain

View file

@ -39,7 +39,6 @@ class GnosisRegistrator(
private val otpProcessorContractAddress: String = when (walletManager.wallet.blockchain) {
Blockchain.SaltPay -> "0x3B4397C817A26521Df8bD01a949AFDE2251d91C2"
Blockchain.SaltPayTestnet -> "0x710BF23486b549836509a08c184cE0188830f197"
else -> throw IllegalArgumentException("GnosisRegistrator supports only the SaltPay blockchain")
}

View file

@ -10,7 +10,7 @@ import com.tangem.domain.common.extensions.successOr
import com.tangem.domain.common.extensions.withMainContext
import com.tangem.datasource.api.paymentology.KYCStatus
import com.tangem.datasource.api.paymentology.RegistrationResponse
import com.tangem.tap.common.analytics.Analytics
import com.tangem.core.analytics.Analytics
import com.tangem.tap.common.analytics.events.Onboarding
import com.tangem.tap.common.extensions.dispatchDebugErrorNotification
import com.tangem.tap.common.extensions.dispatchOnMain

View file

@ -14,7 +14,7 @@ import coil.size.Scale
import com.google.android.material.appbar.MaterialToolbar
import com.google.android.material.button.MaterialButton
import com.tangem.common.extensions.guard
import com.tangem.tap.common.analytics.Analytics
import com.tangem.core.analytics.Analytics
import com.tangem.tap.common.analytics.events.Onboarding
import com.tangem.tap.common.compose.PinCodeWidget
import com.tangem.tap.common.extensions.configureSettings

View file

@ -19,7 +19,7 @@ import com.tangem.common.CardIdFormatter
import com.tangem.common.core.CardIdDisplayFormat
import com.tangem.tangem_sdk_new.ui.widget.leapfrogWidget.LeapfrogWidget
import com.tangem.tangem_sdk_new.ui.widget.leapfrogWidget.PropertyCalculator
import com.tangem.tap.common.analytics.Analytics
import com.tangem.core.analytics.Analytics
import com.tangem.tap.common.analytics.events.Onboarding
import com.tangem.tap.common.extensions.hide
import com.tangem.tap.common.extensions.inflate

View file

@ -4,7 +4,7 @@ import com.tangem.common.CompletionResult
import com.tangem.common.doOnFailure
import com.tangem.common.doOnSuccess
import com.tangem.common.flatMap
import com.tangem.tap.common.analytics.Analytics
import com.tangem.core.analytics.Analytics
import com.tangem.tap.common.analytics.events.AnalyticsParam
import com.tangem.tap.common.analytics.events.MainScreen
import com.tangem.tap.common.analytics.events.Onboarding

View file

@ -17,7 +17,7 @@ import com.tangem.domain.common.CardDTO
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.core.analytics.Analytics
import com.tangem.tap.common.analytics.events.AnalyticsParam
import com.tangem.tap.common.analytics.events.Token
import com.tangem.tap.common.extensions.dispatchDialogShow

View file

@ -16,7 +16,7 @@ import com.google.android.material.textfield.TextInputEditText
import com.tangem.Message
import com.tangem.tangem_sdk_new.extensions.hideSoftKeyboard
import com.tangem.tap.common.KeyboardObserver
import com.tangem.tap.common.analytics.Analytics
import com.tangem.core.analytics.Analytics
import com.tangem.tap.common.analytics.events.Token
import com.tangem.tap.common.entities.FiatCurrency
import com.tangem.tap.common.extensions.getFromClipboard

View file

@ -12,7 +12,7 @@ import androidx.activity.OnBackPressedCallback
import by.kirich1409.viewbindingdelegate.viewBinding
import com.tangem.tap.common.GlobalLayoutStateHandler
import com.tangem.tap.common.KeyboardObserver
import com.tangem.tap.common.analytics.Analytics
import com.tangem.core.analytics.Analytics
import com.tangem.tap.common.analytics.events.Shop
import com.tangem.tap.common.extensions.show
import com.tangem.tap.common.redux.navigation.NavigationAction

View file

@ -13,7 +13,7 @@ import androidx.compose.ui.platform.ComposeView
import com.google.accompanist.appcompattheme.AppCompatTheme
import com.tangem.domain.features.addCustomToken.redux.AddCustomTokenState
import com.tangem.domain.redux.domainStore
import com.tangem.tap.common.analytics.Analytics
import com.tangem.core.analytics.Analytics
import com.tangem.tap.common.analytics.events.ManageTokens
import com.tangem.tap.common.compose.ClosePopupTrigger
import com.tangem.tap.features.BaseStoreFragment

View file

@ -21,7 +21,7 @@ import com.tangem.domain.redux.domainStore
import com.tangem.operations.derivation.ExtendedPublicKeysMap
import com.tangem.tap.DELAY_SDK_DIALOG_CLOSE
import com.tangem.tap.assetReader
import com.tangem.tap.common.analytics.Analytics
import com.tangem.core.analytics.Analytics
import com.tangem.tap.common.analytics.events.ManageTokens
import com.tangem.tap.common.extensions.dispatchDebugErrorNotification
import com.tangem.tap.common.extensions.dispatchOnMain

View file

@ -13,7 +13,7 @@ import androidx.transition.TransitionInflater
import by.kirich1409.viewbindingdelegate.viewBinding
import com.google.accompanist.appcompattheme.AppCompatTheme
import com.tangem.blockchain.common.Blockchain
import com.tangem.tap.common.analytics.Analytics
import com.tangem.core.analytics.Analytics
import com.tangem.tap.common.analytics.events.ManageTokens
import com.tangem.tap.common.extensions.copyToClipboard
import com.tangem.tap.common.extensions.dispatchNotification

View file

@ -29,7 +29,7 @@ import com.tangem.blockchain.common.Blockchain
import com.tangem.core.ui.components.SystemBarsEffect
import com.tangem.domain.common.TapWorkarounds.useOldStyleDerivation
import com.tangem.domain.common.extensions.fromNetworkId
import com.tangem.tap.common.analytics.Analytics
import com.tangem.core.analytics.Analytics
import com.tangem.tap.common.analytics.events.AnalyticsParam
import com.tangem.tap.common.analytics.events.ManageTokens
import com.tangem.tap.common.compose.Keyboard

View file

@ -3,7 +3,7 @@ package com.tangem.tap.features.wallet.redux.middlewares
import com.tangem.common.services.Result
import com.tangem.datasource.api.tangemTech.CurrenciesResponse
import com.tangem.datasource.api.tangemTech.TangemTechService
import com.tangem.tap.common.analytics.Analytics
import com.tangem.core.analytics.Analytics
import com.tangem.tap.common.analytics.events.AnalyticsParam
import com.tangem.tap.common.analytics.events.MainScreen
import com.tangem.tap.common.entities.FiatCurrency

View file

@ -6,7 +6,7 @@ import com.tangem.blockchain.common.WalletManager
import com.tangem.common.doOnSuccess
import com.tangem.common.extensions.guard
import com.tangem.domain.common.extensions.withMainContext
import com.tangem.tap.common.analytics.Analytics
import com.tangem.core.analytics.Analytics
import com.tangem.tap.common.analytics.events.AnalyticsParam
import com.tangem.tap.common.analytics.events.MainScreen
import com.tangem.tap.common.analytics.events.Token.ButtonRemoveToken

View file

@ -2,7 +2,7 @@ package com.tangem.tap.features.wallet.redux.middlewares
import com.tangem.blockchain.blockchains.ethereum.EthereumWalletManager
import com.tangem.blockchain.common.AmountType
import com.tangem.tap.common.analytics.Analytics
import com.tangem.core.analytics.Analytics
import com.tangem.tap.common.analytics.events.AnalyticsParam
import com.tangem.tap.common.analytics.events.Token
import com.tangem.tap.common.extensions.dispatchDebugErrorNotification

View file

@ -1,6 +1,6 @@
package com.tangem.tap.features.wallet.redux.middlewares
import com.tangem.tap.common.analytics.Analytics
import com.tangem.core.analytics.Analytics
import com.tangem.tap.common.analytics.events.AnalyticsParam
import com.tangem.tap.common.analytics.events.Token
import com.tangem.tap.common.extensions.dispatchDialogHide

View file

@ -11,7 +11,7 @@ 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.core.analytics.Analytics
import com.tangem.tap.common.analytics.events.MainScreen
import com.tangem.tap.common.analytics.events.Token
import com.tangem.tap.common.extensions.copyToClipboard

View file

@ -22,7 +22,7 @@ import com.tangem.domain.common.TapWorkarounds.derivationStyle
import com.tangem.tangem_sdk_new.extensions.dpToPx
import com.tangem.tap.common.SnackbarHandler
import com.tangem.tap.common.TestActions
import com.tangem.tap.common.analytics.Analytics
import com.tangem.core.analytics.Analytics
import com.tangem.tap.common.analytics.events.DetailsScreen
import com.tangem.tap.common.analytics.events.Token
import com.tangem.tap.common.extensions.appendIfNotNull

View file

@ -21,7 +21,7 @@ import com.tangem.core.ui.fragments.setStatusBarColor
import com.tangem.core.ui.utils.OneTouchClickListener
import com.tangem.domain.common.TapWorkarounds.isSaltPay
import com.tangem.tap.MainActivity
import com.tangem.tap.common.analytics.Analytics
import com.tangem.core.analytics.Analytics
import com.tangem.tap.common.analytics.converters.BasicSignInEventConverter
import com.tangem.tap.common.analytics.converters.BasicTopUpEventConverter
import com.tangem.tap.common.analytics.events.MainScreen
@ -34,7 +34,6 @@ import com.tangem.tap.common.redux.navigation.NavigationAction
import com.tangem.tap.domain.configurable.warningMessage.WarningMessage
import com.tangem.tap.domain.statePrinter.printScanResponseState
import com.tangem.tap.domain.statePrinter.printWalletState
import com.tangem.tap.domain.termsOfUse.CardTou
import com.tangem.tap.features.details.redux.DetailsAction
import com.tangem.tap.features.wallet.redux.ErrorType
import com.tangem.tap.features.wallet.redux.ProgressState
@ -245,7 +244,6 @@ class WalletFragment : Fragment(R.layout.fragment_wallet), StoreSubscriber<Walle
DetailsAction.PrepareScreen(
scanResponse = scanNoteResponse,
wallets = store.state.walletState.walletManagers.map { it.wallet },
cardTou = CardTou(),
),
)
store.dispatch(NavigationAction.NavigateTo(AppScreen.Details))

View file

@ -7,7 +7,7 @@ import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import com.tangem.domain.common.TapWorkarounds.derivationStyle
import com.tangem.tap.common.analytics.Analytics
import com.tangem.core.analytics.Analytics
import com.tangem.tap.common.analytics.events.Portfolio
import com.tangem.tap.common.extensions.getString
import com.tangem.tap.common.extensions.hide

View file

@ -7,7 +7,7 @@ import androidx.recyclerview.widget.DiffUtil
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.core.analytics.Analytics
import com.tangem.tap.common.analytics.events.AnalyticsParam
import com.tangem.tap.common.analytics.events.MainScreen
import com.tangem.tap.common.extensions.getActivity

View file

@ -4,7 +4,7 @@ import android.content.Context
import android.os.Bundle
import android.view.LayoutInflater
import com.google.android.material.bottomsheet.BottomSheetDialog
import com.tangem.tap.common.analytics.Analytics
import com.tangem.core.analytics.Analytics
import com.tangem.tap.common.analytics.events.AnalyticsParam
import com.tangem.tap.common.analytics.events.Onboarding
import com.tangem.tap.common.analytics.events.Token

View file

@ -2,7 +2,7 @@ package com.tangem.tap.features.wallet.ui.dialogs
import android.content.Context
import androidx.appcompat.app.AlertDialog
import com.tangem.tap.common.analytics.Analytics
import com.tangem.core.analytics.Analytics
import com.tangem.tap.common.analytics.events.IntroductionProcess
import com.tangem.tap.common.extensions.dispatchDialogHide
import com.tangem.tap.common.feedback.ScanFailsEmail

View file

@ -7,7 +7,7 @@ import com.badoo.mvicore.DiffStrategy
import com.badoo.mvicore.modelWatcher
import com.tangem.domain.common.TapWorkarounds.derivationStyle
import com.tangem.domain.common.TapWorkarounds.isTestCard
import com.tangem.tap.common.analytics.Analytics
import com.tangem.core.analytics.Analytics
import com.tangem.tap.common.analytics.events.MainScreen
import com.tangem.tap.common.analytics.events.Portfolio
import com.tangem.tap.common.extensions.animateVisibility

View file

@ -8,7 +8,7 @@ import com.tangem.common.flatMap
import com.tangem.common.map
import com.tangem.domain.common.ScanResponse
import com.tangem.domain.common.util.UserWalletId
import com.tangem.tap.common.analytics.Analytics
import com.tangem.core.analytics.Analytics
import com.tangem.tap.common.analytics.events.MyWallets
import com.tangem.tap.common.extensions.dispatchOnMain
import com.tangem.tap.common.extensions.onUserWalletSelected
@ -139,7 +139,16 @@ internal class WalletSelectorMiddleware {
tangemSdkManager.setAccessCodeRequestPolicy(
useBiometricsForAccessCode = preferencesStorage.shouldSaveAccessCodes,
)
ScanCardProcessor.scan(
onWalletNotCreated = {
// No need to rollback policy, continue with the policy set before the card scan
store.dispatchOnMain(WalletSelectorAction.AddWallet.Success)
store.dispatchOnMain(NavigationAction.PopBackTo(AppScreen.Wallet))
},
disclaimerWillShow = {
store.dispatchOnMain(NavigationAction.PopBackTo())
},
onSuccess = { scanResponse ->
saveUserWalletAndPopBackToWalletScreen(scanResponse)
.doOnFailure { error ->
@ -153,11 +162,6 @@ internal class WalletSelectorMiddleware {
tangemSdkManager.setAccessCodeRequestPolicy(prevUseBiometricsForAccessCode)
store.dispatchOnMain(WalletSelectorAction.AddWallet.Error(error))
},
onWalletNotCreated = {
// No need to rollback policy, continue with the policy set before the card scan
store.dispatchOnMain(WalletSelectorAction.AddWallet.Success)
store.dispatchOnMain(NavigationAction.PopBackTo(AppScreen.Wallet))
},
)
}

View file

@ -22,7 +22,7 @@ import androidx.compose.ui.platform.rememberNestedScrollInteropConnection
import androidx.fragment.app.viewModels
import com.tangem.core.ui.fragments.ComposeBottomSheetFragment
import com.tangem.core.ui.res.TangemTheme
import com.tangem.tap.common.analytics.Analytics
import com.tangem.core.analytics.Analytics
import com.tangem.tap.common.analytics.events.MyWallets
import com.tangem.tap.features.details.ui.cardsettings.resolveReference
import com.tangem.tap.features.walletSelector.ui.components.RenameWalletDialogContent

View file

@ -10,16 +10,11 @@ class DisclaimerPrefStorage(
private val preferences: SharedPreferences,
) {
var hasTangemTosAccepted: Boolean
get() = preferences.getBoolean(KEY_TANGEM_TOS, false)
set(value) = preferences.edit { putBoolean(KEY_TANGEM_TOS, value) }
fun accept(disclaimerKey: String) {
preferences.edit { putBoolean(disclaimerKey, true) }
}
var hasSaltPayTosAccepted: Boolean
get() = preferences.getBoolean(KEY_SALT_PAY_TOS, false)
set(value) = preferences.edit { putBoolean(KEY_SALT_PAY_TOS, value) }
companion object {
private const val KEY_TANGEM_TOS = "tangem_tos_accepted"
private const val KEY_SALT_PAY_TOS = "saltPay_tos_accepted"
fun isAccepted(disclaimerKey: String): Boolean {
return preferences.getBoolean(disclaimerKey, false)
}
}

View file

@ -33,6 +33,7 @@
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<androidx.core.widget.NestedScrollView
android:id="@+id/nested_scroll"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginStart="16dp"
@ -53,35 +54,83 @@
</androidx.core.widget.NestedScrollView>
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintGuide_percent="0.37" />
<View
android:id="@+id/half_transparent_overlay"
android:layout_width="match_parent"
android:layout_height="100dp"
android:layout_height="140dp"
android:background="@drawable/bg_half_transparent_overlay"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<ProgressBar
android:id="@+id/progress"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/tv_error"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:lineSpacingExtra="5sp"
android:text="@string/disclaimer_error_loading"
android:textAlignment="center"
android:textSize="16sp"
app:layout_constraintBottom_toTopOf="@+id/btn_repeat"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_chainStyle="packed" />
<Button
android:id="@+id/btn_repeat"
style="@style/OnboardingButton.Alternative"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="@string/common_retry"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tv_error" />
<com.google.android.material.button.MaterialButton
android:id="@+id/btn_accept"
style="@style/TapButtonWithIcon"
style="@style/TapButton"
android:layout_width="0dp"
android:layout_marginStart="7dp"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:layout_marginBottom="24dp"
android:text="@string/common_accept"
app:icon="@drawable/ic_send"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/guideline" />
app:layout_constraintStart_toStartOf="parent" />
<androidx.constraintlayout.widget.Group
android:id="@+id/group_error"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:constraint_referenced_ids="btn_repeat, tv_error" />
<androidx.constraintlayout.widget.Group
android:id="@+id/group_loading"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:constraint_referenced_ids="progress" />
<androidx.constraintlayout.widget.Group
android:id="@+id/group_accept"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:constraint_referenced_ids="btn_accept, half_transparent_overlay" />
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -59,7 +59,7 @@ object Versions {
// endregion Other libraries
// region Tangem
const val tangemBlockchainSdk = "develop-141"
const val tangemBlockchainSdk = "develop-151"
const val tangemCardSgk = "develop-179"
// endregion Tangem

1
core/analytics/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
/build

Some files were not shown because too many files have changed in this diff Show more