Updated on 2026-08-14

This commit is contained in:
Tangem 2026-03-20 11:34:58 +03:00
parent d748cb7a81
commit 2070f1d21c
4 changed files with 39 additions and 148 deletions

View file

@ -1,15 +1,9 @@
package com.tangem.tap.di package com.tangem.tap.di
import android.content.Context import android.content.Context
import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.core.analytics.api.AnalyticsErrorHandler
import com.tangem.core.analytics.api.AnalyticsExceptionHandler
import com.tangem.core.decompose.di.GlobalUiMessageSender
import com.tangem.core.decompose.ui.UiMessageSender
import com.tangem.core.navigation.finisher.AppFinisher
import com.tangem.domain.card.BuildConfig import com.tangem.domain.card.BuildConfig
import com.tangem.domain.card.repository.CardSdkConfigRepository import com.tangem.domain.card.repository.CardSdkConfigRepository
import com.tangem.utils.coroutines.AppCoroutineScope
import com.tangem.domain.feedback.SendFeedbackEmailUseCase
import com.tangem.features.onboarding.v2.OnboardingV2FeatureToggles import com.tangem.features.onboarding.v2.OnboardingV2FeatureToggles
import com.tangem.sdk.api.TangemSdkManager import com.tangem.sdk.api.TangemSdkManager
import com.tangem.tap.domain.sdk.impl.DefaultTangemSdkManager import com.tangem.tap.domain.sdk.impl.DefaultTangemSdkManager
@ -38,13 +32,8 @@ internal class TangemSdkManagerModule {
visaCardActivationTaskFactory: VisaCardActivationTask.Factory, visaCardActivationTaskFactory: VisaCardActivationTask.Factory,
tangemPayChallengeTaskFactory: TangemPayGenerateAddressAndSignChallengeTask.Factory, tangemPayChallengeTaskFactory: TangemPayGenerateAddressAndSignChallengeTask.Factory,
onboardingV2FeatureToggles: OnboardingV2FeatureToggles, onboardingV2FeatureToggles: OnboardingV2FeatureToggles,
@GlobalUiMessageSender uiMessageSender: UiMessageSender,
appFinisher: AppFinisher,
sendFeedbackEmailUseCase: SendFeedbackEmailUseCase,
analyticsExceptionHandler: AnalyticsExceptionHandler,
blockchainToDeriveFinder: BlockchainToDeriveFinder, blockchainToDeriveFinder: BlockchainToDeriveFinder,
analyticsEventHandler: AnalyticsEventHandler, analyticsErrorHandler: AnalyticsErrorHandler,
appScope: AppCoroutineScope,
): TangemSdkManager { ): TangemSdkManager {
return if (BuildConfig.MOCK_DATA_SOURCE) { return if (BuildConfig.MOCK_DATA_SOURCE) {
MockTangemSdkManager(resources = context.resources) MockTangemSdkManager(resources = context.resources)
@ -56,13 +45,8 @@ internal class TangemSdkManagerModule {
visaCardActivationTaskFactory = visaCardActivationTaskFactory, visaCardActivationTaskFactory = visaCardActivationTaskFactory,
tangemPayChallengeTaskFactory = tangemPayChallengeTaskFactory, tangemPayChallengeTaskFactory = tangemPayChallengeTaskFactory,
onboardingV2FeatureToggles = onboardingV2FeatureToggles, onboardingV2FeatureToggles = onboardingV2FeatureToggles,
uiMessageSender = uiMessageSender,
appFinisher = appFinisher,
sendFeedbackEmailUseCase = sendFeedbackEmailUseCase,
analyticsExceptionHandler = analyticsExceptionHandler,
blockchainToDeriveFinder = blockchainToDeriveFinder, blockchainToDeriveFinder = blockchainToDeriveFinder,
analyticsEventHandler = analyticsEventHandler, analyticsErrorHandler = analyticsErrorHandler,
coroutineScope = appScope,
) )
} }
} }

View file

@ -6,11 +6,9 @@ import androidx.annotation.StringRes
import arrow.core.Either import arrow.core.Either
import arrow.core.left import arrow.core.left
import arrow.core.right import arrow.core.right
import com.tangem.Log
import com.tangem.Message import com.tangem.Message
import com.tangem.TangemSdk import com.tangem.TangemSdk
import com.tangem.common.* import com.tangem.common.*
import com.tangem.common.authentication.AuthenticationManager
import com.tangem.common.authentication.keystore.KeystoreManager import com.tangem.common.authentication.keystore.KeystoreManager
import com.tangem.common.core.* import com.tangem.common.core.*
import com.tangem.common.extensions.ByteArrayKey import com.tangem.common.extensions.ByteArrayKey
@ -18,25 +16,14 @@ import com.tangem.common.extensions.hexToBytes
import com.tangem.common.services.secure.SecureStorage import com.tangem.common.services.secure.SecureStorage
import com.tangem.common.usersCode.UserCodeRepository import com.tangem.common.usersCode.UserCodeRepository
import com.tangem.core.analytics.Analytics import com.tangem.core.analytics.Analytics
import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.core.analytics.api.AnalyticsErrorHandler
import com.tangem.core.analytics.api.AnalyticsExceptionHandler import com.tangem.core.analytics.models.AnalyticsEvent
import com.tangem.core.analytics.models.AnalyticsParam
import com.tangem.core.analytics.models.Basic
import com.tangem.core.analytics.models.ExceptionAnalyticsEvent
import com.tangem.core.decompose.ui.UiMessageSender
import com.tangem.core.navigation.finisher.AppFinisher
import com.tangem.core.res.getStringSafe import com.tangem.core.res.getStringSafe
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.message.DialogMessage
import com.tangem.core.ui.message.EventMessageAction
import com.tangem.crypto.bip39.DefaultMnemonic import com.tangem.crypto.bip39.DefaultMnemonic
import com.tangem.crypto.hdWallet.DerivationPath import com.tangem.crypto.hdWallet.DerivationPath
import com.tangem.crypto.hdWallet.bip32.ExtendedPublicKey import com.tangem.crypto.hdWallet.bip32.ExtendedPublicKey
import com.tangem.domain.card.common.util.cardTypesResolver import com.tangem.domain.card.common.util.cardTypesResolver
import com.tangem.domain.card.repository.CardSdkConfigRepository import com.tangem.domain.card.repository.CardSdkConfigRepository
import com.tangem.utils.coroutines.AppCoroutineScope
import com.tangem.domain.feedback.SendFeedbackEmailUseCase
import com.tangem.domain.feedback.models.FeedbackEmailType
import com.tangem.domain.models.scan.CardDTO import com.tangem.domain.models.scan.CardDTO
import com.tangem.domain.models.scan.ScanResponse import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.models.wallet.UserWalletId import com.tangem.domain.models.wallet.UserWalletId
@ -67,9 +54,10 @@ import com.tangem.tap.domain.twins.CreateSecondTwinWalletTask
import com.tangem.tap.domain.twins.FinalizeTwinTask import com.tangem.tap.domain.twins.FinalizeTwinTask
import com.tangem.tap.domain.visa.VisaCardScanHandler import com.tangem.tap.domain.visa.VisaCardScanHandler
import com.tangem.wallet.R import com.tangem.wallet.R
import kotlinx.coroutines.* import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.sync.withLock import kotlinx.coroutines.suspendCancellableCoroutine
import kotlinx.coroutines.withContext
import kotlin.coroutines.resume import kotlin.coroutines.resume
@Suppress("TooManyFunctions", "LargeClass", "LongParameterList") @Suppress("TooManyFunctions", "LargeClass", "LongParameterList")
@ -80,17 +68,10 @@ internal class DefaultTangemSdkManager(
private val visaCardActivationTaskFactory: VisaCardActivationTask.Factory, private val visaCardActivationTaskFactory: VisaCardActivationTask.Factory,
private val tangemPayChallengeTaskFactory: TangemPayGenerateAddressAndSignChallengeTask.Factory, private val tangemPayChallengeTaskFactory: TangemPayGenerateAddressAndSignChallengeTask.Factory,
private val onboardingV2FeatureToggles: OnboardingV2FeatureToggles, private val onboardingV2FeatureToggles: OnboardingV2FeatureToggles,
private val uiMessageSender: UiMessageSender,
private val appFinisher: AppFinisher,
private val sendFeedbackEmailUseCase: SendFeedbackEmailUseCase,
private val analyticsExceptionHandler: AnalyticsExceptionHandler,
private val blockchainToDeriveFinder: BlockchainToDeriveFinder, private val blockchainToDeriveFinder: BlockchainToDeriveFinder,
private val analyticsEventHandler: AnalyticsEventHandler, private val analyticsErrorHandler: AnalyticsErrorHandler,
private val coroutineScope: AppCoroutineScope,
) : TangemSdkManager { ) : TangemSdkManager {
private val awaitInitializationMutex = Mutex()
private val tangemSdk: TangemSdk private val tangemSdk: TangemSdk
get() = cardSdkConfigRepository.sdk get() = cardSdkConfigRepository.sdk
@ -101,10 +82,32 @@ internal class DefaultTangemSdkManager(
) )
} }
override val needEnrollBiometrics: Boolean override val needEnrollBiometrics: Boolean
get() = tangemSdk.authenticationManager.needEnrollBiometrics get() {
val isNeedEnrollBiometrics = tangemSdk.authenticationManager.needEnrollBiometrics
if (isNeedEnrollBiometrics) {
analyticsErrorHandler.sendErrorEvent(
AnalyticsEvent(
category = "TangemSdkManager",
event = "needEnrollBiometrics",
),
)
}
return isNeedEnrollBiometrics
}
override val canUseBiometry: Boolean override val canUseBiometry: Boolean
get() = tangemSdk.authenticationManager.canAuthenticate || needEnrollBiometrics get() {
val isCanUseBiometry = tangemSdk.authenticationManager.canAuthenticate || needEnrollBiometrics
if (!isCanUseBiometry) {
analyticsErrorHandler.sendErrorEvent(
AnalyticsEvent(
category = "TangemSdkManager",
event = "cantUseBiometry",
),
)
}
return isCanUseBiometry
}
override val keystoreManager: KeystoreManager override val keystoreManager: KeystoreManager
get() = tangemSdk.keystoreManager get() = tangemSdk.keystoreManager
@ -116,49 +119,11 @@ internal class DefaultTangemSdkManager(
get() = tangemSdk.config.userCodeRequestPolicy get() = tangemSdk.config.userCodeRequestPolicy
override suspend fun checkNeedEnrollBiometrics(awaitInitialization: Boolean): Boolean { override suspend fun checkNeedEnrollBiometrics(awaitInitialization: Boolean): Boolean {
return try { return needEnrollBiometrics
needEnrollBiometrics
} catch (e: TangemSdkError.AuthenticationNotInitialized) {
Log.error {
"Trying to access `needEnrollBiometrics` flag when authentication manager is not initialized: " +
if (awaitInitialization) "awaiting initialization" else "failing"
}
if (awaitInitialization) {
val manager = awaitAuthenticationManagerInitialization()
if (manager.isInitialized) {
manager.needEnrollBiometrics
} else {
false
}
} else {
throw e
}
}
} }
override suspend fun checkCanUseBiometry(awaitInitialization: Boolean): Boolean { override suspend fun checkCanUseBiometry(awaitInitialization: Boolean): Boolean {
return try { return canUseBiometry
canUseBiometry
} catch (e: TangemSdkError.AuthenticationNotInitialized) {
Log.error {
"Trying to access `canUseBiometry` flag when authentication manager is not initialized: " +
if (awaitInitialization) "awaiting initialization" else "failing"
}
if (awaitInitialization) {
val manager = awaitAuthenticationManagerInitialization()
if (manager.isInitialized) {
manager.canAuthenticate || manager.needEnrollBiometrics
} else {
false
}
} else {
throw e
}
}
} }
override suspend fun scanProduct( override suspend fun scanProduct(
@ -432,59 +397,6 @@ internal class DefaultTangemSdkManager(
tangemSdk.config.userCodeRequestPolicy = policy tangemSdk.config.userCodeRequestPolicy = policy
} }
private suspend fun awaitAuthenticationManagerInitialization(): AuthenticationManager {
return awaitInitializationMutex.withLock {
var attemps = 0
do {
if (tangemSdk.authenticationManager.isInitialized) {
break
} else {
if (attemps++ >= MAX_INITIALIZE_ATTEMPTS) {
analyticsExceptionHandler.sendException(
ExceptionAnalyticsEvent(
exception = IllegalStateException(
"Can't initialize authentication manager after $MAX_INITIALIZE_ATTEMPTS attempts",
),
),
)
showAlert()
break
} else {
delay(timeMillis = 400)
}
}
} while (true)
tangemSdk.authenticationManager
}
}
private fun showAlert() {
uiMessageSender.send(
message = DialogMessage(
message = resourceReference(id = R.string.alert_authentication_error_message),
title = resourceReference(id = R.string.alert_authentication_error_title),
isDismissable = false,
dismissOnFirstAction = false,
firstActionBuilder = {
EventMessageAction(
title = resourceReference(R.string.alert_button_request_support),
onClick = {
coroutineScope.launch {
analyticsEventHandler.send(
Basic.ButtonSupport(source = AnalyticsParam.ScreensSources.SignIn),
)
sendFeedbackEmailUseCase(FeedbackEmailType.BiometricsAuthenticationFailed)
}
},
)
},
secondActionBuilder = { cancelAction(onClick = appFinisher::finish) },
),
)
}
// region Twin-specific // region Twin-specific
override suspend fun createFirstTwinWallet( override suspend fun createFirstTwinWallet(
@ -624,8 +536,4 @@ internal class DefaultTangemSdkManager(
} }
} }
// endregion // endregion
companion object {
private const val MAX_INITIALIZE_ATTEMPTS = 10
}
} }

View file

@ -229,7 +229,6 @@ class DefaultHotWalletAccessor @Inject constructor(
this is TangemSdkError.AuthenticationLockout || this is TangemSdkError.AuthenticationLockout ||
this is TangemSdkError.AuthenticationUnavailable || this is TangemSdkError.AuthenticationUnavailable ||
this is TangemSdkError.AuthenticationAlreadyInProgress || this is TangemSdkError.AuthenticationAlreadyInProgress ||
this is TangemSdkError.AuthenticationNotInitialized ||
this is TangemSdkError.AuthenticationPermanentLockout this is TangemSdkError.AuthenticationPermanentLockout
} }

View file

@ -5,13 +5,13 @@
# https://github.com/tangem/tangem-sdk-android/ # https://github.com/tangem/tangem-sdk-android/
# https://github.com/tangem/vico # https://github.com/tangem/vico
tangemBlockchainSdk = "develop-1447" tangemBlockchainSdk = "develop-1454"
#tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds #tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds
tangemCardSdk = "develop-582" tangemCardSdk = "develop-598"
#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 ^
tangemHotSdk = "develop-547" tangemHotSdk = "develop-549"
#tangemHotSdk = "0.0.1" # Keep it! - used for local builds ^ #tangemHotSdk = "0.0.1" # Keep it! - used for local builds ^