Updated on 2026-08-14

This commit is contained in:
Tangem 2026-01-30 13:59:48 +04:00
parent 343cf1ed20
commit bbc40d7ddf
14 changed files with 147 additions and 15 deletions

View file

@ -1,8 +1,13 @@
package com.tangem.tap.di
import android.content.Context
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.repository.CardSdkConfigRepository
import com.tangem.domain.feedback.SendFeedbackEmailUseCase
import com.tangem.features.onboarding.v2.OnboardingV2FeatureToggles
import com.tangem.sdk.api.TangemSdkManager
import com.tangem.tap.domain.sdk.impl.DefaultTangemSdkManager
@ -10,6 +15,7 @@ import com.tangem.tap.domain.sdk.impl.MockTangemSdkManager
import com.tangem.tap.domain.tasks.visa.TangemPayGenerateAddressAndSignChallengeTask
import com.tangem.tap.domain.tasks.visa.VisaCardActivationTask
import com.tangem.tap.domain.visa.VisaCardScanHandler
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
@ -30,6 +36,11 @@ internal class TangemSdkManagerModule {
visaCardActivationTaskFactory: VisaCardActivationTask.Factory,
tangemPayChallengeTaskFactory: TangemPayGenerateAddressAndSignChallengeTask.Factory,
onboardingV2FeatureToggles: OnboardingV2FeatureToggles,
@GlobalUiMessageSender uiMessageSender: UiMessageSender,
appFinisher: AppFinisher,
sendFeedbackEmailUseCase: SendFeedbackEmailUseCase,
analyticsExceptionHandler: AnalyticsExceptionHandler,
dispatchers: CoroutineDispatcherProvider,
): TangemSdkManager {
return if (BuildConfig.MOCK_DATA_SOURCE) {
MockTangemSdkManager(resources = context.resources)
@ -41,6 +52,11 @@ internal class TangemSdkManagerModule {
visaCardActivationTaskFactory = visaCardActivationTaskFactory,
tangemPayChallengeTaskFactory = tangemPayChallengeTaskFactory,
onboardingV2FeatureToggles = onboardingV2FeatureToggles,
uiMessageSender = uiMessageSender,
appFinisher = appFinisher,
sendFeedbackEmailUseCase = sendFeedbackEmailUseCase,
analyticsExceptionHandler = analyticsExceptionHandler,
dispatchers = dispatchers,
)
}
}

View file

@ -18,12 +18,21 @@ import com.tangem.common.extensions.hexToBytes
import com.tangem.common.services.secure.SecureStorage
import com.tangem.common.usersCode.UserCodeRepository
import com.tangem.core.analytics.Analytics
import com.tangem.core.analytics.api.AnalyticsExceptionHandler
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.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.hdWallet.DerivationPath
import com.tangem.crypto.hdWallet.bip32.ExtendedPublicKey
import com.tangem.domain.card.common.util.cardTypesResolver
import com.tangem.domain.card.repository.CardSdkConfigRepository
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.ScanResponse
import com.tangem.domain.models.wallet.UserWalletId
@ -57,13 +66,14 @@ import com.tangem.tap.domain.twins.CreateFirstTwinWalletTask
import com.tangem.tap.domain.twins.CreateSecondTwinWalletTask
import com.tangem.tap.domain.twins.FinalizeTwinTask
import com.tangem.tap.domain.visa.VisaCardScanHandler
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import com.tangem.wallet.R
import kotlinx.coroutines.*
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlin.coroutines.resume
@Suppress("TooManyFunctions", "LargeClass")
@Suppress("TooManyFunctions", "LargeClass", "LongParameterList")
internal class DefaultTangemSdkManager(
private val cardSdkConfigRepository: CardSdkConfigRepository,
private val resources: Resources,
@ -71,6 +81,11 @@ internal class DefaultTangemSdkManager(
private val visaCardActivationTaskFactory: VisaCardActivationTask.Factory,
private val tangemPayChallengeTaskFactory: TangemPayGenerateAddressAndSignChallengeTask.Factory,
private val onboardingV2FeatureToggles: OnboardingV2FeatureToggles,
private val uiMessageSender: UiMessageSender,
private val appFinisher: AppFinisher,
private val sendFeedbackEmailUseCase: SendFeedbackEmailUseCase,
private val analyticsExceptionHandler: AnalyticsExceptionHandler,
dispatchers: CoroutineDispatcherProvider,
) : TangemSdkManager {
private val awaitInitializationMutex = Mutex()
@ -99,6 +114,8 @@ internal class DefaultTangemSdkManager(
override val userCodeRequestPolicy: UserCodeRequestPolicy
get() = tangemSdk.config.userCodeRequestPolicy
private val coroutineScope = CoroutineScope(SupervisorJob() + dispatchers.io)
override suspend fun checkNeedEnrollBiometrics(awaitInitialization: Boolean): Boolean {
return try {
needEnrollBiometrics
@ -413,9 +430,16 @@ internal class DefaultTangemSdkManager(
break
} else {
if (attemps++ >= MAX_INITIALIZE_ATTEMPTS) {
error("Can't initialize authentication manager after $MAX_INITIALIZE_ATTEMPTS attempts")
analyticsExceptionHandler.sendException(
ExceptionAnalyticsEvent(
exception = IllegalStateException(
"Can't initialize authentication manager after $MAX_INITIALIZE_ATTEMPTS attempts",
),
),
)
showAlert()
} else {
delay(timeMillis = 200)
delay(timeMillis = 400)
}
}
} while (true)
@ -424,6 +448,28 @@ internal class DefaultTangemSdkManager(
}
}
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 {
sendFeedbackEmailUseCase(FeedbackEmailType.BiometricsAuthenticationFailed)
}
},
)
},
secondActionBuilder = { cancelAction(onClick = appFinisher::finish) },
),
)
}
// region Twin-specific
override suspend fun createFirstTwinWallet(