Updated on 2026-08-14

This commit is contained in:
Tangem 2026-02-26 21:35:53 +07:00
parent d349739524
commit 1806ddc31d
18 changed files with 55 additions and 11 deletions

View file

@ -20,6 +20,7 @@ internal class DefaultScanCardProcessor(
cardId: String?, cardId: String?,
allowsRequestAccessCodeFromRepository: Boolean, allowsRequestAccessCodeFromRepository: Boolean,
analyticsSource: AnalyticsParam.ScreensSources, analyticsSource: AnalyticsParam.ScreensSources,
shouldCheckIsAlreadyActivated: Boolean,
): CompletionResult<ScanResponse> { ): CompletionResult<ScanResponse> {
return if (isNewCardScanningEnabled) { return if (isNewCardScanningEnabled) {
UseCaseScanProcessor.scan(cardId, allowsRequestAccessCodeFromRepository) UseCaseScanProcessor.scan(cardId, allowsRequestAccessCodeFromRepository)
@ -28,6 +29,7 @@ internal class DefaultScanCardProcessor(
analyticsSource = analyticsSource, analyticsSource = analyticsSource,
cardId = cardId, cardId = cardId,
allowsRequestAccessCodeFromRepository = allowsRequestAccessCodeFromRepository, allowsRequestAccessCodeFromRepository = allowsRequestAccessCodeFromRepository,
shouldCheckIsAlreadyActivated = shouldCheckIsAlreadyActivated,
) )
} }
} }
@ -35,6 +37,7 @@ internal class DefaultScanCardProcessor(
@Suppress("LongParameterList") @Suppress("LongParameterList")
override suspend fun scan( override suspend fun scan(
analyticsSource: AnalyticsParam.ScreensSources, analyticsSource: AnalyticsParam.ScreensSources,
shouldCheckIsAlreadyActivated: Boolean,
cardId: String?, cardId: String?,
onProgressStateChange: suspend (showProgress: Boolean) -> Unit, onProgressStateChange: suspend (showProgress: Boolean) -> Unit,
onWalletNotCreated: suspend () -> Unit, onWalletNotCreated: suspend () -> Unit,
@ -56,6 +59,7 @@ internal class DefaultScanCardProcessor(
} else { } else {
legacyScanProcessor.scan( legacyScanProcessor.scan(
analyticsSource = analyticsSource, analyticsSource = analyticsSource,
shouldCheckIsAlreadyActivated = shouldCheckIsAlreadyActivated,
cardId = cardId, cardId = cardId,
onProgressStateChange = onProgressStateChange, onProgressStateChange = onProgressStateChange,
onWalletNotCreated = onWalletNotCreated, onWalletNotCreated = onWalletNotCreated,

View file

@ -55,10 +55,12 @@ internal class LegacyScanProcessor @Inject constructor(
cardId: String? = null, cardId: String? = null,
allowsRequestAccessCodeFromRepository: Boolean = false, allowsRequestAccessCodeFromRepository: Boolean = false,
analyticsSource: AnalyticsParam.ScreensSources, analyticsSource: AnalyticsParam.ScreensSources,
shouldCheckIsAlreadyActivated: Boolean,
): CompletionResult<ScanResponse> { ): CompletionResult<ScanResponse> {
return tangemSdkManager.scanProduct( return tangemSdkManager.scanProduct(
cardId = cardId, cardId = cardId,
allowsRequestAccessCodeFromRepository = allowsRequestAccessCodeFromRepository, allowsRequestAccessCodeFromRepository = allowsRequestAccessCodeFromRepository,
shouldCheckIsAlreadyActivated = shouldCheckIsAlreadyActivated,
) )
.doOnFailure { error -> .doOnFailure { error ->
onScanFailure(analyticsSource = analyticsSource, error = error, onFailure = {}, onCancel = {}) onScanFailure(analyticsSource = analyticsSource, error = error, onFailure = {}, onCancel = {})
@ -68,6 +70,7 @@ internal class LegacyScanProcessor @Inject constructor(
@Suppress("LongParameterList") @Suppress("LongParameterList")
suspend fun scan( suspend fun scan(
analyticsSource: AnalyticsParam.ScreensSources, analyticsSource: AnalyticsParam.ScreensSources,
shouldCheckIsAlreadyActivated: Boolean,
cardId: String?, cardId: String?,
onProgressStateChange: suspend (showProgress: Boolean) -> Unit, onProgressStateChange: suspend (showProgress: Boolean) -> Unit,
onWalletNotCreated: suspend () -> Unit, onWalletNotCreated: suspend () -> Unit,
@ -80,7 +83,10 @@ internal class LegacyScanProcessor @Inject constructor(
tangemSdkManager.changeDisplayedCardIdNumbersCount(null) tangemSdkManager.changeDisplayedCardIdNumbersCount(null)
val result = tangemSdkManager.scanProduct(cardId) val result = tangemSdkManager.scanProduct(
cardId = cardId,
shouldCheckIsAlreadyActivated = shouldCheckIsAlreadyActivated,
)
val analyticsEvent = Basic.CardWasScanned(analyticsSource) val analyticsEvent = Basic.CardWasScanned(analyticsSource)
store.dispatchOnMain(GlobalAction.ScanFailsCounter.ChooseBehavior(result, analyticsSource)) store.dispatchOnMain(GlobalAction.ScanFailsCounter.ChooseBehavior(result, analyticsSource))

View file

@ -13,11 +13,16 @@ internal class DefaultScanCardRepository(
private val exceptionConverter = ScanCardExceptionConverter() private val exceptionConverter = ScanCardExceptionConverter()
override suspend fun scanCard(cardId: String?, allowRequestAccessCodeFromStorage: Boolean): ScanResponse { override suspend fun scanCard(
cardId: String?,
allowRequestAccessCodeFromStorage: Boolean,
shouldCheckIsAlreadyActivated: Boolean,
): ScanResponse {
return when ( return when (
val result = tangemSdkManager.scanProduct( val result = tangemSdkManager.scanProduct(
cardId = cardId, cardId = cardId,
allowsRequestAccessCodeFromRepository = allowRequestAccessCodeFromStorage, allowsRequestAccessCodeFromRepository = allowRequestAccessCodeFromStorage,
shouldCheckIsAlreadyActivated = shouldCheckIsAlreadyActivated,
) )
) { ) {
is CompletionResult.Success -> result.data is CompletionResult.Success -> result.data

View file

@ -166,6 +166,7 @@ internal class DefaultTangemSdkManager(
cardId: String?, cardId: String?,
messageRes: Int?, messageRes: Int?,
allowsRequestAccessCodeFromRepository: Boolean, allowsRequestAccessCodeFromRepository: Boolean,
shouldCheckIsAlreadyActivated: Boolean,
): CompletionResult<ScanResponse> { ): CompletionResult<ScanResponse> {
val message = Message(resources.getStringSafe(messageRes ?: R.string.initial_message_scan_header)) val message = Message(resources.getStringSafe(messageRes ?: R.string.initial_message_scan_header))
return coroutineScope { return coroutineScope {
@ -176,7 +177,7 @@ internal class DefaultTangemSdkManager(
allowsRequestAccessCodeFromRepository = allowsRequestAccessCodeFromRepository, allowsRequestAccessCodeFromRepository = allowsRequestAccessCodeFromRepository,
visaCardScanHandler = visaCardScanHandler, visaCardScanHandler = visaCardScanHandler,
visaCoroutineScope = this, visaCoroutineScope = this,
shouldCheckIsAlreadyActivated = true, shouldCheckIsAlreadyActivated = shouldCheckIsAlreadyActivated,
onboardingV2FeatureToggles = onboardingV2FeatureToggles, onboardingV2FeatureToggles = onboardingV2FeatureToggles,
), ),
cardId = cardId, cardId = cardId,

View file

@ -59,6 +59,7 @@ class MockTangemSdkManager(
cardId: String?, cardId: String?,
messageRes: Int?, messageRes: Int?,
allowsRequestAccessCodeFromRepository: Boolean, allowsRequestAccessCodeFromRepository: Boolean,
shouldCheckIsAlreadyActivated: Boolean,
): CompletionResult<ScanResponse> { ): CompletionResult<ScanResponse> {
return MockProvider.getScanResponse() return MockProvider.getScanResponse()
} }

View file

@ -296,7 +296,9 @@ internal class DefaultUserWalletsListRepository(
} }
val scanResponse = unlockMethod.scanResponse ?: run { val scanResponse = unlockMethod.scanResponse ?: run {
when (val res = tangemSdkManagerProvider().scanProduct()) { when (val res = tangemSdkManagerProvider().scanProduct(
shouldCheckIsAlreadyActivated = false,
)) {
is CompletionResult.Failure -> raise(UnlockWalletError.UserCancelled) is CompletionResult.Failure -> raise(UnlockWalletError.UserCancelled)
is CompletionResult.Success -> res.data is CompletionResult.Success -> res.data
} }

View file

@ -107,6 +107,7 @@ internal class CardSettingsModel @Inject constructor(
private fun scanCard() = modelScope.launch { private fun scanCard() = modelScope.launch {
scanCardProcessor.scan( scanCardProcessor.scan(
analyticsSource = com.tangem.core.analytics.models.AnalyticsParam.ScreensSources.Settings, analyticsSource = com.tangem.core.analytics.models.AnalyticsParam.ScreensSources.Settings,
shouldCheckIsAlreadyActivated = false,
allowsRequestAccessCodeFromRepository = true, allowsRequestAccessCodeFromRepository = true,
) )
.doOnSuccess { scanResponse -> .doOnSuccess { scanResponse ->

View file

@ -11,10 +11,12 @@ interface ScanCardProcessor {
cardId: String? = null, cardId: String? = null,
allowsRequestAccessCodeFromRepository: Boolean = false, allowsRequestAccessCodeFromRepository: Boolean = false,
analyticsSource: AnalyticsParam.ScreensSources, analyticsSource: AnalyticsParam.ScreensSources,
shouldCheckIsAlreadyActivated: Boolean,
): CompletionResult<ScanResponse> ): CompletionResult<ScanResponse>
suspend fun scan( suspend fun scan(
analyticsSource: AnalyticsParam.ScreensSources, analyticsSource: AnalyticsParam.ScreensSources,
shouldCheckIsAlreadyActivated: Boolean,
cardId: String? = null, cardId: String? = null,
onProgressStateChange: suspend (showProgress: Boolean) -> Unit = {}, onProgressStateChange: suspend (showProgress: Boolean) -> Unit = {},
onWalletNotCreated: suspend () -> Unit = {}, onWalletNotCreated: suspend () -> Unit = {},

View file

@ -49,8 +49,13 @@ class ScanCardUseCase(
cardId: String?, cardId: String?,
allowRequestAccessCodeFromStorage: Boolean, allowRequestAccessCodeFromStorage: Boolean,
): Either<ScanCardException, ScanResponse> { ): Either<ScanCardException, ScanResponse> {
return Either.catch { scanCardRepository.scanCard(cardId, allowRequestAccessCodeFromStorage) } return Either.catch {
.mapLeft { e -> scanCardRepository.scanCard(
cardId = cardId,
allowRequestAccessCodeFromStorage = allowRequestAccessCodeFromStorage,
shouldCheckIsAlreadyActivated = true, // TODO use correct when enable NEW_CARD_SCANNING_ENABLED
)
}.mapLeft { e ->
e as? ScanCardException ?: ScanCardException.UnknownException(e) e as? ScanCardException ?: ScanCardException.UnknownException(e)
} }
} }

View file

@ -17,5 +17,9 @@ interface ScanCardRepository {
* @return a [ScanResponse] object with the result of the scan. * @return a [ScanResponse] object with the result of the scan.
* @throws [ScanCardException] if the scan process fails. * @throws [ScanCardException] if the scan process fails.
*/ */
suspend fun scanCard(cardId: String?, allowRequestAccessCodeFromStorage: Boolean): ScanResponse suspend fun scanCard(
cardId: String?,
allowRequestAccessCodeFromStorage: Boolean,
shouldCheckIsAlreadyActivated: Boolean,
): ScanResponse
} }

View file

@ -186,6 +186,7 @@ internal class CreateWalletStartModel @Inject constructor(
scanCardProcessor.scan( scanCardProcessor.scan(
analyticsSource = analyticsSource, analyticsSource = analyticsSource,
shouldCheckIsAlreadyActivated = true,
onProgressStateChange = { showProgress -> onProgressStateChange = { showProgress ->
if (!showProgress) { if (!showProgress) {
delay(HIDE_PROGRESS_DELAY) delay(HIDE_PROGRESS_DELAY)

View file

@ -120,6 +120,7 @@ internal class UserWalletSaver @Inject constructor(
scope.launch { scope.launch {
scanCardProcessor.scan( scanCardProcessor.scan(
analyticsSource = AnalyticsParam.ScreensSources.Settings, analyticsSource = AnalyticsParam.ScreensSources.Settings,
shouldCheckIsAlreadyActivated = true,
onWalletNotCreated = { onWalletNotCreated = {
continuation.resume(Either.Right(null)) continuation.resume(Either.Right(null))
}, },

View file

@ -171,6 +171,7 @@ internal class HomeModel @Inject constructor(
scanCardProcessor.scan( scanCardProcessor.scan(
analyticsSource = analyticsSource, analyticsSource = analyticsSource,
shouldCheckIsAlreadyActivated = true,
onProgressStateChange = { showProgress -> onProgressStateChange = { showProgress ->
if (!showProgress) { if (!showProgress) {
delay(HIDE_PROGRESS_DELAY) delay(HIDE_PROGRESS_DELAY)

View file

@ -104,6 +104,7 @@ internal class CreateHardwareWalletModel @Inject constructor(
scanCardProcessor.scan( scanCardProcessor.scan(
analyticsSource = analyticsSource, analyticsSource = analyticsSource,
shouldCheckIsAlreadyActivated = true,
onProgressStateChange = { showProgress -> onProgressStateChange = { showProgress ->
if (!showProgress) { if (!showProgress) {
delay(HIDE_PROGRESS_DELAY) delay(HIDE_PROGRESS_DELAY)

View file

@ -115,7 +115,7 @@ internal class UpgradeWalletModel @Inject constructor(
) )
tangemSdkManager tangemSdkManager
.scanProduct() .scanProduct(shouldCheckIsAlreadyActivated = true)
.doOnSuccess { scanResponse -> .doOnSuccess { scanResponse ->
checkIsWalletSuitableToBeUsedAsUpgrade(scanResponse = scanResponse) { checkIsWalletSuitableToBeUsedAsUpgrade(scanResponse = scanResponse) {
delay(DELAY_SDK_DIALOG_CLOSE) delay(DELAY_SDK_DIALOG_CLOSE)

View file

@ -192,6 +192,7 @@ internal class WelcomeModel @Inject constructor(
modelScope.launch { modelScope.launch {
scanCardProcessor.scan( scanCardProcessor.scan(
analyticsSource = AnalyticsParam.ScreensSources.SignIn, analyticsSource = AnalyticsParam.ScreensSources.SignIn,
shouldCheckIsAlreadyActivated = true,
onWalletNotCreated = {}, onWalletNotCreated = {},
disclaimerWillShow = { router.pop() }, disclaimerWillShow = { router.pop() },
onSuccess = { scanResponse -> onSuccess = { scanResponse ->

View file

@ -7,7 +7,7 @@
tangemBlockchainSdk = "releases-5.34-1430" tangemBlockchainSdk = "releases-5.34-1430"
#tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds #tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds
tangemCardSdk = "releases-5.34-581" tangemCardSdk = "releases-5.34-584"
#tangemCardSdk = "0.0.1" # Keep it! - used for local builds ^ #tangemCardSdk = "0.0.1" # Keep it! - used for local builds ^
tangemVico = "2.0.0-alpha.25-tangem12" tangemVico = "2.0.0-alpha.25-tangem12"
#tangemVico = "0.0.1" # Keep it! - used for local builds ^ #tangemVico = "0.0.1" # Keep it! - used for local builds ^

View file

@ -45,10 +45,18 @@ interface TangemSdkManager {
suspend fun checkNeedEnrollBiometrics(awaitInitialization: Boolean = true): Boolean suspend fun checkNeedEnrollBiometrics(awaitInitialization: Boolean = true): Boolean
/**
* Scans the card and returns [ScanResponse] with the card information.
* If [cardId] is provided, it will try to find the card with this ID in the card reader's range.
* If [allowsRequestAccessCodeFromRepository] is true, it will try to request access code from the repository
* if the card is protected by access code and the access code is not saved in the repository.
* If [shouldCheckIsAlreadyActivated] is true, it will check if the card is already activated and return an error if it is.
*/
suspend fun scanProduct( suspend fun scanProduct(
cardId: String? = null, cardId: String? = null,
messageRes: Int? = null, messageRes: Int? = null,
allowsRequestAccessCodeFromRepository: Boolean = false, allowsRequestAccessCodeFromRepository: Boolean = false,
shouldCheckIsAlreadyActivated: Boolean,
): CompletionResult<ScanResponse> ): CompletionResult<ScanResponse>
suspend fun createProductWallet( suspend fun createProductWallet(