Updated on 2026-08-14
This commit is contained in:
parent
423e125974
commit
3e5bf1d8c3
12 changed files with 505 additions and 212 deletions
|
|
@ -219,6 +219,7 @@ dependencies {
|
|||
|
||||
/** Chucker */
|
||||
debugImplementation(deps.chucker)
|
||||
mockedImplementation(deps.chuckerStub)
|
||||
externalImplementation(deps.chuckerStub)
|
||||
internalImplementation(deps.chuckerStub)
|
||||
releaseImplementation(deps.chuckerStub)
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
Subproject commit 82f4a8aebcab924047705ad3ecd5f008f07ca0b5
|
||||
Subproject commit b0985600722953fae0a93bcbbc2a83930b9e0cbe
|
||||
|
|
@ -1,9 +1,12 @@
|
|||
package com.tangem.tap.di
|
||||
|
||||
import android.content.Context
|
||||
import com.tangem.domain.card.BuildConfig
|
||||
import com.tangem.domain.card.ScanCardUseCase
|
||||
import com.tangem.domain.card.repository.CardSdkConfigRepository
|
||||
import com.tangem.domain.exchange.RampStateManager
|
||||
import com.tangem.tap.domain.DefaultTangemSdkManager
|
||||
import com.tangem.tap.domain.MockTangemSdkManager
|
||||
import com.tangem.tap.domain.TangemSdkManager
|
||||
import com.tangem.tap.domain.scanCard.repository.DefaultScanCardRepository
|
||||
import com.tangem.tap.network.exchangeServices.DefaultRampManager
|
||||
|
|
@ -28,7 +31,11 @@ internal object ActivityModule {
|
|||
@ApplicationContext context: Context,
|
||||
cardSdkConfigRepository: CardSdkConfigRepository,
|
||||
): TangemSdkManager {
|
||||
return TangemSdkManager(cardSdkConfigRepository = cardSdkConfigRepository, resources = context.resources)
|
||||
return if (BuildConfig.MOCK_DATA_SOURCE) {
|
||||
MockTangemSdkManager(resources = context.resources)
|
||||
} else {
|
||||
DefaultTangemSdkManager(cardSdkConfigRepository = cardSdkConfigRepository, resources = context.resources)
|
||||
}
|
||||
}
|
||||
|
||||
@Provides
|
||||
|
|
|
|||
|
|
@ -0,0 +1,290 @@
|
|||
package com.tangem.tap.domain
|
||||
|
||||
import android.content.res.Resources
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.annotation.StringRes
|
||||
import com.tangem.Message
|
||||
import com.tangem.TangemSdk
|
||||
import com.tangem.common.*
|
||||
import com.tangem.common.authentication.keystore.KeystoreManager
|
||||
import com.tangem.common.card.FirmwareVersion
|
||||
import com.tangem.common.core.*
|
||||
import com.tangem.common.extensions.ByteArrayKey
|
||||
import com.tangem.common.services.secure.SecureStorage
|
||||
import com.tangem.common.usersCode.UserCodeRepository
|
||||
import com.tangem.core.analytics.Analytics
|
||||
import com.tangem.core.analytics.models.Basic
|
||||
import com.tangem.crypto.bip39.DefaultMnemonic
|
||||
import com.tangem.crypto.hdWallet.DerivationPath
|
||||
import com.tangem.domain.card.repository.CardSdkConfigRepository
|
||||
import com.tangem.domain.common.util.cardTypesResolver
|
||||
import com.tangem.domain.common.util.derivationStyleProvider
|
||||
import com.tangem.domain.models.scan.CardDTO
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.operations.ScanTask
|
||||
import com.tangem.operations.derivation.DerivationTaskResponse
|
||||
import com.tangem.operations.derivation.DeriveMultipleWalletPublicKeysTask
|
||||
import com.tangem.operations.pins.SetUserCodeCommand
|
||||
import com.tangem.operations.usersetttings.SetUserCodeRecoveryAllowedTask
|
||||
import com.tangem.crypto.hdWallet.bip32.ExtendedPublicKey
|
||||
import com.tangem.operations.derivation.DeriveWalletPublicKeyTask
|
||||
import com.tangem.tap.derivationsFinder
|
||||
import com.tangem.tap.domain.tasks.product.CreateProductWalletTask
|
||||
import com.tangem.tap.domain.tasks.product.CreateProductWalletTaskResponse
|
||||
import com.tangem.tap.domain.tasks.product.ResetToFactorySettingsTask
|
||||
import com.tangem.tap.domain.tasks.product.ScanProductTask
|
||||
import com.tangem.wallet.R
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlin.coroutines.resume
|
||||
|
||||
@Suppress("TooManyFunctions")
|
||||
class DefaultTangemSdkManager(
|
||||
private val cardSdkConfigRepository: CardSdkConfigRepository,
|
||||
private val resources: Resources,
|
||||
) : TangemSdkManager {
|
||||
|
||||
private val tangemSdk: TangemSdk
|
||||
get() = cardSdkConfigRepository.sdk
|
||||
|
||||
private val userCodeRepository by lazy {
|
||||
UserCodeRepository(
|
||||
keystoreManager = tangemSdk.keystoreManager,
|
||||
secureStorage = tangemSdk.secureStorage,
|
||||
)
|
||||
}
|
||||
|
||||
override val canUseBiometry: Boolean
|
||||
get() = tangemSdk.authenticationManager.canAuthenticate || needEnrollBiometrics
|
||||
|
||||
override val needEnrollBiometrics: Boolean
|
||||
get() = tangemSdk.authenticationManager.needEnrollBiometrics
|
||||
|
||||
override val keystoreManager: KeystoreManager
|
||||
get() = tangemSdk.keystoreManager
|
||||
|
||||
override val secureStorage: SecureStorage
|
||||
get() = tangemSdk.secureStorage
|
||||
|
||||
override val userCodeRequestPolicy: UserCodeRequestPolicy
|
||||
get() = tangemSdk.config.userCodeRequestPolicy
|
||||
|
||||
override suspend fun scanProduct(
|
||||
cardId: String?,
|
||||
messageRes: Int?,
|
||||
allowsRequestAccessCodeFromRepository: Boolean,
|
||||
): CompletionResult<ScanResponse> {
|
||||
val message = Message(resources.getString(messageRes ?: R.string.initial_message_scan_header))
|
||||
return runTaskAsyncReturnOnMain(
|
||||
runnable = ScanProductTask(
|
||||
card = null,
|
||||
derivationsFinder = derivationsFinder,
|
||||
allowsRequestAccessCodeFromRepository = allowsRequestAccessCodeFromRepository,
|
||||
),
|
||||
cardId = cardId,
|
||||
initialMessage = message,
|
||||
).also { sendScanResultsToAnalytics(it) }
|
||||
}
|
||||
|
||||
override suspend fun createProductWallet(
|
||||
scanResponse: ScanResponse,
|
||||
shouldReset: Boolean,
|
||||
): CompletionResult<CreateProductWalletTaskResponse> {
|
||||
return runTaskAsync(
|
||||
runnable = CreateProductWalletTask(
|
||||
cardTypesResolver = scanResponse.cardTypesResolver,
|
||||
derivationStyleProvider = scanResponse.derivationStyleProvider,
|
||||
shouldReset = shouldReset,
|
||||
),
|
||||
cardId = scanResponse.card.cardId,
|
||||
initialMessage = Message(resources.getString(R.string.initial_message_create_wallet_body)),
|
||||
iconScanRes = if (scanResponse.cardTypesResolver.isRing()) R.drawable.img_hand_scan_ring else null,
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun importWallet(
|
||||
scanResponse: ScanResponse,
|
||||
mnemonic: String,
|
||||
passphrase: String?,
|
||||
shouldReset: Boolean,
|
||||
): CompletionResult<CreateProductWalletTaskResponse> {
|
||||
val defaultMnemonic = try {
|
||||
DefaultMnemonic(mnemonic, tangemSdk.wordlist)
|
||||
} catch (e: TangemSdkError.MnemonicException) {
|
||||
return CompletionResult.Failure(e)
|
||||
}
|
||||
return runTaskAsync(
|
||||
CreateProductWalletTask(
|
||||
cardTypesResolver = scanResponse.cardTypesResolver,
|
||||
derivationStyleProvider = scanResponse.derivationStyleProvider,
|
||||
mnemonic = defaultMnemonic,
|
||||
passphrase = passphrase,
|
||||
shouldReset = shouldReset,
|
||||
),
|
||||
scanResponse.card.cardId,
|
||||
Message(resources.getString(R.string.initial_message_create_wallet_body)),
|
||||
)
|
||||
}
|
||||
|
||||
private fun sendScanResultsToAnalytics(result: CompletionResult<ScanResponse>) {
|
||||
if (result is CompletionResult.Failure) {
|
||||
(result.error as? TangemSdkError)?.let { error ->
|
||||
Analytics.send(Basic.ScanError(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun derivePublicKeys(
|
||||
cardId: String?,
|
||||
derivations: Map<ByteArrayKey, List<DerivationPath>>,
|
||||
): CompletionResult<DerivationTaskResponse> {
|
||||
return runTaskAsyncReturnOnMain(DeriveMultipleWalletPublicKeysTask(derivations), cardId)
|
||||
}
|
||||
|
||||
override suspend fun deriveExtendedPublicKey(
|
||||
cardId: String?,
|
||||
walletPublicKey: ByteArray,
|
||||
derivation: DerivationPath,
|
||||
): CompletionResult<ExtendedPublicKey> = withContext(Dispatchers.Main) {
|
||||
runTaskAsyncReturnOnMain(
|
||||
DeriveWalletPublicKeyTask(walletPublicKey, derivation),
|
||||
cardId,
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun resetToFactorySettings(
|
||||
cardId: String,
|
||||
allowsRequestAccessCodeFromRepository: Boolean,
|
||||
): CompletionResult<CardDTO> {
|
||||
return runTaskAsyncReturnOnMain(
|
||||
runnable = ResetToFactorySettingsTask(
|
||||
allowsRequestAccessCodeFromRepository = allowsRequestAccessCodeFromRepository,
|
||||
),
|
||||
cardId = cardId,
|
||||
initialMessage = Message(resources.getString(R.string.card_settings_reset_card_to_factory)),
|
||||
)
|
||||
.map { CardDTO(it) }
|
||||
}
|
||||
|
||||
override suspend fun saveAccessCode(accessCode: String, cardsIds: Set<String>): CompletionResult<Unit> {
|
||||
return userCodeRepository.save(
|
||||
cardsIds = cardsIds,
|
||||
userCode = UserCode(
|
||||
type = UserCodeType.AccessCode,
|
||||
stringValue = accessCode,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun deleteSavedUserCodes(cardsIds: Set<String>): CompletionResult<Unit> {
|
||||
return userCodeRepository.delete(cardsIds.toSet())
|
||||
}
|
||||
|
||||
override suspend fun clearSavedUserCodes(): CompletionResult<Unit> {
|
||||
return userCodeRepository.clear()
|
||||
}
|
||||
|
||||
override suspend fun setPasscode(cardId: String?): CompletionResult<SuccessResponse> {
|
||||
return runTaskAsyncReturnOnMain(
|
||||
SetUserCodeCommand.changePasscode(null),
|
||||
cardId,
|
||||
initialMessage = Message(resources.getString(R.string.initial_message_change_passcode_body)),
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun setAccessCode(cardId: String?): CompletionResult<SuccessResponse> {
|
||||
return runTaskAsyncReturnOnMain(
|
||||
SetUserCodeCommand.changeAccessCode(null),
|
||||
cardId,
|
||||
initialMessage = Message(resources.getString(R.string.initial_message_change_access_code_body)),
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun setLongTap(cardId: String?): CompletionResult<SuccessResponse> {
|
||||
return runTaskAsyncReturnOnMain(
|
||||
SetUserCodeCommand.resetUserCodes(),
|
||||
cardId,
|
||||
initialMessage = Message(resources.getString(R.string.initial_message_tap_header)),
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun setAccessCodeRecoveryEnabled(
|
||||
cardId: String?,
|
||||
enabled: Boolean,
|
||||
): CompletionResult<SuccessResponse> {
|
||||
return runTaskAsyncReturnOnMain(
|
||||
SetUserCodeRecoveryAllowedTask(enabled),
|
||||
cardId,
|
||||
initialMessage = Message(resources.getString(R.string.initial_message_tap_header)),
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun scanCard(
|
||||
cardId: String?,
|
||||
allowRequestAccessCodeFromRepository: Boolean,
|
||||
): CompletionResult<CardDTO> {
|
||||
return runTaskAsyncReturnOnMain(
|
||||
runnable = ScanTask(allowRequestAccessCodeFromRepository),
|
||||
cardId = cardId,
|
||||
initialMessage = Message(resources.getString(R.string.initial_message_tap_header)),
|
||||
)
|
||||
.map { CardDTO(it) }
|
||||
}
|
||||
|
||||
override suspend fun <T> runTaskAsync(
|
||||
runnable: CardSessionRunnable<T>,
|
||||
cardId: String?,
|
||||
initialMessage: Message?,
|
||||
accessCode: String?,
|
||||
@DrawableRes iconScanRes: Int?,
|
||||
): CompletionResult<T> = withContext(Dispatchers.Main) {
|
||||
suspendCancellableCoroutine { continuation ->
|
||||
tangemSdk.startSessionWithRunnable(runnable, cardId, initialMessage, accessCode, iconScanRes) { result ->
|
||||
if (continuation.isActive) continuation.resume(result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun <T> runTaskAsyncReturnOnMain(
|
||||
runnable: CardSessionRunnable<T>,
|
||||
cardId: String? = null,
|
||||
initialMessage: Message? = null,
|
||||
): CompletionResult<T> {
|
||||
val result = runTaskAsync(runnable, cardId, initialMessage)
|
||||
return withContext(Dispatchers.Main) { result }
|
||||
}
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
override fun changeDisplayedCardIdNumbersCount(scanResponse: ScanResponse?) {
|
||||
tangemSdk.config.cardIdDisplayFormat = when {
|
||||
scanResponse == null -> CardIdDisplayFormat.Full
|
||||
scanResponse.cardTypesResolver.isTangemTwins() -> CardIdDisplayFormat.LastLuhn(4)
|
||||
else -> CardIdDisplayFormat.Full
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated("TangemSdkManager shouldn't returns a string from resources")
|
||||
override fun getString(@StringRes stringResId: Int, vararg formatArgs: Any?): String {
|
||||
return resources.getString(stringResId, *formatArgs)
|
||||
}
|
||||
|
||||
override fun setUserCodeRequestPolicy(policy: UserCodeRequestPolicy) {
|
||||
tangemSdk.config.userCodeRequestPolicy = policy
|
||||
}
|
||||
|
||||
companion object {
|
||||
@Deprecated("Use [DefaultCardSdkProvider] instead")
|
||||
val config = Config(
|
||||
linkedTerminal = true,
|
||||
allowUntrustedCards = true,
|
||||
filter = CardFilter(
|
||||
allowedCardTypes = FirmwareVersion.FirmwareType.values().toList(),
|
||||
maxFirmwareVersion = FirmwareVersion(major = 6, minor = 33),
|
||||
batchIdFilter = CardFilter.Companion.ItemFilter.Deny(
|
||||
items = setOf("0027", "0030", "0031", "0035"),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
148
app/src/main/java/com/tangem/tap/domain/MockTangemSdkManager.kt
Normal file
148
app/src/main/java/com/tangem/tap/domain/MockTangemSdkManager.kt
Normal file
|
|
@ -0,0 +1,148 @@
|
|||
package com.tangem.tap.domain
|
||||
|
||||
import android.content.res.Resources
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.annotation.StringRes
|
||||
import com.tangem.Message
|
||||
import com.tangem.common.*
|
||||
import com.tangem.common.authentication.keystore.KeystoreManager
|
||||
import com.tangem.common.core.*
|
||||
import com.tangem.common.extensions.ByteArrayKey
|
||||
import com.tangem.common.services.secure.SecureStorage
|
||||
import com.tangem.crypto.hdWallet.DerivationPath
|
||||
import com.tangem.domain.models.scan.CardDTO
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.operations.derivation.DerivationTaskResponse
|
||||
import com.tangem.crypto.hdWallet.bip32.ExtendedPublicKey
|
||||
import com.tangem.tap.domain.tasks.product.CreateProductWalletTaskResponse
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
@Suppress("TooManyFunctions")
|
||||
class MockTangemSdkManager(
|
||||
private val resources: Resources,
|
||||
) : TangemSdkManager {
|
||||
|
||||
override val canUseBiometry: Boolean
|
||||
get() = TODO()
|
||||
|
||||
override val needEnrollBiometrics: Boolean
|
||||
get() = TODO()
|
||||
|
||||
override val keystoreManager: KeystoreManager
|
||||
get() = TODO()
|
||||
|
||||
override val secureStorage: SecureStorage
|
||||
get() = TODO()
|
||||
|
||||
override val userCodeRequestPolicy: UserCodeRequestPolicy
|
||||
get() = TODO()
|
||||
|
||||
override suspend fun scanProduct(
|
||||
cardId: String?,
|
||||
messageRes: Int?,
|
||||
allowsRequestAccessCodeFromRepository: Boolean,
|
||||
): CompletionResult<ScanResponse> {
|
||||
TODO()
|
||||
}
|
||||
|
||||
override suspend fun createProductWallet(
|
||||
scanResponse: ScanResponse,
|
||||
shouldReset: Boolean,
|
||||
): CompletionResult<CreateProductWalletTaskResponse> {
|
||||
TODO()
|
||||
}
|
||||
|
||||
override suspend fun importWallet(
|
||||
scanResponse: ScanResponse,
|
||||
mnemonic: String,
|
||||
passphrase: String?,
|
||||
shouldReset: Boolean,
|
||||
): CompletionResult<CreateProductWalletTaskResponse> {
|
||||
TODO()
|
||||
}
|
||||
|
||||
override suspend fun derivePublicKeys(
|
||||
cardId: String?,
|
||||
derivations: Map<ByteArrayKey, List<DerivationPath>>,
|
||||
): CompletionResult<DerivationTaskResponse> {
|
||||
TODO()
|
||||
}
|
||||
|
||||
override suspend fun deriveExtendedPublicKey(
|
||||
cardId: String?,
|
||||
walletPublicKey: ByteArray,
|
||||
derivation: DerivationPath,
|
||||
): CompletionResult<ExtendedPublicKey> {
|
||||
TODO()
|
||||
}
|
||||
|
||||
override suspend fun resetToFactorySettings(
|
||||
cardId: String,
|
||||
allowsRequestAccessCodeFromRepository: Boolean,
|
||||
): CompletionResult<CardDTO> {
|
||||
TODO()
|
||||
}
|
||||
|
||||
override suspend fun saveAccessCode(accessCode: String, cardsIds: Set<String>): CompletionResult<Unit> {
|
||||
TODO()
|
||||
}
|
||||
|
||||
override suspend fun deleteSavedUserCodes(cardsIds: Set<String>): CompletionResult<Unit> {
|
||||
TODO()
|
||||
}
|
||||
|
||||
override suspend fun clearSavedUserCodes(): CompletionResult<Unit> {
|
||||
TODO()
|
||||
}
|
||||
|
||||
override suspend fun setPasscode(cardId: String?): CompletionResult<SuccessResponse> {
|
||||
TODO()
|
||||
}
|
||||
|
||||
override suspend fun setAccessCode(cardId: String?): CompletionResult<SuccessResponse> {
|
||||
TODO()
|
||||
}
|
||||
|
||||
override suspend fun setLongTap(cardId: String?): CompletionResult<SuccessResponse> {
|
||||
TODO()
|
||||
}
|
||||
|
||||
override suspend fun setAccessCodeRecoveryEnabled(
|
||||
cardId: String?,
|
||||
enabled: Boolean,
|
||||
): CompletionResult<SuccessResponse> {
|
||||
TODO()
|
||||
}
|
||||
|
||||
override suspend fun scanCard(
|
||||
cardId: String?,
|
||||
allowRequestAccessCodeFromRepository: Boolean,
|
||||
): CompletionResult<CardDTO> {
|
||||
TODO()
|
||||
}
|
||||
|
||||
override suspend fun <T> runTaskAsync(
|
||||
runnable: CardSessionRunnable<T>,
|
||||
cardId: String?,
|
||||
initialMessage: Message?,
|
||||
accessCode: String?,
|
||||
@DrawableRes iconScanRes: Int?,
|
||||
): CompletionResult<T> = withContext(Dispatchers.Main) {
|
||||
TODO()
|
||||
}
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
override fun changeDisplayedCardIdNumbersCount(scanResponse: ScanResponse?) {
|
||||
TODO()
|
||||
}
|
||||
|
||||
@Deprecated("TangemSdkManager shouldn't returns a string from resources")
|
||||
override fun getString(@StringRes stringResId: Int, vararg formatArgs: Any?): String {
|
||||
return resources.getString(stringResId, *formatArgs)
|
||||
}
|
||||
|
||||
override fun setUserCodeRequestPolicy(policy: UserCodeRequestPolicy) {
|
||||
TODO()
|
||||
}
|
||||
}
|
||||
|
|
@ -1,233 +1,84 @@
|
|||
package com.tangem.tap.domain
|
||||
|
||||
import android.content.res.Resources
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.annotation.StringRes
|
||||
import com.tangem.Message
|
||||
import com.tangem.TangemSdk
|
||||
import com.tangem.common.*
|
||||
import com.tangem.common.authentication.keystore.KeystoreManager
|
||||
import com.tangem.common.card.FirmwareVersion
|
||||
import com.tangem.common.core.*
|
||||
import com.tangem.common.extensions.ByteArrayKey
|
||||
import com.tangem.common.services.secure.SecureStorage
|
||||
import com.tangem.common.usersCode.UserCodeRepository
|
||||
import com.tangem.core.analytics.Analytics
|
||||
import com.tangem.core.analytics.models.Basic
|
||||
import com.tangem.crypto.bip39.DefaultMnemonic
|
||||
import com.tangem.crypto.hdWallet.DerivationPath
|
||||
import com.tangem.domain.card.repository.CardSdkConfigRepository
|
||||
import com.tangem.domain.common.util.cardTypesResolver
|
||||
import com.tangem.domain.common.util.derivationStyleProvider
|
||||
import com.tangem.domain.models.scan.CardDTO
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.operations.ScanTask
|
||||
import com.tangem.operations.derivation.DerivationTaskResponse
|
||||
import com.tangem.operations.derivation.DeriveMultipleWalletPublicKeysTask
|
||||
import com.tangem.operations.pins.SetUserCodeCommand
|
||||
import com.tangem.operations.usersetttings.SetUserCodeRecoveryAllowedTask
|
||||
import com.tangem.crypto.hdWallet.bip32.ExtendedPublicKey
|
||||
import com.tangem.operations.derivation.DeriveWalletPublicKeyTask
|
||||
import com.tangem.tap.derivationsFinder
|
||||
import com.tangem.tap.domain.tasks.product.CreateProductWalletTask
|
||||
import com.tangem.tap.domain.tasks.product.CreateProductWalletTaskResponse
|
||||
import com.tangem.tap.domain.tasks.product.ResetToFactorySettingsTask
|
||||
import com.tangem.tap.domain.tasks.product.ScanProductTask
|
||||
import com.tangem.wallet.R
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlin.coroutines.resume
|
||||
|
||||
@Suppress("TooManyFunctions")
|
||||
class TangemSdkManager(
|
||||
private val cardSdkConfigRepository: CardSdkConfigRepository,
|
||||
private val resources: Resources,
|
||||
) {
|
||||
|
||||
private val tangemSdk: TangemSdk
|
||||
get() = cardSdkConfigRepository.sdk
|
||||
|
||||
private val userCodeRepository by lazy {
|
||||
UserCodeRepository(
|
||||
keystoreManager = tangemSdk.keystoreManager,
|
||||
secureStorage = tangemSdk.secureStorage,
|
||||
)
|
||||
}
|
||||
interface TangemSdkManager {
|
||||
|
||||
val canUseBiometry: Boolean
|
||||
get() = tangemSdk.authenticationManager.canAuthenticate || needEnrollBiometrics
|
||||
|
||||
val needEnrollBiometrics: Boolean
|
||||
get() = tangemSdk.authenticationManager.needEnrollBiometrics
|
||||
|
||||
val keystoreManager: KeystoreManager
|
||||
get() = tangemSdk.keystoreManager
|
||||
|
||||
val secureStorage: SecureStorage
|
||||
get() = tangemSdk.secureStorage
|
||||
|
||||
val userCodeRequestPolicy: UserCodeRequestPolicy
|
||||
get() = tangemSdk.config.userCodeRequestPolicy
|
||||
|
||||
suspend fun scanProduct(
|
||||
cardId: String? = null,
|
||||
messageRes: Int? = null,
|
||||
allowsRequestAccessCodeFromRepository: Boolean = false,
|
||||
): CompletionResult<ScanResponse> {
|
||||
val message = Message(resources.getString(messageRes ?: R.string.initial_message_scan_header))
|
||||
return runTaskAsyncReturnOnMain(
|
||||
runnable = ScanProductTask(
|
||||
card = null,
|
||||
derivationsFinder = derivationsFinder,
|
||||
allowsRequestAccessCodeFromRepository = allowsRequestAccessCodeFromRepository,
|
||||
),
|
||||
cardId = cardId,
|
||||
initialMessage = message,
|
||||
).also { sendScanResultsToAnalytics(it) }
|
||||
}
|
||||
): CompletionResult<ScanResponse>
|
||||
|
||||
suspend fun createProductWallet(
|
||||
scanResponse: ScanResponse,
|
||||
shouldReset: Boolean = false,
|
||||
): CompletionResult<CreateProductWalletTaskResponse> {
|
||||
return runTaskAsync(
|
||||
runnable = CreateProductWalletTask(
|
||||
cardTypesResolver = scanResponse.cardTypesResolver,
|
||||
derivationStyleProvider = scanResponse.derivationStyleProvider,
|
||||
shouldReset = shouldReset,
|
||||
),
|
||||
cardId = scanResponse.card.cardId,
|
||||
initialMessage = Message(resources.getString(R.string.initial_message_create_wallet_body)),
|
||||
iconScanRes = if (scanResponse.cardTypesResolver.isRing()) R.drawable.img_hand_scan_ring else null,
|
||||
)
|
||||
}
|
||||
): CompletionResult<CreateProductWalletTaskResponse>
|
||||
|
||||
suspend fun importWallet(
|
||||
scanResponse: ScanResponse,
|
||||
mnemonic: String,
|
||||
passphrase: String?,
|
||||
shouldReset: Boolean,
|
||||
): CompletionResult<CreateProductWalletTaskResponse> {
|
||||
val defaultMnemonic = try {
|
||||
DefaultMnemonic(mnemonic, tangemSdk.wordlist)
|
||||
} catch (e: TangemSdkError.MnemonicException) {
|
||||
return CompletionResult.Failure(e)
|
||||
}
|
||||
return runTaskAsync(
|
||||
CreateProductWalletTask(
|
||||
cardTypesResolver = scanResponse.cardTypesResolver,
|
||||
derivationStyleProvider = scanResponse.derivationStyleProvider,
|
||||
mnemonic = defaultMnemonic,
|
||||
passphrase = passphrase,
|
||||
shouldReset = shouldReset,
|
||||
),
|
||||
scanResponse.card.cardId,
|
||||
Message(resources.getString(R.string.initial_message_create_wallet_body)),
|
||||
)
|
||||
}
|
||||
|
||||
private fun sendScanResultsToAnalytics(result: CompletionResult<ScanResponse>) {
|
||||
if (result is CompletionResult.Failure) {
|
||||
(result.error as? TangemSdkError)?.let { error ->
|
||||
Analytics.send(Basic.ScanError(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
): CompletionResult<CreateProductWalletTaskResponse>
|
||||
|
||||
suspend fun derivePublicKeys(
|
||||
cardId: String?,
|
||||
derivations: Map<ByteArrayKey, List<DerivationPath>>,
|
||||
): CompletionResult<DerivationTaskResponse> {
|
||||
return runTaskAsyncReturnOnMain(DeriveMultipleWalletPublicKeysTask(derivations), cardId)
|
||||
}
|
||||
): CompletionResult<DerivationTaskResponse>
|
||||
|
||||
suspend fun deriveExtendedPublicKey(
|
||||
cardId: String?,
|
||||
walletPublicKey: ByteArray,
|
||||
derivation: DerivationPath,
|
||||
): CompletionResult<ExtendedPublicKey> = withContext(Dispatchers.Main) {
|
||||
runTaskAsyncReturnOnMain(
|
||||
DeriveWalletPublicKeyTask(walletPublicKey, derivation),
|
||||
cardId,
|
||||
)
|
||||
}
|
||||
): CompletionResult<ExtendedPublicKey>
|
||||
|
||||
suspend fun resetToFactorySettings(
|
||||
cardId: String,
|
||||
allowsRequestAccessCodeFromRepository: Boolean,
|
||||
): CompletionResult<CardDTO> {
|
||||
return runTaskAsyncReturnOnMain(
|
||||
runnable = ResetToFactorySettingsTask(
|
||||
allowsRequestAccessCodeFromRepository = allowsRequestAccessCodeFromRepository,
|
||||
),
|
||||
cardId = cardId,
|
||||
initialMessage = Message(resources.getString(R.string.card_settings_reset_card_to_factory)),
|
||||
)
|
||||
.map { CardDTO(it) }
|
||||
}
|
||||
): CompletionResult<CardDTO>
|
||||
|
||||
suspend fun saveAccessCode(accessCode: String, cardsIds: Set<String>): CompletionResult<Unit> {
|
||||
return userCodeRepository.save(
|
||||
cardsIds = cardsIds,
|
||||
userCode = UserCode(
|
||||
type = UserCodeType.AccessCode,
|
||||
stringValue = accessCode,
|
||||
),
|
||||
)
|
||||
}
|
||||
suspend fun saveAccessCode(accessCode: String, cardsIds: Set<String>): CompletionResult<Unit>
|
||||
|
||||
suspend fun deleteSavedUserCodes(cardsIds: Set<String>): CompletionResult<Unit> {
|
||||
return userCodeRepository.delete(cardsIds.toSet())
|
||||
}
|
||||
suspend fun deleteSavedUserCodes(cardsIds: Set<String>): CompletionResult<Unit>
|
||||
|
||||
suspend fun clearSavedUserCodes(): CompletionResult<Unit> {
|
||||
return userCodeRepository.clear()
|
||||
}
|
||||
suspend fun clearSavedUserCodes(): CompletionResult<Unit>
|
||||
|
||||
suspend fun setPasscode(cardId: String?): CompletionResult<SuccessResponse> {
|
||||
return runTaskAsyncReturnOnMain(
|
||||
SetUserCodeCommand.changePasscode(null),
|
||||
cardId,
|
||||
initialMessage = Message(resources.getString(R.string.initial_message_change_passcode_body)),
|
||||
)
|
||||
}
|
||||
suspend fun setPasscode(cardId: String?): CompletionResult<SuccessResponse>
|
||||
|
||||
suspend fun setAccessCode(cardId: String?): CompletionResult<SuccessResponse> {
|
||||
return runTaskAsyncReturnOnMain(
|
||||
SetUserCodeCommand.changeAccessCode(null),
|
||||
cardId,
|
||||
initialMessage = Message(resources.getString(R.string.initial_message_change_access_code_body)),
|
||||
)
|
||||
}
|
||||
suspend fun setAccessCode(cardId: String?): CompletionResult<SuccessResponse>
|
||||
|
||||
suspend fun setLongTap(cardId: String?): CompletionResult<SuccessResponse> {
|
||||
return runTaskAsyncReturnOnMain(
|
||||
SetUserCodeCommand.resetUserCodes(),
|
||||
cardId,
|
||||
initialMessage = Message(resources.getString(R.string.initial_message_tap_header)),
|
||||
)
|
||||
}
|
||||
suspend fun setLongTap(cardId: String?): CompletionResult<SuccessResponse>
|
||||
|
||||
suspend fun setAccessCodeRecoveryEnabled(cardId: String?, enabled: Boolean): CompletionResult<SuccessResponse> {
|
||||
return runTaskAsyncReturnOnMain(
|
||||
SetUserCodeRecoveryAllowedTask(enabled),
|
||||
cardId,
|
||||
initialMessage = Message(resources.getString(R.string.initial_message_tap_header)),
|
||||
)
|
||||
}
|
||||
suspend fun setAccessCodeRecoveryEnabled(cardId: String?, enabled: Boolean): CompletionResult<SuccessResponse>
|
||||
|
||||
suspend fun scanCard(
|
||||
cardId: String? = null,
|
||||
allowRequestAccessCodeFromRepository: Boolean = false,
|
||||
): CompletionResult<CardDTO> {
|
||||
return runTaskAsyncReturnOnMain(
|
||||
runnable = ScanTask(allowRequestAccessCodeFromRepository),
|
||||
cardId = cardId,
|
||||
initialMessage = Message(resources.getString(R.string.initial_message_tap_header)),
|
||||
)
|
||||
.map { CardDTO(it) }
|
||||
}
|
||||
): CompletionResult<CardDTO>
|
||||
|
||||
suspend fun <T> runTaskAsync(
|
||||
runnable: CardSessionRunnable<T>,
|
||||
|
|
@ -235,53 +86,13 @@ class TangemSdkManager(
|
|||
initialMessage: Message? = null,
|
||||
accessCode: String? = null,
|
||||
@DrawableRes iconScanRes: Int? = null,
|
||||
): CompletionResult<T> = withContext(Dispatchers.Main) {
|
||||
suspendCancellableCoroutine { continuation ->
|
||||
tangemSdk.startSessionWithRunnable(runnable, cardId, initialMessage, accessCode, iconScanRes) { result ->
|
||||
if (continuation.isActive) continuation.resume(result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun <T> runTaskAsyncReturnOnMain(
|
||||
runnable: CardSessionRunnable<T>,
|
||||
cardId: String? = null,
|
||||
initialMessage: Message? = null,
|
||||
): CompletionResult<T> {
|
||||
val result = runTaskAsync(runnable, cardId, initialMessage)
|
||||
return withContext(Dispatchers.Main) { result }
|
||||
}
|
||||
): CompletionResult<T>
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
fun changeDisplayedCardIdNumbersCount(scanResponse: ScanResponse?) {
|
||||
tangemSdk.config.cardIdDisplayFormat = when {
|
||||
scanResponse == null -> CardIdDisplayFormat.Full
|
||||
scanResponse.cardTypesResolver.isTangemTwins() -> CardIdDisplayFormat.LastLuhn(4)
|
||||
else -> CardIdDisplayFormat.Full
|
||||
}
|
||||
}
|
||||
fun changeDisplayedCardIdNumbersCount(scanResponse: ScanResponse?)
|
||||
|
||||
@Deprecated("TangemSdkManager shouldn't returns a string from resources")
|
||||
fun getString(@StringRes stringResId: Int, vararg formatArgs: Any?): String {
|
||||
return resources.getString(stringResId, *formatArgs)
|
||||
}
|
||||
fun getString(@StringRes stringResId: Int, vararg formatArgs: Any?): String
|
||||
|
||||
fun setUserCodeRequestPolicy(policy: UserCodeRequestPolicy) {
|
||||
tangemSdk.config.userCodeRequestPolicy = policy
|
||||
}
|
||||
|
||||
companion object {
|
||||
@Deprecated("Use [DefaultCardSdkProvider] instead")
|
||||
val config = Config(
|
||||
linkedTerminal = true,
|
||||
allowUntrustedCards = true,
|
||||
filter = CardFilter(
|
||||
allowedCardTypes = FirmwareVersion.FirmwareType.values().toList(),
|
||||
maxFirmwareVersion = FirmwareVersion(major = 6, minor = 33),
|
||||
batchIdFilter = CardFilter.Companion.ItemFilter.Deny(
|
||||
items = setOf("0027", "0030", "0031", "0035"),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
fun setUserCodeRequestPolicy(policy: UserCodeRequestPolicy)
|
||||
}
|
||||
|
|
@ -10,7 +10,7 @@ import com.tangem.domain.common.configs.MultiWalletCardConfig
|
|||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.operations.derivation.DerivationTaskResponse
|
||||
import com.tangem.tap.domain.TangemSdkManager
|
||||
import com.tangem.tap.domain.DefaultTangemSdkManager
|
||||
import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider
|
||||
import io.mockk.*
|
||||
import kotlinx.coroutines.test.runTest
|
||||
|
|
@ -21,7 +21,7 @@ import org.junit.Test
|
|||
*/
|
||||
internal class DefaultDerivationsRepositoryTest {
|
||||
|
||||
private val tangemSdkManager = mockk<TangemSdkManager>()
|
||||
private val tangemSdkManager = mockk<DefaultTangemSdkManager>()
|
||||
private val userWalletsStore = mockk<UserWalletsStore>()
|
||||
private val repository = DefaultDerivationsRepository(
|
||||
tangemSdkManager = tangemSdkManager,
|
||||
|
|
|
|||
|
|
@ -53,6 +53,7 @@ dependencies {
|
|||
|
||||
/** Chucker */
|
||||
debugImplementation(deps.chucker)
|
||||
mockedImplementation(deps.chuckerStub)
|
||||
externalImplementation(deps.chuckerStub)
|
||||
internalImplementation(deps.chuckerStub)
|
||||
releaseImplementation(deps.chuckerStub)
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import com.squareup.moshi.JsonClass
|
|||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
|
||||
// TODO remove
|
||||
class FeatureModel(
|
||||
val isTopUpEnabled: Boolean,
|
||||
val isCreatingTwinCardsAllowed: Boolean,
|
||||
|
|
|
|||
|
|
@ -76,6 +76,7 @@ private fun AndroidBuildType.configureBuildVariant(extension: AppExtension, buil
|
|||
}
|
||||
BuildType.Internal,
|
||||
BuildType.External,
|
||||
BuildType.Mocked
|
||||
-> {
|
||||
initWith(extension.buildTypes.getByName(BuildType.Release.id))
|
||||
matchingFallbacks.add(BuildType.Release.id)
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ internal sealed class BuildConfigField(val type: String, val name: String, val v
|
|||
value = "\"$value\"",
|
||||
)
|
||||
|
||||
// TODO remove
|
||||
class TestActionEnabled(isEnabled: Boolean) : BuildConfigField(
|
||||
type = "Boolean",
|
||||
name = "TEST_ACTION_ENABLED",
|
||||
|
|
@ -32,4 +33,10 @@ internal sealed class BuildConfigField(val type: String, val name: String, val v
|
|||
name = "TESTER_MENU_ENABLED",
|
||||
value = isEnabled.toString(),
|
||||
)
|
||||
|
||||
class MockDataSource(isEnabled: Boolean) : BuildConfigField(
|
||||
type = "Boolean",
|
||||
name = "MOCK_DATA_SOURCE",
|
||||
value = isEnabled.toString(),
|
||||
)
|
||||
}
|
||||
|
|
@ -27,6 +27,29 @@ internal enum class BuildType(
|
|||
BuildConfigField.TestActionEnabled(isEnabled = true),
|
||||
BuildConfigField.LogEnabled(isEnabled = true),
|
||||
BuildConfigField.TesterMenuAvailability(isEnabled = true),
|
||||
BuildConfigField.MockDataSource(isEnabled = false),
|
||||
),
|
||||
),
|
||||
|
||||
/**
|
||||
* Build type for QA and business
|
||||
*
|
||||
* Features:
|
||||
* - Env: dev
|
||||
* - Signing config: debug
|
||||
* - Logs
|
||||
* - Enabled mocked datasource
|
||||
* */
|
||||
Mocked(
|
||||
id = "mocked",
|
||||
appIdSuffix = "mocked",
|
||||
versionSuffix = "mocked",
|
||||
configFields = listOf(
|
||||
BuildConfigField.Environment(value = "dev"),
|
||||
BuildConfigField.TestActionEnabled(isEnabled = false),
|
||||
BuildConfigField.LogEnabled(isEnabled = true),
|
||||
BuildConfigField.TesterMenuAvailability(isEnabled = false),
|
||||
BuildConfigField.MockDataSource(isEnabled = true),
|
||||
),
|
||||
),
|
||||
|
||||
|
|
@ -50,6 +73,7 @@ internal enum class BuildType(
|
|||
BuildConfigField.TestActionEnabled(isEnabled = true),
|
||||
BuildConfigField.LogEnabled(isEnabled = true),
|
||||
BuildConfigField.TesterMenuAvailability(isEnabled = true),
|
||||
BuildConfigField.MockDataSource(isEnabled = false),
|
||||
),
|
||||
),
|
||||
|
||||
|
|
@ -70,6 +94,7 @@ internal enum class BuildType(
|
|||
BuildConfigField.TestActionEnabled(isEnabled = false),
|
||||
BuildConfigField.LogEnabled(isEnabled = false),
|
||||
BuildConfigField.TesterMenuAvailability(isEnabled = false),
|
||||
BuildConfigField.MockDataSource(isEnabled = false),
|
||||
),
|
||||
),
|
||||
|
||||
|
|
@ -88,6 +113,7 @@ internal enum class BuildType(
|
|||
BuildConfigField.TestActionEnabled(isEnabled = false),
|
||||
BuildConfigField.LogEnabled(isEnabled = false),
|
||||
BuildConfigField.TesterMenuAvailability(isEnabled = false),
|
||||
BuildConfigField.MockDataSource(isEnabled = false),
|
||||
),
|
||||
),
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue