diff --git a/app/src/main/java/com/tangem/tap/common/analytics/converters/BasicEventConverter.kt b/app/src/main/java/com/tangem/tap/common/analytics/converters/BasicEventConverter.kt index 45d55aac2a..52fa450434 100644 --- a/app/src/main/java/com/tangem/tap/common/analytics/converters/BasicEventConverter.kt +++ b/app/src/main/java/com/tangem/tap/common/analytics/converters/BasicEventConverter.kt @@ -3,11 +3,11 @@ package com.tangem.tap.common.analytics.converters import com.tangem.common.Converter import com.tangem.common.extensions.isZero import com.tangem.domain.common.ScanResponse -import com.tangem.domain.common.util.userWalletId import com.tangem.tap.common.analytics.events.AnalyticsParam import com.tangem.tap.common.analytics.events.Basic import com.tangem.tap.common.analytics.filters.BasicTopUpFilter import com.tangem.tap.domain.extensions.isMultiwalletAllowed +import com.tangem.tap.domain.model.builders.UserWalletIdBuilder import com.tangem.tap.features.wallet.redux.ProgressState import com.tangem.tap.features.wallet.redux.WalletData import com.tangem.tap.features.wallet.redux.WalletState @@ -29,7 +29,9 @@ class BasicSignInEventConverter( currency = cardCurrency, batch = scanResponse.card.batchId, ).apply { - filterData = scanResponse.card.userWalletId.stringValue + filterData = UserWalletIdBuilder.scanResponse(scanResponse) + .build() + ?.stringValue } } } @@ -43,7 +45,7 @@ class BasicTopUpEventConverter( val cardCurrency = ParamCardCurrencyConverter().convert(scanResponse) ?: return null val data = BasicTopUpFilter.Data( - walletId = scanResponse.card.userWalletId.stringValue, + walletId = UserWalletIdBuilder.scanResponse(scanResponse).build()?.stringValue ?: "", cardBalanceState = AnalyticsParam.CardBalanceState.from(value.walletsDataFromStores), ) diff --git a/app/src/main/java/com/tangem/tap/common/feedback/AdditionalFeedbackInfo.kt b/app/src/main/java/com/tangem/tap/common/feedback/AdditionalFeedbackInfo.kt index 16e1bb559a..589597238f 100644 --- a/app/src/main/java/com/tangem/tap/common/feedback/AdditionalFeedbackInfo.kt +++ b/app/src/main/java/com/tangem/tap/common/feedback/AdditionalFeedbackInfo.kt @@ -10,8 +10,8 @@ import com.tangem.blockchain.common.WalletManager import com.tangem.blockchain.common.address.Address import com.tangem.domain.common.CardDTO import com.tangem.domain.common.ScanResponse -import com.tangem.domain.common.util.userWalletId import com.tangem.tap.common.extensions.stripZeroPlainString +import com.tangem.tap.domain.model.builders.UserWalletIdBuilder class AdditionalFeedbackInfo { class EmailWalletInfo( @@ -54,7 +54,7 @@ class AdditionalFeedbackInfo { cardFirmwareVersion = data.card.firmwareVersion.stringValue cardIssuer = data.card.issuer.name signedHashesCount = formatSignedHashes(data.card.wallets) - userWalletId = data.card.userWalletId.stringValue + userWalletId = UserWalletIdBuilder.scanResponse(data).build()?.stringValue ?: "" } fun setWalletsInfo(walletManagers: List) { diff --git a/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationMiddleware.kt b/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationMiddleware.kt index 871ef1ea5c..28357c1efd 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationMiddleware.kt @@ -57,11 +57,8 @@ val navigationMiddleware: Middleware = { _, state -> Build.VERSION.SDK_INT >= Build.VERSION_CODES.R -> { Settings.ACTION_BIOMETRIC_ENROLL } - Build.VERSION.SDK_INT >= Build.VERSION_CODES.P -> { - Settings.ACTION_FINGERPRINT_ENROLL - } else -> { - Settings.ACTION_SETTINGS + Settings.ACTION_SECURITY_SETTINGS } } val intent = Intent(settingsAction).apply { diff --git a/app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt b/app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt index 01350c6c63..be8e4f65b2 100644 --- a/app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt @@ -234,6 +234,11 @@ class TangemSdkManager(private val tangemSdk: TangemSdk, private val context: Co } } + fun useBiometricsForAccessCode(): Boolean { + val policy = tangemSdk.config.userCodeRequestPolicy + return policy is UserCodeRequestPolicy.AlwaysWithBiometrics && policy.codeType == UserCodeType.AccessCode + } + companion object { val config = Config( linkedTerminal = true, diff --git a/app/src/main/java/com/tangem/tap/domain/model/UserWallet.kt b/app/src/main/java/com/tangem/tap/domain/model/UserWallet.kt index 4dec0df7d1..a76fbb9d1a 100644 --- a/app/src/main/java/com/tangem/tap/domain/model/UserWallet.kt +++ b/app/src/main/java/com/tangem/tap/domain/model/UserWallet.kt @@ -1,6 +1,5 @@ package com.tangem.tap.domain.model -import com.tangem.common.extensions.toHexString import com.tangem.domain.common.ScanResponse import com.tangem.domain.common.util.UserWalletId @@ -33,23 +32,4 @@ data class UserWallet( val isLocked: Boolean get() = scanResponse.card.wallets.isEmpty() -} - -/** - * !!! Workaround !!! - * - * Calculate same [UserWalletId] for twins instead - * - * TODO: Remove after [REDACTED_JIRA] - * */ -fun UserWallet.isTwinnedWith(other: UserWallet): Boolean { - if (!scanResponse.isTangemTwins() || !other.scanResponse.isTangemTwins()) return false - if (scanResponse.secondTwinPublicKey == null || other.scanResponse.secondTwinPublicKey == null) return false - if (other.scanResponse.secondTwinPublicKey == scanResponse.card.wallets.firstOrNull()?.publicKey?.toHexString()) { - return true - } - if (scanResponse.secondTwinPublicKey == other.scanResponse.card.wallets.firstOrNull()?.publicKey?.toHexString()) { - return true - } - return false } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/model/builders/UserWalletBuilder.kt b/app/src/main/java/com/tangem/tap/domain/model/builders/UserWalletBuilder.kt index 6e4d834d38..9148486bc6 100644 --- a/app/src/main/java/com/tangem/tap/domain/model/builders/UserWalletBuilder.kt +++ b/app/src/main/java/com/tangem/tap/domain/model/builders/UserWalletBuilder.kt @@ -1,5 +1,7 @@ package com.tangem.tap.domain.model.builders +import com.tangem.common.card.EllipticCurve +import com.tangem.common.card.FirmwareVersion import com.tangem.common.extensions.toHexString import com.tangem.common.services.Result import com.tangem.domain.common.CardDTO @@ -8,7 +10,6 @@ import com.tangem.domain.common.ScanResponse import com.tangem.domain.common.TapWorkarounds.isStart2Coin import com.tangem.domain.common.TwinCardNumber import com.tangem.domain.common.TwinsHelper -import com.tangem.domain.common.util.userWalletId import com.tangem.operations.attestation.OnlineCardVerifier import com.tangem.operations.attestation.TangemApi import com.tangem.tap.domain.model.UserWallet @@ -40,7 +41,16 @@ class UserWalletBuilder( ProductType.Note -> false ProductType.Twins -> false ProductType.SaltPay -> false - ProductType.Wallet -> !card.isStart2Coin + ProductType.Wallet -> when { + card.isStart2Coin -> false + card.firmwareVersion >= FirmwareVersion.MultiWalletAvailable -> true + else -> { + val cardWallets = card.wallets + require(cardWallets.isNotEmpty()) { "Card wallets must not be empty" } + + cardWallets.first().curve == EllipticCurve.Secp256k1 + } + } } fun backupCardsIds(backupCardsIds: Set?) = this.apply { @@ -49,16 +59,20 @@ class UserWalletBuilder( } } - suspend fun build(): UserWallet { + suspend fun build(): UserWallet? { return with(scanResponse) { - UserWallet( - walletId = card.userWalletId, - name = userWalletName, - artworkUrl = loadArtworkUrl(card.cardId, card.cardPublicKey), - cardsInWallet = backupCardsIds.plus(card.cardId), - scanResponse = this, - isMultiCurrency = isMultiCurrency, - ) + UserWalletIdBuilder.scanResponse(scanResponse) + .build() + ?.let { + UserWallet( + walletId = it, + name = userWalletName, + artworkUrl = loadArtworkUrl(card.cardId, card.cardPublicKey), + cardsInWallet = backupCardsIds.plus(card.cardId), + scanResponse = this, + isMultiCurrency = isMultiCurrency, + ) + } } } diff --git a/app/src/main/java/com/tangem/tap/domain/model/builders/UserWalletIdBuilder.kt b/app/src/main/java/com/tangem/tap/domain/model/builders/UserWalletIdBuilder.kt new file mode 100644 index 0000000000..1fdef7c37c --- /dev/null +++ b/app/src/main/java/com/tangem/tap/domain/model/builders/UserWalletIdBuilder.kt @@ -0,0 +1,70 @@ +package com.tangem.tap.domain.model.builders + +import com.tangem.common.extensions.calculateSha256 +import com.tangem.common.extensions.hexToBytes +import com.tangem.crypto.Secp256k1 +import com.tangem.domain.common.CardDTO +import com.tangem.domain.common.ProductType +import com.tangem.domain.common.ScanResponse +import com.tangem.domain.common.TapWorkarounds.isTangemTwins +import com.tangem.domain.common.extensions.calculateHmacSha256 +import com.tangem.domain.common.util.UserWalletId + +class UserWalletIdBuilder private constructor( + private val publicKey: ByteArray?, + private val pairTwinPublicKey: ByteArray? = null, +) { + fun build(): UserWalletId? { + val seed = if (publicKey != null) { + if (pairTwinPublicKey != null) { + Secp256k1.sum(publicKey, pairTwinPublicKey) + } else { + publicKey + } + } else null + + return seed?.let { + UserWalletId(value = calculateUserWalletId(it)) + } + } + + private fun calculateUserWalletId(seed: ByteArray?): ByteArray? { + val message = MESSAGE_FOR_WALLET_ID.toByteArray() + val keyHash = seed?.calculateSha256() + + return if (keyHash != null) { + message.calculateHmacSha256(keyHash) + } else null + } + + companion object { + private const val MESSAGE_FOR_WALLET_ID = "UserWalletID" + + @Throws(IllegalArgumentException::class) + fun card(card: CardDTO): UserWalletIdBuilder { + require(!card.isTangemTwins) { + "For twin cards use scanResponse to ID calculation" + } + + return UserWalletIdBuilder(findPublicKey(card.wallets)) + } + + fun scanResponse(scanResponse: ScanResponse): UserWalletIdBuilder { + return UserWalletIdBuilder( + publicKey = findPublicKey(scanResponse.card.wallets), + pairTwinPublicKey = when (scanResponse.productType) { + ProductType.Twins -> scanResponse.secondTwinPublicKey?.hexToBytes() + ProductType.Note, + ProductType.Wallet, + ProductType.SaltPay, + -> null + }, + ) + } + + private fun findPublicKey(wallets: List): ByteArray? { + return wallets.firstOrNull() + ?.publicKey + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/tokens/UserTokensRepository.kt b/app/src/main/java/com/tangem/tap/domain/tokens/UserTokensRepository.kt index 6fb7de9ad7..b3efb6ce5b 100644 --- a/app/src/main/java/com/tangem/tap/domain/tokens/UserTokensRepository.kt +++ b/app/src/main/java/com/tangem/tap/domain/tokens/UserTokensRepository.kt @@ -6,9 +6,9 @@ import com.tangem.common.services.Result import com.tangem.datasource.api.tangemTech.TangemTechService import com.tangem.datasource.api.tangemTech.UserTokensResponse import com.tangem.domain.common.CardDTO -import com.tangem.domain.common.util.userWalletId import com.tangem.tap.common.AndroidFileReader import com.tangem.tap.domain.NoDataError +import com.tangem.tap.domain.model.builders.UserWalletIdBuilder import com.tangem.tap.domain.tokens.models.BlockchainNetwork import com.tangem.tap.features.demo.DemoHelper import com.tangem.tap.features.wallet.models.Currency @@ -24,7 +24,7 @@ class UserTokensRepository( private val networkService: UserTokensNetworkService, ) { suspend fun getUserTokens(card: CardDTO): List { - val userId = card.userWalletId.stringValue + val userId = getUserWalletId(card) ?: return emptyList() if (DemoHelper.isDemoCardId(card.cardId)) { return loadTokensOffline(card, userId).ifEmpty { loadDemoCurrencies() } } @@ -47,14 +47,14 @@ class UserTokensRepository( } suspend fun saveUserTokens(card: CardDTO, tokens: List) { - val userId = card.userWalletId.stringValue + val userId = getUserWalletId(card) ?: return val userTokens = tokens.toUserTokensResponse() networkService.saveUserTokens(userId, userTokens) storageService.saveUserTokens(userId, userTokens) } suspend fun removeUserTokens(card: CardDTO) { - val userId = card.userWalletId.stringValue + val userId = getUserWalletId(card) ?: return val userTokens = emptyList().toUserTokensResponse() networkService.saveUserTokens(userId, userTokens) storageService.saveUserTokens(userId, userTokens) @@ -70,7 +70,7 @@ class UserTokensRepository( } suspend fun loadBlockchainsToDerive(card: CardDTO): List { - val userId = card.userWalletId.stringValue + val userId = getUserWalletId(card) ?: return emptyList() val blockchainNetworks = loadTokensOffline(card, userId).toBlockchainNetworks() if (DemoHelper.isDemoCardId(card.cardId)) { @@ -103,6 +103,7 @@ class UserTokensRepository( coroutineScope { launch { networkService.saveUserTokens(userId = userId, tokens = userTokens) } } tokens } + else -> { val tokens = storageService.getUserTokens(userId) ?: storageService.getUserTokens(card) tokens.distinct() @@ -114,6 +115,11 @@ class UserTokensRepository( return storageService.getUserTokens(userId) ?: storageService.getUserTokens(card) } + private fun getUserWalletId(card: CardDTO): String? { + return UserWalletIdBuilder.card(card).build() + ?.stringValue + } + companion object { const val SORT_DEFAULT_VALUE = "manual" const val GROUP_DEFAULT_VALUE = "none" diff --git a/app/src/main/java/com/tangem/tap/domain/userWalletList/UserWalletsListManager.kt b/app/src/main/java/com/tangem/tap/domain/userWalletList/UserWalletsListManager.kt index dfd62c05f5..9e7619e7c5 100644 --- a/app/src/main/java/com/tangem/tap/domain/userWalletList/UserWalletsListManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/userWalletList/UserWalletsListManager.kt @@ -19,7 +19,7 @@ interface UserWalletsListManager { suspend fun selectWallet(userWalletId: UserWalletId): CompletionResult /** - * Save user wallet + * Save provided user wallet and set it as selected * @param userWallet [UserWallet] to save * @param canOverride If false, then terminate with [UserWalletListError.WalletAlreadySaved] when user tries to save an * already saved card @@ -27,6 +27,17 @@ interface UserWalletsListManager { * */ suspend fun save(userWallet: UserWallet, canOverride: Boolean = false): CompletionResult + /** + * Same as [save] but not change selected user wallet ID + * and not terminate with [UserWalletListError.WalletAlreadySaved] if [UserWallet] already saved + * + * Can terminate with [NoSuchElementException] if unable to find [UserWallet] with provided [UserWalletId] + * @param userWalletId update [UserWallet] with that [UserWalletId] + * @param update lambda that receives stored [UserWallet] and returns updated [UserWallet] + * @return [CompletionResult] of operation with updated [UserWallet] + * */ + suspend fun update(userWalletId: UserWalletId, update: (UserWallet) -> UserWallet): CompletionResult + suspend fun delete(userWalletIds: List): CompletionResult suspend fun clear(): CompletionResult diff --git a/app/src/main/java/com/tangem/tap/domain/userWalletList/implementation/BiometricUserWalletsListManager.kt b/app/src/main/java/com/tangem/tap/domain/userWalletList/implementation/BiometricUserWalletsListManager.kt index f7781ca770..db1db2ebbd 100644 --- a/app/src/main/java/com/tangem/tap/domain/userWalletList/implementation/BiometricUserWalletsListManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/userWalletList/implementation/BiometricUserWalletsListManager.kt @@ -3,7 +3,6 @@ package com.tangem.tap.domain.userWalletList.implementation import com.tangem.common.* import com.tangem.domain.common.util.UserWalletId import com.tangem.tap.domain.model.UserWallet -import com.tangem.tap.domain.model.isTwinnedWith import com.tangem.tap.domain.userWalletList.UserWalletListError import com.tangem.tap.domain.userWalletList.UserWalletsListManager import com.tangem.tap.domain.userWalletList.model.UserWalletEncryptionKey @@ -11,6 +10,7 @@ import com.tangem.tap.domain.userWalletList.repository.SelectedUserWalletReposit import com.tangem.tap.domain.userWalletList.repository.UserWalletsKeysRepository import com.tangem.tap.domain.userWalletList.repository.UserWalletsPublicInformationRepository import com.tangem.tap.domain.userWalletList.repository.UserWalletsSensitiveInformationRepository +import com.tangem.tap.domain.userWalletList.utils.encryptionKey import com.tangem.tap.domain.userWalletList.utils.toUserWallets import com.tangem.tap.domain.userWalletList.utils.updateWith import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -51,7 +51,7 @@ internal class BiometricUserWalletsListManager( get() = state.value.isLocked override val hasSavedUserWallets: Boolean - get() = publicInformationRepository.isNotEmpty() + get() = keysRepository.hasSavedEncryptionKeys() override suspend fun unlockWithBiometry(): CompletionResult { return unlockWithBiometryInternal() @@ -85,22 +85,35 @@ internal class BiometricUserWalletsListManager( override suspend fun save(userWallet: UserWallet, canOverride: Boolean): CompletionResult { return if (canOverride) { - saveInternal(userWallet) + saveInternal(userWallet, changeSelectedUserWallet = true) } else { val isWalletSaved = state.value.userWallets .any { - // Workaround, check [UserWallet.isTwinnedWith] - it.cardsInWallet.contains(userWallet.cardId) || it.isTwinnedWith(userWallet) + it.walletId == userWallet.walletId || it.cardsInWallet.contains(userWallet.cardId) } if (isWalletSaved) { CompletionResult.Failure(UserWalletListError.WalletAlreadySaved) } else { - saveInternal(userWallet) + saveInternal(userWallet, changeSelectedUserWallet = true) } } } + override suspend fun update( + userWalletId: UserWalletId, + update: (UserWallet) -> UserWallet, + ): CompletionResult { + return get(userWalletId) + .map { storedUserWallet -> + update(storedUserWallet) + } + .flatMap { updatedUserWallet -> + saveInternal(updatedUserWallet, changeSelectedUserWallet = false) + .map { updatedUserWallet } + } + } + override suspend fun delete(userWalletIds: List): CompletionResult { if (userWalletIds.isEmpty()) { return CompletionResult.Success(Unit) @@ -113,9 +126,12 @@ internal class BiometricUserWalletsListManager( .flatMap { keysRepository.delete(userWalletIds) } .map { state.update { prevState -> + val newUserWallets = prevState.userWallets.filter { it.walletId !in userWalletIds } + prevState.copy( encryptionKeys = prevState.encryptionKeys.filter { it.walletId !in userWalletIds }, - userWallets = prevState.userWallets.filter { it.walletId !in userWalletIds }, + userWallets = newUserWallets, + isLocked = newUserWallets.any { it.isLocked }, ) } } @@ -137,27 +153,23 @@ internal class BiometricUserWalletsListManager( } } - private suspend fun saveInternal(userWallet: UserWallet): CompletionResult { - val newEncryptionKeys = state.value.encryptionKeys - .plus(UserWalletEncryptionKey(userWallet)) - .distinctBy { it.walletId } - - return keysRepository.store(newEncryptionKeys) - .doOnSuccess { - state.update { prevState -> - prevState.copy( - encryptionKeys = newEncryptionKeys, - selectedUserWalletId = userWallet.walletId, - ) - } - } + private suspend fun saveInternal( + userWallet: UserWallet, + changeSelectedUserWallet: Boolean, + ): CompletionResult { + return saveEncryptionKeyIfNotNull(userWallet) + .flatMap { sensitiveInformationRepository.save(userWallet, encryptionKey = it) } .flatMap { publicInformationRepository.save(userWallet) } - .flatMap { sensitiveInformationRepository.save(userWallet) } .map { selectedUserWalletRepository.set(userWallet.walletId) } .flatMap { loadModels() } .doOnSuccess { state.update { prevState -> prevState.copy( + selectedUserWalletId = if (changeSelectedUserWallet) { + userWallet.walletId + } else { + prevState.selectedUserWalletId + }, isLocked = prevState.userWallets.any { it.isLocked }, ) } @@ -183,6 +195,27 @@ internal class BiometricUserWalletsListManager( } } + private suspend fun saveEncryptionKeyIfNotNull(userWallet: UserWallet): CompletionResult { + val encryptionKey = userWallet.scanResponse.card.encryptionKey + ?.let { UserWalletEncryptionKey(userWallet.walletId, it) } + + return if (encryptionKey != null) { + keysRepository.save(encryptionKey) + .doOnSuccess { + state.update { prevState -> + prevState.copy( + encryptionKeys = prevState.encryptionKeys + .plus(encryptionKey) + .distinctBy { it.walletId }, + ) + } + } + .map { encryptionKey.encryptionKey } + } else { + CompletionResult.Success(data = null) + } + } + private suspend fun loadModels(): CompletionResult { return getSavedUserWallets() .map { userWallets -> diff --git a/app/src/main/java/com/tangem/tap/domain/userWalletList/implementation/DummyUserWalletsListManager.kt b/app/src/main/java/com/tangem/tap/domain/userWalletList/implementation/DummyUserWalletsListManager.kt deleted file mode 100644 index e616c48758..0000000000 --- a/app/src/main/java/com/tangem/tap/domain/userWalletList/implementation/DummyUserWalletsListManager.kt +++ /dev/null @@ -1,56 +0,0 @@ -package com.tangem.tap.domain.userWalletList.implementation - -import com.tangem.common.CompletionResult -import com.tangem.common.catching -import com.tangem.domain.common.util.UserWalletId -import com.tangem.tap.domain.model.UserWallet -import com.tangem.tap.domain.userWalletList.UserWalletsListManager -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.flowOf - -class DummyUserWalletsListManager : UserWalletsListManager { - override val userWallets: Flow> - get() = flowOf(emptyList()) - override val selectedUserWallet: Flow - get() = flowOf() - override val selectedUserWalletSync: UserWallet? - get() = null - override val isLocked: Flow - get() = flowOf(true) - override val isLockedSync: Boolean - get() = true - override val hasSavedUserWallets: Boolean - get() = false - - override suspend fun unlockWithBiometry(): CompletionResult { - return CompletionResult.Success(null) - } - - override fun lock() { - /* no-op */ - } - - override suspend fun selectWallet(userWalletId: UserWalletId): CompletionResult { - return catching { - error("Not implemented") - } - } - - override suspend fun save(userWallet: UserWallet, canOverride: Boolean): CompletionResult { - return CompletionResult.Success(Unit) - } - - override suspend fun delete(userWalletIds: List): CompletionResult { - return CompletionResult.Success(Unit) - } - - override suspend fun clear(): CompletionResult { - return CompletionResult.Success(Unit) - } - - override suspend fun get(userWalletId: UserWalletId): CompletionResult { - return catching { - error("Not implemented") - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/userWalletList/model/UserWalletEncryptionKey.kt b/app/src/main/java/com/tangem/tap/domain/userWalletList/model/UserWalletEncryptionKey.kt index cdf1edbdfb..df0f473bae 100644 --- a/app/src/main/java/com/tangem/tap/domain/userWalletList/model/UserWalletEncryptionKey.kt +++ b/app/src/main/java/com/tangem/tap/domain/userWalletList/model/UserWalletEncryptionKey.kt @@ -2,19 +2,12 @@ package com.tangem.tap.domain.userWalletList.model import com.squareup.moshi.JsonClass import com.tangem.domain.common.util.UserWalletId -import com.tangem.domain.common.util.encryptionKey -import com.tangem.tap.domain.model.UserWallet @JsonClass(generateAdapter = true) internal data class UserWalletEncryptionKey( val walletId: UserWalletId, val encryptionKey: ByteArray, ) { - constructor(userWallet: UserWallet) : this( - walletId = userWallet.walletId, - encryptionKey = userWallet.scanResponse.card.encryptionKey, - ) - override fun equals(other: Any?): Boolean { if (this === other) return true if (other !is UserWalletEncryptionKey) return false diff --git a/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/UserWalletsKeysRepository.kt b/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/UserWalletsKeysRepository.kt index f10e0beeb5..99b62dc99d 100644 --- a/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/UserWalletsKeysRepository.kt +++ b/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/UserWalletsKeysRepository.kt @@ -14,11 +14,11 @@ internal interface UserWalletsKeysRepository { suspend fun getAll(): CompletionResult> /** - * Store the encryption keys for user wallets. Biometric authentication not required - * @param encryptionKeys List of encryption keys for user wallets + * Save the encryption key for user wallet. Biometric authentication not required + * @param encryptionKey [UserWalletEncryptionKey] to save * @return [CompletionResult] of operation * */ - suspend fun store(encryptionKeys: List): CompletionResult + suspend fun save(encryptionKey: UserWalletEncryptionKey): CompletionResult /** * Delete encryption keys for user wallets. Biometric authentication not required @@ -32,4 +32,10 @@ internal interface UserWalletsKeysRepository { * @return [CompletionResult] of operation * */ suspend fun clear(): CompletionResult + + /** + * Determine if the user has saved user wallets + * @return [Boolean] true if user has saved wallets + * */ + fun hasSavedEncryptionKeys(): Boolean } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/UserWalletsPublicInformationRepository.kt b/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/UserWalletsPublicInformationRepository.kt index f0387d3f0e..8765a6b804 100644 --- a/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/UserWalletsPublicInformationRepository.kt +++ b/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/UserWalletsPublicInformationRepository.kt @@ -12,6 +12,4 @@ internal interface UserWalletsPublicInformationRepository { suspend fun delete(walletIds: List): CompletionResult suspend fun clear(): CompletionResult - - fun isNotEmpty(): Boolean } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/UserWalletsSensitiveInformationRepository.kt b/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/UserWalletsSensitiveInformationRepository.kt index 7e03055150..fdae3e64a0 100644 --- a/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/UserWalletsSensitiveInformationRepository.kt +++ b/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/UserWalletsSensitiveInformationRepository.kt @@ -7,7 +7,7 @@ import com.tangem.tap.domain.userWalletList.model.UserWalletEncryptionKey import com.tangem.tap.domain.userWalletList.model.UserWalletSensitiveInformation internal interface UserWalletsSensitiveInformationRepository { - suspend fun save(userWallet: UserWallet): CompletionResult + suspend fun save(userWallet: UserWallet, encryptionKey: ByteArray?): CompletionResult suspend fun getAll( encryptionKeys: List, ): CompletionResult> diff --git a/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/implementation/BiometricUserWalletsKeysRepository.kt b/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/implementation/BiometricUserWalletsKeysRepository.kt index 5e57d10474..11e558535d 100644 --- a/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/implementation/BiometricUserWalletsKeysRepository.kt +++ b/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/implementation/BiometricUserWalletsKeysRepository.kt @@ -17,6 +17,7 @@ import com.tangem.tap.domain.userWalletList.UserWalletListError import com.tangem.tap.domain.userWalletList.model.UserWalletEncryptionKey import com.tangem.tap.domain.userWalletList.repository.UserWalletsKeysRepository import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.runBlocking import kotlinx.coroutines.withContext internal class BiometricUserWalletsKeysRepository( @@ -44,10 +45,9 @@ internal class BiometricUserWalletsKeysRepository( } } - override suspend fun store(encryptionKeys: List): CompletionResult { + override suspend fun save(encryptionKey: UserWalletEncryptionKey): CompletionResult { return withContext(Dispatchers.IO) { - encryptionKeys.map { storeEncryptionKey(it) } - .fold() + storeEncryptionKey(encryptionKey) .mapFailure { error -> UserWalletListError.SaveEncryptionKeysError(error.cause ?: error) } @@ -77,6 +77,12 @@ internal class BiometricUserWalletsKeysRepository( } } + override fun hasSavedEncryptionKeys(): Boolean { + return runBlocking { + getUserWalletsIds().isNotEmpty() + } + } + private suspend fun getAllInternal(): CompletionResult> { return getUserWalletsIds() .map { userWalletId -> diff --git a/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/implementation/DefaultUserWalletsPublicInformationRepository.kt b/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/implementation/DefaultUserWalletsPublicInformationRepository.kt index 46a016f8c0..7d1bd9333b 100644 --- a/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/implementation/DefaultUserWalletsPublicInformationRepository.kt +++ b/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/implementation/DefaultUserWalletsPublicInformationRepository.kt @@ -72,10 +72,6 @@ internal class DefaultUserWalletsPublicInformationRepository( } } - override fun isNotEmpty(): Boolean { - return secureStorage.get(StorageKey.UserWalletPublicInformation.name)?.isNotEmpty() == true - } - @JvmName("saveWithPublicInformation") private suspend fun save( publicInformation: List, diff --git a/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/implementation/DefaultUserWalletsSensitiveInformationRepository.kt b/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/implementation/DefaultUserWalletsSensitiveInformationRepository.kt index 5918312b6e..673ec51a10 100644 --- a/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/implementation/DefaultUserWalletsSensitiveInformationRepository.kt +++ b/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/implementation/DefaultUserWalletsSensitiveInformationRepository.kt @@ -9,13 +9,13 @@ import com.tangem.common.catching import com.tangem.common.mapFailure import com.tangem.common.services.secure.SecureStorage import com.tangem.domain.common.util.UserWalletId -import com.tangem.domain.common.util.encryptionKey import com.tangem.tap.common.extensions.filterNotNull import com.tangem.tap.domain.model.UserWallet import com.tangem.tap.domain.userWalletList.UserWalletListError import com.tangem.tap.domain.userWalletList.model.UserWalletEncryptionKey import com.tangem.tap.domain.userWalletList.model.UserWalletSensitiveInformation import com.tangem.tap.domain.userWalletList.repository.UserWalletsSensitiveInformationRepository +import com.tangem.tap.domain.userWalletList.utils.encryptionKey import com.tangem.tap.domain.userWalletList.utils.sensitiveInformation import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext @@ -38,11 +38,12 @@ internal class DefaultUserWalletsSensitiveInformationRepository( Cipher.getInstance("$algorithm/$blockMode/$encryptionPadding") } - override suspend fun save(userWallet: UserWallet): CompletionResult { + override suspend fun save(userWallet: UserWallet, encryptionKey: ByteArray?): CompletionResult { + if (encryptionKey == null) return CompletionResult.Success(Unit) // Encryption key is null, do nothing return catching { val encryptedSensitiveInformation = userWallet.sensitiveInformation .encode() - .encryptAndStoreIv(userWallet.walletId.stringValue, userWallet.scanResponse.card.encryptionKey) + .encryptAndStoreIv(userWallet.walletId.stringValue, encryptionKey) getAllEncrypted().toMutableMap() .apply { set(userWallet.walletId.stringValue, encryptedSensitiveInformation) } diff --git a/domain/src/main/java/com/tangem/domain/common/util/CardExtensions.kt b/app/src/main/java/com/tangem/tap/domain/userWalletList/utils/UserWalletEncyptionKeyCalculator.kt similarity index 57% rename from domain/src/main/java/com/tangem/domain/common/util/CardExtensions.kt rename to app/src/main/java/com/tangem/tap/domain/userWalletList/utils/UserWalletEncyptionKeyCalculator.kt index 4dc70fd2bd..d97af026cc 100644 --- a/domain/src/main/java/com/tangem/domain/common/util/CardExtensions.kt +++ b/app/src/main/java/com/tangem/tap/domain/userWalletList/utils/UserWalletEncyptionKeyCalculator.kt @@ -1,24 +1,20 @@ -package com.tangem.domain.common.util +package com.tangem.tap.domain.userWalletList.utils import com.tangem.common.extensions.calculateSha256 import com.tangem.domain.common.CardDTO import com.tangem.domain.common.extensions.calculateHmacSha256 -val CardDTO.userWalletId: UserWalletId - get() = UserWalletId(findWalletPublicKey(wallets)) - -val CardDTO.encryptionKey: ByteArray - get() = findWalletPublicKey(wallets) - ?.let { calculateEncryptionKey(it) } - ?: error("Wallet ID not found") +internal val CardDTO.encryptionKey: ByteArray? + get() = findPublicKey(wallets)?.let { calculateEncryptionKey(it) } private fun calculateEncryptionKey(publicKey: ByteArray): ByteArray { val message = MESSAGE_FOR_ENCRYPTION_KEY.toByteArray() val keyHash = publicKey.calculateSha256() + return message.calculateHmacSha256(keyHash) } -private fun findWalletPublicKey(wallets: List): ByteArray? { +private fun findPublicKey(wallets: List): ByteArray? { return wallets.firstOrNull() ?.publicKey } diff --git a/app/src/main/java/com/tangem/tap/domain/walletCurrencies/implementation/DefaultWalletCurrenciesManager.kt b/app/src/main/java/com/tangem/tap/domain/walletCurrencies/implementation/DefaultWalletCurrenciesManager.kt index 5fd2e25234..e000fe1c6d 100644 --- a/app/src/main/java/com/tangem/tap/domain/walletCurrencies/implementation/DefaultWalletCurrenciesManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/walletCurrencies/implementation/DefaultWalletCurrenciesManager.kt @@ -20,6 +20,7 @@ import com.tangem.tap.domain.walletStores.repository.WalletStoresRepository import com.tangem.tap.features.wallet.models.Currency import com.tangem.tap.features.wallet.models.toBlockchainNetworks import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.withContext internal class DefaultWalletCurrenciesManager( @@ -64,7 +65,15 @@ internal class DefaultWalletCurrenciesManager( saveUserCurrencies(card, newCurrencies) } .flatMap { - walletAmountsRepository.updateAmountsForUserWallet( + val updatedBlockchains = updatedBlockchainNetworks + .map { it.blockchain } + val updatedWalletStores = walletStoresRepository.get(userWallet.walletId) + .firstOrNull() + ?.filter { it.blockchain in updatedBlockchains } + ?: return@flatMap CompletionResult.Success(Unit) + + walletAmountsRepository.updateAmountsForWalletStores( + walletStores = updatedWalletStores, userWallet = userWallet, fiatCurrency = appCurrencyProvider(), ) diff --git a/app/src/main/java/com/tangem/tap/domain/walletStores/repository/WalletAmountsRepository.kt b/app/src/main/java/com/tangem/tap/domain/walletStores/repository/WalletAmountsRepository.kt index 0af5b8f85e..a054ed0ff2 100644 --- a/app/src/main/java/com/tangem/tap/domain/walletStores/repository/WalletAmountsRepository.kt +++ b/app/src/main/java/com/tangem/tap/domain/walletStores/repository/WalletAmountsRepository.kt @@ -29,6 +29,12 @@ interface WalletAmountsRepository { fiatCurrency: FiatCurrency, ): CompletionResult + suspend fun updateAmountsForWalletStores( + walletStores: List, + userWallet: UserWallet, + fiatCurrency: FiatCurrency, + ): CompletionResult + /** * Fetch wallet amounts and fiat rates then update [com.tangem.tap.domain.walletStores.storage.WalletStoresStorage] * and [com.tangem.tap.domain.walletStores.storage.WalletManagerStorage] with new data diff --git a/app/src/main/java/com/tangem/tap/domain/walletStores/repository/implementation/DefaultWalletAmountsRepository.kt b/app/src/main/java/com/tangem/tap/domain/walletStores/repository/implementation/DefaultWalletAmountsRepository.kt index 640c08fe67..444d6fc480 100644 --- a/app/src/main/java/com/tangem/tap/domain/walletStores/repository/implementation/DefaultWalletAmountsRepository.kt +++ b/app/src/main/java/com/tangem/tap/domain/walletStores/repository/implementation/DefaultWalletAmountsRepository.kt @@ -41,7 +41,8 @@ import com.tangem.tap.network.NetworkConnectivity import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.async import kotlinx.coroutines.awaitAll -import kotlinx.coroutines.flow.first +import kotlinx.coroutines.coroutineScope +import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.withContext import timber.log.Timber import java.math.BigDecimal @@ -60,7 +61,7 @@ internal class DefaultWalletAmountsRepository( else withContext(Dispatchers.Default) { awaitAll( async { fetchAmountsForUserWallets(userWallets) }, - async { fetchFiatRates(userWallets, fiatCurrency) }, + async { fetchFiatRates(userWallets, walletStores = null, fiatCurrency) }, ) .fold() } @@ -73,46 +74,40 @@ internal class DefaultWalletAmountsRepository( return updateAmountsForUserWallets(listOf(userWallet), fiatCurrency) } + override suspend fun updateAmountsForWalletStores( + walletStores: List, + userWallet: UserWallet, + fiatCurrency: FiatCurrency, + ): CompletionResult { + return if (walletStores.isEmpty()) CompletionResult.Success(Unit) + else withContext(Dispatchers.Default) { + val userWalletId = userWallet.walletId + val scanResponse = userWallet.scanResponse + + awaitAll( + async { fetchAmountForWalletStores(userWalletId, scanResponse, walletStores) }, + async { fetchFiatRates(listOf(userWallet), walletStores, fiatCurrency) }, + ) + .fold() + } + } + override suspend fun updateAmountsForWalletStore( walletStore: WalletStoreModel, userWallet: UserWallet, fiatCurrency: FiatCurrency, - ): CompletionResult = withContext(Dispatchers.Default) { - val walletId = userWallet.walletId - val scanResponse = userWallet.scanResponse - - awaitAll( - async { - // TODO: Find wallet manager via [com.tangem.tap.domain.walletStores.repository.WalletManagersRepository] - val walletManager = walletStore.walletManager - fetchAmountsForWalletStore(walletId, scanResponse, walletStore, walletManager) - }, - async { fetchFiatRates(listOf(userWallet), fiatCurrency) }, - ) - .fold() - } - - private suspend fun fetchAmountsForUserWallets( - userWallets: List, - ): CompletionResult = withContext(Dispatchers.Default) { - userWallets.map { async { fetchAmountsForUserWallet(it) } } - .awaitAll() - .fold() + ): CompletionResult { + return updateAmountsForWalletStores(listOf(walletStore), userWallet, fiatCurrency) } private suspend fun fetchFiatRates( userWallets: List, + walletStores: List?, fiatCurrency: FiatCurrency, ): CompletionResult { - val walletsIds = userWallets.map { it.walletId } - val walletStores = walletsIds - .flatMap { - walletStoresStorage.getAll() - .first() - .getOrElse(it) { emptyList() } - } + val walletStoresInternal = walletStores ?: getWalletStores(userWallets) - val currencies = walletStores + val currencies = walletStoresInternal .asSequence() .flatMap { it.walletsData } .map { it.currency } @@ -129,7 +124,7 @@ internal class DefaultWalletAmountsRepository( return when (fiatRatesResult) { is Result.Success -> { updateWalletStoresWithFiatRates( - walletStores = walletStores, + walletStores = walletStoresInternal, fiatRates = fiatRatesResult.data.rates, ) @@ -145,7 +140,6 @@ internal class DefaultWalletAmountsRepository( error, """ Unable to fetch fiat rates - |- User wallets ids: $walletsIds |- Coins ids: $coinsIds """.trimIndent(), ) @@ -155,20 +149,34 @@ internal class DefaultWalletAmountsRepository( } } + private suspend fun fetchAmountsForUserWallets( + userWallets: List, + ): CompletionResult = withContext(Dispatchers.Default) { + userWallets.map { async { fetchAmountsForUserWallet(it) } } + .awaitAll() + .fold() + } + private suspend fun fetchAmountsForUserWallet( userWallet: UserWallet, ): CompletionResult = withContext(Dispatchers.Default) { - val walletId = userWallet.walletId + val userWalletId = userWallet.walletId val scanResponse = userWallet.scanResponse - val walletStores = walletStoresStorage.getAll() - .first() - .getOrElse(walletId) { emptyList() } + val walletStores = getWalletStores(listOf(userWallet)) + fetchAmountForWalletStores(userWalletId, scanResponse, walletStores) + } + + private suspend fun fetchAmountForWalletStores( + userWalletId: UserWalletId, + scanResponse: ScanResponse, + walletStores: List, + ): CompletionResult = coroutineScope { walletStores.map { walletStore -> async { // TODO: Find wallet manager via [com.tangem.tap.domain.walletStores.repository.WalletManagersRepository] val walletManager = walletStore.walletManager - fetchAmountsForWalletStore(walletId, scanResponse, walletStore, walletManager) + fetchAmountsForWalletStore(userWalletId, scanResponse, walletStore, walletManager) } } .awaitAll() @@ -176,7 +184,7 @@ internal class DefaultWalletAmountsRepository( } private suspend fun fetchAmountsForWalletStore( - walletId: UserWalletId, + userWalletId: UserWalletId, scanResponse: ScanResponse, walletStore: WalletStoreModel, walletManager: WalletManager?, @@ -186,11 +194,15 @@ internal class DefaultWalletAmountsRepository( } return when { - hasMissedDerivations -> updateWalletStoreWithMissedDerivation(walletStore) - walletManager == null -> updateWalletStoreWithUnreachable(walletStore) + hasMissedDerivations -> { + updateWalletStoreWithMissedDerivation(walletStore) + } + walletManager == null -> { + updateWalletStoreWithUnreachable(walletStore) + } else -> { withInternetConnection { walletManager.update() } - .map { updateWalletManagerWithAmounts(walletId, walletManager) } + .map { updateWalletManagerWithAmounts(userWalletId, walletManager) } .flatMap { updateWalletStoreWithAmounts( walletStore = walletStore, @@ -248,35 +260,6 @@ internal class DefaultWalletAmountsRepository( return CompletionResult.Success(Unit) } - private suspend inline fun withInternetConnection(crossinline block: suspend () -> Unit): CompletionResult { - return if (!NetworkConnectivity.getInstance().isOnlineOrConnecting()) { - val error = WalletStoresError.NoInternetConnection - Timber.e(error) - CompletionResult.Failure(error) - } else withContext(Dispatchers.IO) { - catching { block() } - } - } - - private suspend fun updateWalletManagerWithAmounts( - walletId: UserWalletId, - walletManager: WalletManager, - ) = withContext(Dispatchers.Default) { - walletManagersStorage.update { prevManagers -> - val newManagersForUserWallet = prevManagers[walletId].orEmpty() - .toMutableList() - .apply { - replaceByOrAdd(walletManager) { - it.wallet.blockchain == it.wallet.blockchain - } - } - - prevManagers.apply { - set(walletId, newManagersForUserWallet) - } - } - } - private suspend fun updateWalletStoreWithError( walletStore: WalletStoreModel, wallet: Wallet, @@ -425,4 +408,44 @@ internal class DefaultWalletAmountsRepository( } } } + + private suspend inline fun withInternetConnection(crossinline block: suspend () -> Unit): CompletionResult { + return if (!NetworkConnectivity.getInstance().isOnlineOrConnecting()) { + val error = WalletStoresError.NoInternetConnection + Timber.e(error) + CompletionResult.Failure(error) + } else withContext(Dispatchers.IO) { + catching { block() } + } + } + + private suspend fun updateWalletManagerWithAmounts( + userWalletId: UserWalletId, + walletManager: WalletManager, + ) = withContext(Dispatchers.Default) { + walletManagersStorage.update { prevManagers -> + val newManagersForUserWallet = prevManagers[userWalletId].orEmpty() + .toMutableList() + .apply { + replaceByOrAdd(walletManager) { + it.wallet.blockchain == it.wallet.blockchain + } + } + + prevManagers.apply { + set(userWalletId, newManagersForUserWallet) + } + } + } + + private suspend fun getWalletStores(userWallets: List): List { + return userWallets + .map { it.walletId } + .flatMap { userWalletId -> + walletStoresStorage.getAll() + .firstOrNull() + ?.get(userWalletId) + .orEmpty() + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/walletStores/repository/implementation/DefaultWalletManagersRepository.kt b/app/src/main/java/com/tangem/tap/domain/walletStores/repository/implementation/DefaultWalletManagersRepository.kt index 3de7a3f6b1..6355f2f6d6 100644 --- a/app/src/main/java/com/tangem/tap/domain/walletStores/repository/implementation/DefaultWalletManagersRepository.kt +++ b/app/src/main/java/com/tangem/tap/domain/walletStores/repository/implementation/DefaultWalletManagersRepository.kt @@ -106,11 +106,7 @@ internal class DefaultWalletManagersRepository( override suspend fun delete(userWalletIds: List): CompletionResult = catching { walletManagersStorage.update { prevManagers -> - prevManagers.apply { - userWalletIds.forEach { userWalletId -> - remove(userWalletId) - } - } + prevManagers.filterKeys { it !in userWalletIds } as HashMap> } } diff --git a/app/src/main/java/com/tangem/tap/domain/walletStores/repository/implementation/utils/WalletStoreOperations.kt b/app/src/main/java/com/tangem/tap/domain/walletStores/repository/implementation/utils/WalletStoreOperations.kt index 445ff655f2..2aa7ffcf2d 100644 --- a/app/src/main/java/com/tangem/tap/domain/walletStores/repository/implementation/utils/WalletStoreOperations.kt +++ b/app/src/main/java/com/tangem/tap/domain/walletStores/repository/implementation/utils/WalletStoreOperations.kt @@ -62,9 +62,11 @@ internal fun WalletStoreModel.updateWithSelf( ): WalletStoreModel { val oldStore = this return oldStore.copy( - walletManager = newWalletStore.walletManager, - walletRent = newWalletStore.walletRent, + derivationPath = newWalletStore.derivationPath, walletsData = oldStore.walletsData.updateWithSelf(newWalletStore.walletsData), + walletRent = newWalletStore.walletRent, + blockchainNetwork = newWalletStore.blockchainNetwork, + walletManager = newWalletStore.walletManager, ) } diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsMiddleware.kt b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsMiddleware.kt index 319071642c..9fe984d9c9 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsMiddleware.kt @@ -4,10 +4,8 @@ import com.tangem.common.CompletionResult import com.tangem.common.core.TangemSdkError import com.tangem.common.doOnFailure import com.tangem.common.doOnSuccess -import com.tangem.common.extensions.toHexString import com.tangem.common.flatMap import com.tangem.domain.common.TapWorkarounds.isTangemTwins -import com.tangem.domain.common.util.userWalletId import com.tangem.tap.common.analytics.Analytics import com.tangem.tap.common.analytics.events.AnalyticsParam import com.tangem.tap.common.analytics.events.Settings @@ -20,6 +18,7 @@ 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.model.builders.UserWalletBuilder +import com.tangem.tap.domain.model.builders.UserWalletIdBuilder import com.tangem.tap.features.demo.DemoHelper import com.tangem.tap.features.onboarding.products.twins.redux.CreateTwinWalletMode import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsAction @@ -27,6 +26,7 @@ import com.tangem.tap.preferencesStorage import com.tangem.tap.scope import com.tangem.tap.store import com.tangem.tap.tangemSdkManager +import com.tangem.tap.userTokensRepository import com.tangem.tap.userWalletsListManager import com.tangem.tap.walletStoresManager import com.tangem.wallet.R @@ -79,20 +79,16 @@ class DetailsMiddleware { } DetailsAction.ScanCard -> { scope.launch { - tangemSdkManager.scanCard(allowRequestAccessCodeFromRepository = true) - .doOnSuccess { card -> - val isSameWallet = state.scanResponse?.card?.userWalletId - ?.equals(card.userWalletId) - ?: false + tangemSdkManager.scanProduct(userTokensRepository) + .doOnSuccess { scanResponse -> + val currentUserWalletId = state.scanResponse + ?.let { UserWalletIdBuilder.scanResponse(it).build() } + val scannedUserWalletId = UserWalletIdBuilder.scanResponse(scanResponse) + .build() + val isSameWallet = currentUserWalletId == scannedUserWalletId - // !!! Workaround !!! - // TODO: Remove after [REDACTED_JIRA] - val isTwinned = card.wallets.firstOrNull()?.publicKey?.toHexString() - ?.equals(state.scanResponse?.secondTwinPublicKey) - ?: false - - if (isSameWallet || isTwinned) { - store.dispatchOnMain(DetailsAction.PrepareCardSettingsData(card)) + if (isSameWallet) { + store.dispatchOnMain(DetailsAction.PrepareCardSettingsData(scanResponse.card)) } else { store.dispatchDialogShow( AppDialog.SimpleOkDialogRes( @@ -122,8 +118,10 @@ class DetailsMiddleware { is DetailsAction.ResetToFactory.Proceed -> { val card = store.state.detailsState.cardSettingsState?.card ?: return scope.launch { + val userWalletId = UserWalletIdBuilder.card(card).build() + tangemSdkManager.resetToFactorySettings(card.cardId) - .flatMap { userWalletsListManager.delete(listOf(card.userWalletId)) } + .flatMap { userWalletsListManager.delete(listOfNotNull(userWalletId)) } .flatMap { tangemSdkManager.deleteSavedUserCodes(setOf(card.cardId)) } .doOnSuccess { Analytics.send(Settings.CardSettings.FactoryResetFinished()) @@ -275,12 +273,9 @@ class DetailsMiddleware { private suspend fun saveCurrentWallet(state: DetailsState) { val scanResponse = state.scanResponse ?: return - val userWallet = UserWalletBuilder(scanResponse).build() + val userWallet = UserWalletBuilder(scanResponse).build() ?: return userWalletsListManager.save(userWallet) - .doOnFailure { error -> - Timber.e(error, "Wallet saving failed") - } .doOnSuccess { Analytics.send(Settings.AppSettings.SaveWalletSwitcherChanged(AnalyticsParam.OnOffState.On)) @@ -296,6 +291,9 @@ class DetailsMiddleware { store.onUserWalletSelected(userWallet) } + .doOnFailure { error -> + Timber.e(error, "Unable to save user wallet") + } } private suspend fun deleteSavedWallets() { @@ -315,6 +313,9 @@ class DetailsMiddleware { store.dispatchOnMain(NavigationAction.PopBackTo(AppScreen.Home)) } + .doOnFailure { error -> + Timber.e(error, "Unable to delete saved wallets") + } } private fun saveAccessCodes(state: DetailsState) { @@ -353,6 +354,9 @@ class DetailsMiddleware { ) } } + .doOnFailure { error -> + Timber.e(error, "Unable to delete saved access codes") + } } } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsReducer.kt b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsReducer.kt index f00f0b46b4..11b737097a 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsReducer.kt @@ -11,7 +11,6 @@ import com.tangem.tap.domain.extensions.signedHashesCount import com.tangem.tap.preferencesStorage import com.tangem.tap.store import com.tangem.tap.tangemSdkManager -import com.tangem.tap.userWalletsListManager import org.rekotlin.Action import java.util.* @@ -57,7 +56,7 @@ private fun handlePrepareScreen( createBackupAllowed = action.scanResponse.card.backupStatus == CardDTO.BackupStatus.NoBackup, appCurrency = store.state.globalState.appCurrency, isBiometricsAvailable = tangemSdkManager.canUseBiometry, - saveWallets = userWalletsListManager.hasSavedUserWallets, + saveWallets = preferencesStorage.shouldSaveUserWallets, saveAccessCodes = preferencesStorage.shouldSaveAccessCodes, ) } diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/cardsettings/CardSettingsScreenState.kt b/app/src/main/java/com/tangem/tap/features/details/ui/cardsettings/CardSettingsScreenState.kt index e7354df5c5..927780750e 100644 --- a/app/src/main/java/com/tangem/tap/features/details/ui/cardsettings/CardSettingsScreenState.kt +++ b/app/src/main/java/com/tangem/tap/features/details/ui/cardsettings/CardSettingsScreenState.kt @@ -15,18 +15,18 @@ data class CardSettingsScreenState( ) sealed class CardInfo( - val titleRes: TextReference, val subtitle: TextReference, val clickable: Boolean = false, + val titleRes: TextReference, + val subtitle: TextReference, + val clickable: Boolean = false, ) { class CardId(subtitle: String) : CardInfo( titleRes = TextReference.Res(R.string.details_row_title_cid), - subtitle = TextReference - .Str(subtitle), + subtitle = TextReference.Str(subtitle), ) class Issuer(subtitle: String) : CardInfo( titleRes = TextReference.Res(R.string.details_row_title_issuer), - subtitle = TextReference - .Str(subtitle), + subtitle = TextReference.Str(subtitle), ) class SignedHashes(hashes: String) : CardInfo( @@ -34,10 +34,7 @@ sealed class CardInfo( subtitle = TextReference.Res(R.string.details_row_subtitle_signed_hashes_format, hashes), ) - class SecurityMode( - securityOption: SecurityOption, - clickable: Boolean, - ) : CardInfo( + class SecurityMode(securityOption: SecurityOption, clickable: Boolean) : CardInfo( titleRes = TextReference.Res(R.string.card_settings_security_mode), subtitle = TextReference.Res(securityOption.toTitleRes()), clickable = clickable, @@ -49,9 +46,9 @@ sealed class CardInfo( clickable = true, ) - object ResetToFactorySettings : CardInfo( + class ResetToFactorySettings(subtitle: TextReference.Res) : CardInfo( titleRes = TextReference.Res(R.string.card_settings_reset_card_to_factory), - subtitle = TextReference.Res(R.string.card_settings_reset_card_to_factory_footer), + subtitle = subtitle, clickable = true, ) } diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/cardsettings/CardSettingsViewModel.kt b/app/src/main/java/com/tangem/tap/features/details/ui/cardsettings/CardSettingsViewModel.kt index 44f5cdb8d2..ce9da7f8b0 100644 --- a/app/src/main/java/com/tangem/tap/features/details/ui/cardsettings/CardSettingsViewModel.kt +++ b/app/src/main/java/com/tangem/tap/features/details/ui/cardsettings/CardSettingsViewModel.kt @@ -8,6 +8,7 @@ import com.tangem.tap.common.analytics.events.Settings import com.tangem.tap.common.redux.AppState import com.tangem.tap.features.details.redux.CardSettingsState import com.tangem.tap.features.details.redux.DetailsAction +import com.tangem.wallet.R import org.rekotlin.Store class CardSettingsViewModel(private val store: Store) { @@ -46,7 +47,17 @@ class CardSettingsViewModel(private val store: Store) { cardDetails.add(CardInfo.ChangeAccessCode) } if (state.resetCardAllowed) { - cardDetails.add(CardInfo.ResetToFactorySettings) + cardDetails.add( + CardInfo.ResetToFactorySettings( + subtitle = TextReference.Res( + if (state.card.backupStatus?.isActive == true) { + R.string.reset_card_with_backup_to_factory_message + } else { + R.string.reset_card_without_backup_to_factory_message + }, + ), + ), + ) } CardSettingsScreenState( diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/resetcard/ResetCardScreen.kt b/app/src/main/java/com/tangem/tap/features/details/ui/resetcard/ResetCardScreen.kt index 85d53a13eb..944e4c715f 100644 --- a/app/src/main/java/com/tangem/tap/features/details/ui/resetcard/ResetCardScreen.kt +++ b/app/src/main/java/com/tangem/tap/features/details/ui/resetcard/ResetCardScreen.kt @@ -32,77 +32,64 @@ import com.tangem.tap.features.details.ui.common.SettingsScreensScaffold import com.tangem.wallet.R @Composable -fun ResetCardScreen( - state: ResetCardScreenState, - onBackPressed: () -> Unit, - modifier: Modifier = Modifier, -) { - SettingsScreensScaffold( - content = { ResetCardView(state = state, modifier = modifier) }, - onBackClick = onBackPressed, - backgroundColor = Color.Transparent, - ) +fun ResetCardScreen(state: ResetCardScreenState, onBackPressed: () -> Unit) { + SettingsScreensScaffold( + content = { ResetCardView(state = state) }, + onBackClick = onBackPressed, + backgroundColor = Color.Transparent, + ) } @Composable -fun ResetCardView( - state: ResetCardScreenState, - modifier: Modifier = Modifier, -) { +fun ResetCardView(state: ResetCardScreenState) { Column( - modifier = modifier + modifier = Modifier .fillMaxSize() .verticalScroll(rememberScrollState()), verticalArrangement = Arrangement.SpaceBetween, ) { - Box( - modifier = modifier, - ) { + Box { Image( painter = painterResource(id = R.drawable.ic_reset_background), - contentDescription = "", - modifier = modifier.offset(y = (-82).dp), + contentDescription = null, + modifier = Modifier.offset(y = (-82).dp), ) ScreenTitle(titleRes = R.string.card_settings_reset_card_to_factory) } - Spacer( - modifier = modifier.weight(1f), - ) + Spacer(modifier = Modifier.weight(1f)) Column( - modifier = modifier - .offset(y = (-32).dp), + modifier = Modifier.offset(y = (-32).dp), verticalArrangement = Arrangement.Bottom, ) { Text( text = stringResource(id = R.string.common_attention), - modifier = modifier.padding(start = 20.dp, end = 20.dp), + modifier = Modifier.padding(start = 20.dp, end = 20.dp), style = TangemTypography.headline3, color = colorResource(id = R.color.text_primary_1), ) - Spacer(modifier = modifier.size(24.dp)) + Spacer(modifier = Modifier.size(24.dp)) Text( - text = stringResource(id = R.string.reset_card_to_factory_message), - modifier = modifier - .padding(start = 20.dp, end = 20.dp), + text = stringResource(id = state.descriptionResId), + modifier = Modifier.padding(start = 20.dp, end = 20.dp), style = TangemTypography.body1, color = colorResource(id = R.color.text_secondary), ) - Spacer(modifier = modifier.size(28.dp)) + Spacer(modifier = Modifier.size(28.dp)) Row( - modifier = modifier + modifier = Modifier .fillMaxWidth() .clickable( - onClick = { state.onAcceptWarningToggleClick(!state.accepted) }, - ) + onClick = { state.onAcceptWarningToggleClick(!state.accepted) }, + ) .padding(top = 16.dp, bottom = 16.dp), ) { IconToggleButton( checked = state.accepted, onCheckedChange = state.onAcceptWarningToggleClick, - modifier = modifier.padding(start = 20.dp, end = 20.dp), + modifier = Modifier.padding(start = 20.dp, end = 20.dp), ) { Icon( painter = painterResource( @@ -124,15 +111,13 @@ fun ResetCardView( text = stringResource(id = R.string.reset_card_to_factory_warning_message), style = TangemTypography.body2, color = colorResource(id = R.color.text_secondary), - modifier = modifier - .padding(end = 20.dp), + modifier = Modifier.padding(end = 20.dp), ) } - Spacer(modifier = modifier.size(16.dp)) + Spacer(modifier = Modifier.size(16.dp)) Box( - modifier = modifier - .padding(start = 16.dp, end = 16.dp, bottom = 32.dp), + modifier = Modifier.padding(start = 16.dp, end = 16.dp, bottom = 32.dp), ) { DetailsMainButton( title = stringResource(id = R.string.reset_card_to_factory_button_title), @@ -146,8 +131,14 @@ fun ResetCardView( @Composable @Preview -fun ResetCardScreenPreview( - -) { - ResetCardScreen(state = ResetCardScreenState(onAcceptWarningToggleClick = {}, accepted = true) {}, {}) +fun ResetCardScreenPreview() { + ResetCardScreen( + state = ResetCardScreenState( + descriptionResId = R.string.reset_card_without_backup_to_factory_message, + accepted = false, + onAcceptWarningToggleClick = {}, + onResetButtonClick = {}, + ), + onBackPressed = {}, + ) } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/resetcard/ResetCardScreenState.kt b/app/src/main/java/com/tangem/tap/features/details/ui/resetcard/ResetCardScreenState.kt index f658eac463..54693e7557 100644 --- a/app/src/main/java/com/tangem/tap/features/details/ui/resetcard/ResetCardScreenState.kt +++ b/app/src/main/java/com/tangem/tap/features/details/ui/resetcard/ResetCardScreenState.kt @@ -1,6 +1,9 @@ package com.tangem.tap.features.details.ui.resetcard +import androidx.annotation.StringRes + data class ResetCardScreenState( + @StringRes val descriptionResId: Int, val accepted: Boolean = false, val onAcceptWarningToggleClick: (Boolean) -> Unit, val onResetButtonClick: () -> Unit, diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/resetcard/ResetCardViewModel.kt b/app/src/main/java/com/tangem/tap/features/details/ui/resetcard/ResetCardViewModel.kt index 15f891dba3..1606631d68 100644 --- a/app/src/main/java/com/tangem/tap/features/details/ui/resetcard/ResetCardViewModel.kt +++ b/app/src/main/java/com/tangem/tap/features/details/ui/resetcard/ResetCardViewModel.kt @@ -3,12 +3,18 @@ package com.tangem.tap.features.details.ui.resetcard import com.tangem.tap.common.redux.AppState import com.tangem.tap.features.details.redux.CardSettingsState import com.tangem.tap.features.details.redux.DetailsAction +import com.tangem.wallet.R import org.rekotlin.Store class ResetCardViewModel(private val store: Store) { fun updateState(state: CardSettingsState?): ResetCardScreenState { return ResetCardScreenState( + descriptionResId = if (state?.card?.backupStatus?.isActive == true) { + R.string.reset_card_with_backup_to_factory_message + } else { + R.string.reset_card_without_backup_to_factory_message + }, accepted = state?.resetConfirmed ?: false, onAcceptWarningToggleClick = { store.dispatch(DetailsAction.ResetToFactory.Confirm(it)) }, onResetButtonClick = { store.dispatch(DetailsAction.ResetToFactory.Proceed) }, diff --git a/app/src/main/java/com/tangem/tap/features/home/redux/HomeMiddleware.kt b/app/src/main/java/com/tangem/tap/features/home/redux/HomeMiddleware.kt index 0c5b990b06..fdacaa5bd1 100644 --- a/app/src/main/java/com/tangem/tap/features/home/redux/HomeMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/home/redux/HomeMiddleware.kt @@ -93,7 +93,7 @@ private fun readCard() = scope.launch { onSuccess = { scanResponse -> scope.launch { if (preferencesStorage.shouldSaveUserWallets) { - val userWallet = UserWalletBuilder(scanResponse).build() + val userWallet = UserWalletBuilder(scanResponse).build() ?: return@launch userWalletsListManager.save(userWallet) .doOnFailure { error -> Timber.e(error, "Unable to save user wallet") diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/AddressInfoBottomSheetDialog.kt b/app/src/main/java/com/tangem/tap/features/onboarding/AddressInfoBottomSheetDialog.kt index 4635716436..b56ddaf6cd 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/AddressInfoBottomSheetDialog.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/AddressInfoBottomSheetDialog.kt @@ -11,8 +11,8 @@ import com.tangem.tap.common.extensions.copyToClipboard import com.tangem.tap.common.extensions.dispatchDialogHide import com.tangem.tap.common.extensions.dispatchShare import com.tangem.tap.common.extensions.dispatchToastNotification +import com.tangem.tap.common.extensions.getString import com.tangem.tap.common.redux.AppDialog -import com.tangem.tap.features.wallet.models.Currency import com.tangem.tap.features.wallet.redux.AddressData import com.tangem.tap.store import com.tangem.wallet.R @@ -62,26 +62,12 @@ class AddressInfoBottomSheetDialog( Analytics.send(Token.Recieve.ButtonShareAddress()) store.dispatchShare(data.shareUrl) } - tvReceiveMessage.text = getQRReceiveMessage(tvReceiveMessage.context, stateDialog.currency) - } -} - -fun getQRReceiveMessage(context: Context, currency: Currency): String { - return when (currency) { - is Currency.Blockchain -> { - context.getString( - R.string.address_qr_code_message_format, - currency.blockchain.fullName, - currency.currencySymbol, - ) - } - is Currency.Token -> { - context.getString( - R.string.address_qr_code_message_token_format, - currency.token.name, - currency.currencySymbol, - currency.blockchain.fullName, - ) - } + val blockchain = stateDialog.currency.blockchain + tvReceiveMessage.text = tvReceiveMessage.getString( + id = R.string.address_qr_code_message_format, + blockchain.fullName, + blockchain.currency, + blockchain.fullName, + ) } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/OnboardingHelper.kt b/app/src/main/java/com/tangem/tap/features/onboarding/OnboardingHelper.kt index 9b5c5e9958..775dca1ed3 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/OnboardingHelper.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/OnboardingHelper.kt @@ -70,7 +70,10 @@ class OnboardingHelper { // then open save wallet screen tangemSdkManager.canUseBiometry && preferencesStorage.shouldShowSaveUserWalletScreen -> scope.launch { + store.onCardScanned(scanResponse) + delay(timeMillis = 1_200) + store.dispatchOnMain( SaveWalletAction.ProvideBackupInfo( scanResponse = scanResponse, diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/redux/TwinCardsMiddleware.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/redux/TwinCardsMiddleware.kt index ae60d9f637..b5bd3c7504 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/redux/TwinCardsMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/redux/TwinCardsMiddleware.kt @@ -6,7 +6,6 @@ import com.tangem.common.CompletionResult import com.tangem.common.extensions.guard import com.tangem.domain.common.ScanResponse import com.tangem.domain.common.extensions.withMainContext -import com.tangem.domain.common.util.userWalletId import com.tangem.tap.DELAY_SDK_DIALOG_CLOSE import com.tangem.tap.common.analytics.Analytics import com.tangem.tap.common.analytics.events.AnalyticsParam @@ -25,6 +24,7 @@ import com.tangem.tap.common.redux.navigation.AppScreen import com.tangem.tap.common.redux.navigation.NavigationAction import com.tangem.tap.domain.TapError import com.tangem.tap.domain.extensions.makePrimaryWalletManager +import com.tangem.tap.domain.model.builders.UserWalletIdBuilder import com.tangem.tap.domain.twins.TwinCardsManager import com.tangem.tap.features.onboarding.OnboardingHelper import com.tangem.tap.features.wallet.models.Currency @@ -156,7 +156,7 @@ private fun handle(action: Action, dispatch: DispatchFunction) { TwinCardsStep.CreateFirstWallet -> { scope.launch { userWalletsListManager.delete( - listOf(getScanResponse().card.userWalletId), + listOfNotNull(UserWalletIdBuilder.scanResponse(getScanResponse()).build()), ) } } diff --git a/app/src/main/java/com/tangem/tap/features/saveWallet/redux/SaveWalletMiddleware.kt b/app/src/main/java/com/tangem/tap/features/saveWallet/redux/SaveWalletMiddleware.kt index 106ebfa9af..60bc2e076e 100644 --- a/app/src/main/java/com/tangem/tap/features/saveWallet/redux/SaveWalletMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/saveWallet/redux/SaveWalletMiddleware.kt @@ -88,7 +88,7 @@ internal class SaveWalletMiddleware { scope.launch { val userWallet = UserWalletBuilder(scanResponse) .backupCardsIds(state.backupInfo?.backupCardsIds) - .build() + .build() ?: return@launch val isFirstSavedWallet = !userWalletsListManager.hasSavedUserWallets diff --git a/app/src/main/java/com/tangem/tap/features/tokens/redux/TokensMiddleware.kt b/app/src/main/java/com/tangem/tap/features/tokens/redux/TokensMiddleware.kt index b07a4fd3ca..af6ab1607f 100644 --- a/app/src/main/java/com/tangem/tap/features/tokens/redux/TokensMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/tokens/redux/TokensMiddleware.kt @@ -149,7 +149,7 @@ class TokensMiddleware { && blockchainsToAdd.isEmpty() && blockchainsToRemove.isEmpty() ) { store.dispatchDebugErrorNotification("Nothing to save") - store.dispatch(NavigationAction.PopBackTo()) + store.dispatchOnMain(NavigationAction.PopBackTo()) return@launch } @@ -281,13 +281,16 @@ class TokensMiddleware { ) { val selectedUserWallet = userWalletsListManager.selectedUserWalletSync if (selectedUserWallet != null) { - val updatedUserWallet = selectedUserWallet.copy( - scanResponse = scanResponse, - ) - scope.launch { - userWalletsListManager.save(updatedUserWallet, canOverride = true) - .flatMap { + userWalletsListManager.update( + userWalletId = selectedUserWallet.walletId, + update = { userWallet -> + userWallet.copy( + scanResponse = scanResponse, + ) + }, + ) + .flatMap { updatedUserWallet -> walletCurrenciesManager.addCurrencies( userWallet = updatedUserWallet, currenciesToAdd = currencyList, diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/MultiWalletMiddleware.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/MultiWalletMiddleware.kt index 9bba0011a5..ba82d17ccd 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/MultiWalletMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/MultiWalletMiddleware.kt @@ -12,6 +12,7 @@ import com.tangem.tap.common.analytics.events.MainScreen import com.tangem.tap.common.analytics.events.Token.ButtonRemoveToken import com.tangem.tap.common.extensions.dispatchDialogShow import com.tangem.tap.common.extensions.dispatchErrorNotification +import com.tangem.tap.common.extensions.dispatchOnMain import com.tangem.tap.common.extensions.safeUpdate import com.tangem.tap.common.redux.global.GlobalAction import com.tangem.tap.common.redux.global.GlobalState @@ -179,21 +180,25 @@ class MultiWalletMiddleware { } private fun scanAndUpdateCard( - selectedWallet: UserWallet, + selectedUserWallet: UserWallet, state: WalletState?, - ) = scope.launch { + ) = scope.launch(Dispatchers.Default) { Analytics.send(MainScreen.CardWasScanned()) ScanCardProcessor.scan( - cardId = selectedWallet.cardId, + cardId = selectedUserWallet.cardId, additionalBlockchainsToDerive = state?.missingDerivations?.map { it.blockchain }, ) { scanResponse -> - val userWallet = selectedWallet.copy( - scanResponse = scanResponse, + userWalletsListManager.update( + userWalletId = selectedUserWallet.walletId, + update = { userWallet -> + userWallet.copy( + scanResponse = scanResponse, + ) + }, ) - - userWalletsListManager.save(userWallet, canOverride = true) - .doOnSuccess { - store.state.globalState.tapWalletManager.loadData(userWallet, refresh = true) + .doOnSuccess { updatedUserWallet -> + store.dispatchOnMain(WalletAction.MultiWallet.AddMissingDerivations(emptyList())) + store.state.globalState.tapWalletManager.loadData(updatedUserWallet, refresh = true) } } } diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WalletMiddleware.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WalletMiddleware.kt index 5dcd8b81bd..243ab5eab6 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WalletMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WalletMiddleware.kt @@ -305,7 +305,7 @@ class WalletMiddleware { val reduxWalletStores = wallStores.mapToReduxModels(state.isMultiwalletAllowed) store.dispatchOnMain( WalletAction.WalletStoresChanged.UpdateWalletStores( - reduxWalletStores = reduxWalletStores.toList(), + reduxWalletStores = reduxWalletStores, ), ) } diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/MultiWalletReducer.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/MultiWalletReducer.kt index b1e6cebbac..afb523a5ca 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/MultiWalletReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/MultiWalletReducer.kt @@ -1,12 +1,15 @@ package com.tangem.tap.features.wallet.redux.reducers +import com.google.firebase.crashlytics.FirebaseCrashlytics import com.tangem.blockchain.blockchains.polkadot.ExistentialDepositProvider import com.tangem.blockchain.common.AmountType import com.tangem.blockchain.common.Token import com.tangem.blockchain.common.WalletManager -import com.tangem.common.extensions.guard +import com.tangem.tap.common.extensions.dispatchToastNotification import com.tangem.tap.common.extensions.toFiatString import com.tangem.tap.common.extensions.toFormattedCurrencyString +import com.tangem.tap.common.redux.navigation.AppScreen +import com.tangem.tap.common.redux.navigation.NavigationAction import com.tangem.tap.domain.getFirstToken import com.tangem.tap.domain.tokens.models.BlockchainNetwork import com.tangem.tap.features.wallet.models.Currency @@ -25,13 +28,15 @@ import com.tangem.tap.features.wallet.ui.BalanceStatus import com.tangem.tap.features.wallet.ui.BalanceWidgetData import com.tangem.tap.features.wallet.ui.TokenData import com.tangem.tap.store +import com.tangem.tap.userWalletsListManager +import com.tangem.wallet.R import java.math.BigDecimal class MultiWalletReducer { fun reduce(action: WalletAction.MultiWallet, state: WalletState): WalletState { return when (action) { is WalletAction.MultiWallet.AddBlockchains -> { - val walletStores: List = action.blockchains.mapNotNull { blockchain -> + val walletStores: List = action.blockchains.map { blockchain -> val walletManager = action.walletManagers.firstOrNull { it.wallet.blockchain == blockchain.blockchain && (it.wallet.publicKey.derivationPath?.rawPath == blockchain.derivationPath) @@ -65,10 +70,10 @@ class MultiWalletReducer { ) } - val selectedCurrency = if (!state.isMultiwalletAllowed) { - walletStores.firstOrNull()?.walletsData?.firstOrNull()?.currency + val selectedCurrency = if (state.isMultiwalletAllowed) { + state.selectedCurrency } else { - state.selectedWalletData?.currency + walletStores.firstOrNull()?.walletsData?.firstOrNull()?.currency } state.copy( walletsStores = walletStores, @@ -106,16 +111,22 @@ class MultiWalletReducer { newState } } - is WalletAction.MultiWallet.AddTokens -> { - addTokens(action.tokens, action.blockchain, state) - } - is WalletAction.MultiWallet.AddToken -> { - addTokens(listOf(action.token), action.blockchain, state) - } + is WalletAction.MultiWallet.AddTokens -> addTokens(action.tokens, action.blockchain, state) + is WalletAction.MultiWallet.AddToken -> addTokens(listOf(action.token), action.blockchain, state) is WalletAction.MultiWallet.TokenLoaded -> { val currency = Currency.fromBlockchainNetwork(action.blockchain, action.token) - val walletManager = state.getWalletManager(currency).guard { - throw NullPointerException("MultiWallet.TokenLoaded: WalletManager must be not NULL") + val walletManager = state.getWalletManager(currency) + if (walletManager == null) { + if (userWalletsListManager.hasSavedUserWallets) { + store.dispatch(NavigationAction.PopBackTo(screen = AppScreen.Welcome)) + } else { + store.dispatch(NavigationAction.PopBackTo(screen = AppScreen.Home)) + } + FirebaseCrashlytics.getInstance().recordException( + IllegalStateException("MultiWallet.TokenLoaded: walletManager is null"), + ) + store.dispatchToastNotification(R.string.internal_error_wallet_manager_not_found) + return state } val wallet = walletManager.wallet val pendingTransactions = wallet.getPendingTransactions() @@ -170,41 +181,28 @@ class MultiWalletReducer { action.currencies.forEach { updatedState = updatedState.removeWalletData(state.getWalletData(it)) } updatedState } - is WalletAction.MultiWallet.SetPrimaryBlockchain -> - state.copy(primaryBlockchain = action.blockchain) - - is WalletAction.MultiWallet.SetPrimaryToken -> - state.copy(primaryToken = action.token) + is WalletAction.MultiWallet.SetPrimaryBlockchain -> state.copy(primaryBlockchain = action.blockchain) + is WalletAction.MultiWallet.SetPrimaryToken -> state.copy(primaryToken = action.token) is WalletAction.MultiWallet.SaveCurrencies -> state - is WalletAction.MultiWallet.ShowWalletBackupWarning -> state.copy( - showBackupWarning = action.show, - ) - is WalletAction.MultiWallet.AddMissingDerivations -> state.copy( - missingDerivations = action.blockchains, - ) + is WalletAction.MultiWallet.ShowWalletBackupWarning -> state.copy(showBackupWarning = action.show) + is WalletAction.MultiWallet.AddMissingDerivations -> state.copy(missingDerivations = action.blockchains) is WalletAction.MultiWallet.BackupWallet -> state - is WalletAction.MultiWallet.ScanToGetDerivations -> state.copy( - state = ProgressState.Loading, - ) + is WalletAction.MultiWallet.ScanToGetDerivations -> state.copy(state = ProgressState.Loading) } } private fun findWalletRent(walletStore: WalletStore?): WalletRent? { - return walletStore?.walletsData?.firstOrNull { - it.walletRent != null - }?.walletRent + return walletStore?.walletsData?.firstOrNull { it.walletRent != null }?.walletRent } private fun getExistentialDeposit(walletManager: WalletManager?): String? { return (walletManager as? ExistentialDepositProvider)?.getExistentialDeposit()?.toPlainString() } -} -private fun addTokens( - tokens: List, blockchain: BlockchainNetwork, state: WalletState, -): WalletState { - val wallets = tokens.mapNotNull { token -> token.toWallet(state, blockchain) } - return state.updateWalletsData(wallets) + private fun addTokens(tokens: List, blockchain: BlockchainNetwork, state: WalletState): WalletState { + val wallets = tokens.mapNotNull { token -> token.toWallet(state, blockchain) } + return state.updateWalletsData(wallets) + } } fun Token.toWallet(state: WalletState, blockchain: BlockchainNetwork): WalletData? { diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/WalletReducer.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/WalletReducer.kt index 254fe48e3b..2505702c20 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/WalletReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/WalletReducer.kt @@ -20,7 +20,6 @@ import com.tangem.tap.domain.extensions.isMultiwalletAllowed import com.tangem.tap.domain.getFirstToken import com.tangem.tap.domain.tokens.models.BlockchainNetwork import com.tangem.tap.features.wallet.models.Currency -import com.tangem.tap.features.wallet.models.TotalBalance import com.tangem.tap.features.wallet.models.WalletRent import com.tangem.tap.features.wallet.redux.AddressData import com.tangem.tap.features.wallet.redux.Artwork @@ -36,7 +35,6 @@ import com.tangem.tap.features.wallet.redux.replaceSomeWalletsData import com.tangem.tap.features.wallet.ui.BalanceStatus import com.tangem.tap.features.wallet.ui.BalanceWidgetData import com.tangem.tap.proxy.AppStateHolder -import com.tangem.tap.store import org.rekotlin.Action import timber.log.Timber import java.math.BigDecimal @@ -344,8 +342,9 @@ private fun internalReduce(action: Action, state: AppState, appStateHolder: AppS card.settings.isBackupAllowed && card.backupStatus == CardDTO.BackupStatus.NoBackup, walletCardsCount = card.findCardsCount(), + walletsStores = newState.walletsStores, totalBalance = if (isMultiCurrency) { - TotalBalance(ProgressState.Loading, BigDecimal.ZERO, store.state.globalState.appCurrency) + newState.totalBalance } else { null }, @@ -362,13 +361,13 @@ private fun internalReduce(action: Action, state: AppState, appStateHolder: AppS ) } is WalletAction.LoadData.Success -> { - val selectedCurrency = if (!newState.isMultiwalletAllowed) { + val selectedCurrency = if (newState.isMultiwalletAllowed) { + newState.selectedCurrency + } else { newState.walletsStores.firstOrNull() ?.walletsData ?.firstOrNull() ?.currency - } else { - newState.selectedWalletData?.currency } newState = newState.copy( diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletDetailsFragment.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletDetailsFragment.kt index a7f230dc35..bd1608e7de 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletDetailsFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletDetailsFragment.kt @@ -36,7 +36,6 @@ import com.tangem.tap.common.extensions.toQrCode import com.tangem.tap.common.recyclerView.SpaceItemDecoration import com.tangem.tap.common.redux.navigation.NavigationAction import com.tangem.tap.domain.tokens.models.BlockchainNetwork -import com.tangem.tap.features.onboarding.getQRReceiveMessage import com.tangem.tap.features.wallet.models.Currency import com.tangem.tap.features.wallet.models.PendingTransaction import com.tangem.tap.features.wallet.redux.ErrorType @@ -132,12 +131,8 @@ class WalletDetailsFragment : Fragment(R.layout.fragment_wallet_details), override fun onStop() { super.onStop() store.unsubscribe(this) - } - - override fun onDestroy() { walletDataWatcher.clear() walletStateWatcher.clear() - super.onDestroy() } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { @@ -328,8 +323,21 @@ class WalletDetailsFragment : Fragment(R.layout.fragment_wallet_details), ) } ivQrCode.setImageBitmap(state.walletAddresses.selectedAddress.shareUrl.toQrCode()) - tvReceiveMessage.text = - getQRReceiveMessage(tvReceiveMessage.context, state.currency) + + tvReceiveMessage.text = when (val currency = state.currency) { + is Currency.Blockchain -> tvReceiveMessage.getString( + id = R.string.address_qr_code_message_format, + currency.blockchain.fullName, + currency.currencySymbol, + currency.blockchain.fullName, + ) + is Currency.Token -> tvReceiveMessage.getString( + id = R.string.address_qr_code_message_format, + currency.token.name, + currency.currencySymbol, + currency.blockchain.fullName, + ) + } } } diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletFragment.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletFragment.kt index c0bde194bf..f01805dd39 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletFragment.kt @@ -1,5 +1,6 @@ package com.tangem.tap.features.wallet.ui +import android.content.Context import android.os.Bundle import android.view.Menu import android.view.MenuInflater @@ -9,6 +10,7 @@ import androidx.activity.OnBackPressedCallback import androidx.appcompat.app.AppCompatActivity import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels +import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import androidx.transition.TransitionInflater @@ -16,6 +18,7 @@ import by.kirich1409.viewbindingdelegate.viewBinding import coil.load import coil.size.Scale 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 @@ -59,6 +62,13 @@ class WalletFragment : Fragment(R.layout.fragment_wallet), StoreSubscriber() + override fun onAttach(context: Context) { + super.onAttach(context) + activity?.lifecycleScope?.launchWhenCreated { + viewModel.launch() + } + } + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setHasOptionsMenu(true) @@ -81,7 +91,6 @@ class WalletFragment : Fragment(R.layout.fragment_wallet), StoreSubscriber - walletStoresManager.get(selectedWallet.walletId) + .map { it.walletId } + .distinctUntilChanged() + .flatMapLatest { selectedUserWalletId -> + walletStoresManager.get(selectedUserWalletId) } .onEach { walletStores -> store.dispatch(WalletAction.WalletStoresChanged(walletStores)) diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/dialogs/ScanFailsDialog.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/dialogs/ScanFailsDialog.kt index d3bc64fa17..cef955c28e 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/dialogs/ScanFailsDialog.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/dialogs/ScanFailsDialog.kt @@ -24,7 +24,7 @@ class ScanFailsDialog { Analytics.send(IntroductionProcess.ButtonRequestSupport()) store.dispatch(GlobalAction.SendEmail(ScanFailsEmail())) } - setNeutralButton(R.string.alert_troubleshooting_scan_card_ok) { _, _ -> } + setNeutralButton(R.string.common_cancel) { _, _ -> } setOnDismissListener { store.dispatchDialogHide() } }.create() } diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/SingleWalletView.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/SingleWalletView.kt index e8fc90a242..18ebc3dd4b 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/SingleWalletView.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/SingleWalletView.kt @@ -6,6 +6,7 @@ import androidx.recyclerview.widget.LinearLayoutManager import com.tangem.tap.common.extensions.beginDelayedTransition import com.tangem.tap.common.extensions.fitChipsByGroupWidth import com.tangem.tap.common.extensions.getQuantityString +import com.tangem.tap.common.extensions.getString import com.tangem.tap.common.extensions.hide import com.tangem.tap.common.extensions.show import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsState @@ -65,7 +66,7 @@ class SingleWalletView : WalletView() { setupTwinCards(state.twinCardsState, binding) setupButtons(state.primaryWallet, binding, state.isExchangeServiceFeatureOn) - setupAddressCard(state.primaryWallet, binding) + setupAddressCard(state, binding) showPendingTransactionsIfPresent(state.primaryWallet.pendingTransactions) setupBalance(state, state.primaryWallet) } @@ -146,35 +147,50 @@ class SingleWalletView : WalletView() { } } - private fun setupAddressCard(state: WalletData, binding: FragmentWalletBinding) = with(binding.lAddress) { - if (state.walletAddresses != null && state.currency is Currency.Blockchain) { + private fun setupAddressCard(state: WalletState, binding: FragmentWalletBinding) = with(binding.lAddress) { + val primaryWallet = state.primaryWallet + if (primaryWallet?.walletAddresses != null && primaryWallet.currency is Currency.Blockchain) { binding.lAddress.root.show() - if (state.shouldShowMultipleAddress()) { + if (primaryWallet.shouldShowMultipleAddress()) { (binding.lAddress.root as? ViewGroup)?.beginDelayedTransition() chipGroupAddressType.show() chipGroupAddressType.fitChipsByGroupWidth() - val checkedId = MultipleAddressUiHelper.typeToId(state.walletAddresses.selectedAddress.type) + val checkedId = MultipleAddressUiHelper.typeToId(primaryWallet.walletAddresses.selectedAddress.type) if (checkedId != View.NO_ID) chipGroupAddressType.check(checkedId) chipGroupAddressType.setOnCheckedChangeListener { group, checkedId -> if (checkedId == -1) return@setOnCheckedChangeListener - val type = MultipleAddressUiHelper.idToType(checkedId, state.currency.blockchain) + val type = MultipleAddressUiHelper.idToType(checkedId, primaryWallet.currency.blockchain) type?.let { store.dispatch(WalletAction.ChangeSelectedAddress(type)) } } } else { chipGroupAddressType.hide() } - tvAddress.text = state.walletAddresses.selectedAddress.address + tvAddress.text = primaryWallet.walletAddresses.selectedAddress.address tvExplore.setOnClickListener { store.dispatch( WalletAction.ExploreAddress( - state.walletAddresses.selectedAddress.exploreUrl, + primaryWallet.walletAddresses.selectedAddress.exploreUrl, fragment!!.requireContext(), ), ) } + setupCardInfo(state) } else { binding.lAddress.root.hide() } } + + private fun setupCardInfo(state: WalletState) { + val textView = binding?.lAddress?.tvInfo + val blockchain = state.primaryWallet?.currency?.blockchain + if (textView != null && blockchain != null) { + textView.text = textView.getString( + id = R.string.address_qr_code_message_format, + blockchain.fullName, + blockchain.currency, + blockchain.fullName, + ) + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/walletSelector/redux/WalletSelectorMiddleware.kt b/app/src/main/java/com/tangem/tap/features/walletSelector/redux/WalletSelectorMiddleware.kt index b6b31419de..4772b501d7 100644 --- a/app/src/main/java/com/tangem/tap/features/walletSelector/redux/WalletSelectorMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/walletSelector/redux/WalletSelectorMiddleware.kt @@ -1,6 +1,7 @@ package com.tangem.tap.features.walletSelector.redux import com.tangem.common.CompletionResult +import com.tangem.common.core.TangemSdkError import com.tangem.common.doOnFailure import com.tangem.common.doOnSuccess import com.tangem.common.flatMap @@ -19,6 +20,7 @@ import com.tangem.tap.domain.model.UserWallet import com.tangem.tap.domain.model.WalletStoreModel import com.tangem.tap.domain.model.builders.UserWalletBuilder import com.tangem.tap.domain.scanCard.ScanCardProcessor +import com.tangem.tap.preferencesStorage import com.tangem.tap.scope import com.tangem.tap.store import com.tangem.tap.tangemSdkManager @@ -131,21 +133,46 @@ internal class WalletSelectorMiddleware { private fun addWallet() = scope.launch { Analytics.send(MyWallets.Button.ScanNewCard) - scanCardInternal { scanResponse -> - val userWallet = UserWalletBuilder(scanResponse).build() + val prevUseBiometricsForAccessCode = tangemSdkManager.useBiometricsForAccessCode() - userWalletsListManager.save(userWallet) - .doOnFailure { error -> - store.dispatchOnMain(WalletSelectorAction.AddWallet.Error(error)) - } - .doOnSuccess { - Analytics.send(MyWallets.CardWasScanned) + // Update access code policy for access code saving when a card was scanned + tangemSdkManager.setAccessCodeRequestPolicy( + useBiometricsForAccessCode = preferencesStorage.shouldSaveAccessCodes, + ) + ScanCardProcessor.scan( + onSuccess = { scanResponse -> + saveUserWalletAndPopBackToWalletScreen(scanResponse) + .doOnFailure { error -> + // Rollback policy if card saving was failed + tangemSdkManager.setAccessCodeRequestPolicy(prevUseBiometricsForAccessCode) + store.dispatchOnMain(WalletSelectorAction.AddWallet.Error(error)) + } + }, + onFailure = { error -> + // Rollback policy if card scanning was failed + 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)) + }, + ) + } - store.dispatchOnMain(WalletSelectorAction.AddWallet.Success) - store.dispatchOnMain(NavigationAction.PopBackTo(AppScreen.Wallet)) - store.onUserWalletSelected(userWallet) - } - } + private suspend fun saveUserWalletAndPopBackToWalletScreen(scanResponse: ScanResponse): CompletionResult { + val userWallet = UserWalletBuilder(scanResponse).build() + ?: return CompletionResult.Failure(TangemSdkError.WalletIsNotCreated()) + + return userWalletsListManager.save(userWallet) + .doOnSuccess { + Analytics.send(MyWallets.CardWasScanned) + + store.dispatchOnMain(WalletSelectorAction.AddWallet.Success) + store.dispatchOnMain(NavigationAction.PopBackTo(AppScreen.Wallet)) + store.onUserWalletSelected(userWallet) + } } private fun selectWallet(userWalletId: UserWalletId) { @@ -212,9 +239,7 @@ internal class WalletSelectorMiddleware { Analytics.send(MyWallets.Button.EditWalletTapped) scope.launch { - userWalletsListManager.get(userWalletId) - .map { it.copy(name = newName) } - .flatMap { userWalletsListManager.save(it, canOverride = true) } + userWalletsListManager.update(userWalletId) { it.copy(name = newName) } .doOnFailure { error -> store.dispatchOnMain(WalletSelectorAction.HandleError(error)) } @@ -252,43 +277,32 @@ internal class WalletSelectorMiddleware { .flatMap { walletStoresManager.delete(userWalletsIds) } .flatMap { deleteAccessCodes(userWalletsIds) } .doOnSuccess { - val selectedWallet = userWalletsListManager.selectedUserWalletSync + val selectedUserWallet = userWalletsListManager.selectedUserWalletSync when { - selectedWallet == null -> { + selectedUserWallet == null -> { store.dispatchOnMain(NavigationAction.PopBackTo(AppScreen.Welcome)) } - currentSelectedWalletId != selectedWallet.walletId -> { - store.onUserWalletSelected(selectedWallet) + + currentSelectedWalletId != selectedUserWallet.walletId -> { + store.onUserWalletSelected(selectedUserWallet) } } } } - private suspend inline fun scanCardInternal( - crossinline onCardScanned: suspend (ScanResponse) -> Unit, - ) { - ScanCardProcessor.scan( - onSuccess = { - onCardScanned(it) - }, - onFailure = { error -> - store.dispatchOnMain(WalletSelectorAction.AddWallet.Error(error)) - }, - onWalletNotCreated = { - store.dispatchOnMain(WalletSelectorAction.AddWallet.Success) - store.dispatchOnMain(NavigationAction.PopBackTo()) - }, - ) - } - private suspend fun deleteAccessCodes(userWalletsIds: List): CompletionResult { - val cardsIds = userWalletsListManager.userWallets.firstOrNull().orEmpty() - .asSequence() - .filter { it.walletId in userWalletsIds } - .flatMap { it.cardsInWallet } + val cardsIds = userWalletsListManager.userWallets.firstOrNull() + ?.asSequence() + ?.filter { it.walletId in userWalletsIds } + ?.flatMap { it.cardsInWallet } + ?.toSet() - return tangemSdkManager.deleteSavedUserCodes(cardsIds.toSet()) + return if (cardsIds.isNullOrEmpty()) { + CompletionResult.Success(Unit) + } else { + tangemSdkManager.deleteSavedUserCodes(cardsIds.toSet()) + } } private suspend fun UserWalletModel.updateWalletStoresAndCalculateFiatBalance( diff --git a/app/src/main/java/com/tangem/tap/features/walletSelector/ui/WalletSelectorViewModel.kt b/app/src/main/java/com/tangem/tap/features/walletSelector/ui/WalletSelectorViewModel.kt index 1192b37c4a..52a99013e3 100644 --- a/app/src/main/java/com/tangem/tap/features/walletSelector/ui/WalletSelectorViewModel.kt +++ b/app/src/main/java/com/tangem/tap/features/walletSelector/ui/WalletSelectorViewModel.kt @@ -38,7 +38,6 @@ internal class WalletSelectorViewModel : ViewModel(), StoreSubscriber Unit editingUserWalletsIds.isNotEmpty() && !editingUserWalletsIds.contains(userWalletId) -> { editWallet(userWalletId) } @@ -52,7 +51,7 @@ internal class WalletSelectorViewModel : ViewModel(), StoreSubscriber - store.dispatch(WalletSelectorAction.RenameWallet(editedWalletId, newName)) + store.dispatch(WalletSelectorAction.RenameWallet(editedUserWalletId, newName)) stateInternal.update { prevState -> prevState.copy( renameWalletDialog = null, @@ -137,11 +136,6 @@ internal class WalletSelectorViewModel : ViewModel(), StoreSubscriber appState.skip { old, new -> old.walletSelectorState == new.walletSelectorState } diff --git a/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeMiddleware.kt b/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeMiddleware.kt index 7b0f37e36e..e7cb2e01e7 100644 --- a/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeMiddleware.kt @@ -71,7 +71,7 @@ internal class WelcomeMiddleware { private fun proceedWithCard(state: WelcomeState) = scope.launch { scanCardInternal { scanResponse -> - val userWallet = UserWalletBuilder(scanResponse).build() + val userWallet = UserWalletBuilder(scanResponse).build() ?: return@scanCardInternal userWalletsListManager.save(userWallet, canOverride = true) .doOnFailure { error -> diff --git a/app/src/main/java/com/tangem/tap/proxy/UserWalletManagerImpl.kt b/app/src/main/java/com/tangem/tap/proxy/UserWalletManagerImpl.kt index 8856cdc80d..e12ca9b9be 100644 --- a/app/src/main/java/com/tangem/tap/proxy/UserWalletManagerImpl.kt +++ b/app/src/main/java/com/tangem/tap/proxy/UserWalletManagerImpl.kt @@ -8,12 +8,12 @@ import com.tangem.domain.common.CardDTO import com.tangem.domain.common.TapWorkarounds.derivationStyle import com.tangem.domain.common.extensions.fromNetworkId import com.tangem.domain.common.extensions.toNetworkId -import com.tangem.domain.common.util.userWalletId import com.tangem.lib.crypto.UserWalletManager import com.tangem.lib.crypto.models.Currency import com.tangem.lib.crypto.models.NativeToken import com.tangem.lib.crypto.models.NonNativeToken import com.tangem.tap.domain.extensions.makeWalletManagerForApp +import com.tangem.tap.domain.model.builders.UserWalletIdBuilder import com.tangem.tap.domain.tokens.models.BlockchainNetwork import com.tangem.tap.features.wallet.redux.WalletAction import org.rekotlin.Action @@ -54,7 +54,12 @@ class UserWalletManagerImpl( } override fun getWalletId(): String { - return appStateHolder.getActualCard()?.userWalletId?.stringValue ?: "" + return appStateHolder.getActualCard()?.let { + UserWalletIdBuilder.card(it) + .build() + ?.stringValue + } + ?: "" } override suspend fun isTokenAdded(currency: Currency): Boolean { diff --git a/app/src/main/res/layout/dialog_onboarding_address_info.xml b/app/src/main/res/layout/dialog_onboarding_address_info.xml index ca04440067..eadb8f7f36 100644 --- a/app/src/main/res/layout/dialog_onboarding_address_info.xml +++ b/app/src/main/res/layout/dialog_onboarding_address_info.xml @@ -36,7 +36,7 @@ app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/imv_qr_code" - tools:text="@string/address_qr_code_message_token_format" /> + tools:text="Send only Ethereum (ETH) from Ethereum network to this address. Using other tokens and networks may result in loss of funds." /> + + + tools:text="Send only Ethereum (ETH) from Ethereum network to this address. Using other tokens and networks may result in loss of funds." /> diff --git a/buildSrc/src/main/java/Versions.kt b/buildSrc/src/main/java/Versions.kt index 869eaf8f0b..89471ec92c 100644 --- a/buildSrc/src/main/java/Versions.kt +++ b/buildSrc/src/main/java/Versions.kt @@ -61,7 +61,7 @@ object Versions { // region Tangem const val tangemBlockchainSdk = "develop-141" - const val tangemCardSgk = "develop-178" + const val tangemCardSgk = "develop-179" // endregion Tangem // region Testing diff --git a/core/res/src/main/res/values-de/strings-app.xml b/core/res/src/main/res/values-de/strings.xml similarity index 51% rename from core/res/src/main/res/values-de/strings-app.xml rename to core/res/src/main/res/values-de/strings.xml index 9b09fb053e..0bee218f2b 100644 --- a/core/res/src/main/res/values-de/strings-app.xml +++ b/core/res/src/main/res/values-de/strings.xml @@ -1,5 +1,223 @@ - + + Tangem Wallet + 3 cards + 2 cards + Shipping + Free + I have a promo code… + Total + Other payment methods + Buy now + Meet\nTangem + Buy + Store + Send + Pay + Exchange + Lend + Borrow + Revolutionary Hardware Wallet + Store your crypto assets secure while keeping private keys contained in your card + Ultra Secure Backup + Up to + 3 physical cards + to one wallet + Thousands of Currencies + A hardware wallet for your Bitcoin, Ethereum and many more currencies simultaneously – all in one card + DeFi Compatible + Exchange, buy NFT\'s, make loans and deposits in more than 100 different decentralized services + The Wallet for Everyone + Use it on the go, anywhere, anytime. No wires or batteries. Just tap the card to your phone when you need your crypto. + Order + Search tokens + You are currently running in Demo mode. All funds are not real. + This feature is disabled in Demo mode + Not enough funds for fee on your %s wallet to send a transaction. Top up your %s wallet first. + Available networks + %s network not found. Please, add it first and try again. + Attention + Note that tokens on different networks have different addresses. Double check that your address matches the network when you transfer funds. + Tokens in Solana network are not supported by this card due to firmware limitation. + Contract address copied! + Contract address + Required field + Please select the network + Decimal number must be a valid integer, no higher than %d + Contract address is invalid + Derivation path is invalid + Note that tokens can be created by anyone. Be aware of adding scam tokens, they can cost nothing. + This token/network has already been added to your list + Decimals + Network + Not selected + E.g. USD Coin + Name + E.g. USDC + Token symbol + BIP44 coin type + Default + The server is not available, please try again later + Total balance + The amount does not include some of your funds + Tokens + Manage tokens + No rate + Network is unreachable + Hide token + Hide %s + Hide + You are about to hide this token from the main screen. You can add it back anytime through the manage tokens page. + Unable to hide %s + The %s token is the main currency on the %s network and cannot be hidden as long as you have other tokens on this network in the list. + Your wallet has not been backed up + To protect your assets, we advise you to carry out this procedure + Erneut versuchen + Chat + %s network + I understand + Yes + No + Russian bank cards are not accepted at the moment + Do you have a bank card of another country or a UnionPay card? + This network is not supported. Please select another network. + Card Settings + Security Mode + Change Access Code + Access code will be changed on this card only + Continue + Tangem Bot + Get your card ready! + Scan the card to change its settings. The changes will impact only the card you\'ve scanned and will not affect other cards tied to your wallet. + Scan Card + App Settings + Keep the wallet in the app + Enable to link all the wallets to Tangem app. Biometric authentication will be required for unlocking the app. Transaction signing requires tapping your Tangem card. + Save Access Code + Biometric authentication will be requested instead of the access code for interactions with your card. + Removing the saved card deletes all the saved wallets and their access codes. + This will delete all the saved wallet access codes. Any further operation with the wallet will require submitting the access code. + WalletConnect + Connect to Dapps + Factory Reset will completely delete the wallet from the selected card and remove it from the app. You will not be able to restore the current wallet. + Factory Reset will completely delete the wallet from the selected card and remove it from the app. You will not be able to restore the current wallet or use the card to recover the access code. + I understand that after performing this action, I will no longer have access to the current wallet + Reset the Card + Reset to Factory Settings + Select network + Scan your card + To access all the networks you need to scan the card + Connection with this Dapp cannot be established due to its technical implementation. + Welcome back! + Use %s or scan a card to access the app + Log in with %s + Karte scannen + Access the app + Log into the app and check your balance without scanning the card + Access code + Note that making a transaction with your funds will still require your card + My Wallets + Multi-currency + Single-currency + Add new wallet + Rename Wallet + Wallet name + Unlock all with %s + Invalid Tag. It won\'t be added to the transaction. + Invalid Memo. It won\'t be added to the transaction. + Tag + Memo + Attention + Tap the card with the visa logo + No funds for activation + Please contact support + Four identical digits isn\'t safe + Such a PIN can be brute-forced easily + Pin code + Connect + KYC + Verify your identity + Set PIN code + Register + Verify via Utorg + Refresh + Connect your card + Verify your identity + KYC is in progress + Connect your card to the decentralized payment system + To start using your card you have to pass the KYC process + Please wait until the verification is completed. You\'ll be notified via email. Usually it takes up to 1 hour. You can close the app and come back later. + PIN Code + Set PIN code for your SaltPay card + Chat with support + Please hold the card until the operation complete + To start the backup process you have to add Tangem card as your backup + No backup card + Backup card ready + Finalize the backup process by creating an access code + Prepare the SaltPay card + Tap the SaltPay card + Tap the Tangem card + Support + Claim %s + To get started, simply claim wxDAI to your wallet + Claim + Congratulations! Your first payment crypto card has been activated + Claiming + It will take a few seconds + Something went wrong + Please check your email for further instructions + Do you want to exit the activation process? + In this case, you will need to start from the beginning. + You have used a card from another wallet. Tap the card associated with this wallet + Referral program + Refer your friends to Tangem + You + Will get + for each wallet bought by your friend on your %s network address%s + Your friend + Will get a + %s discount + when buying a card on tangem.com + Your friends bought + Your personal code + Participate + terms and conditions + By tapping this button you accept + You\'ve accepted + of the referral program + Internal error: wallet manager not found + Bilanz: %s + Share + Copy + Erfolg + Give Permission + To continue you need to allow 1inch smart contracts to use your %s + Amount %s + Your Wallet + Spender + Approve + Swap of %s to + Swap + Insufficient funds + Give Permission + Permit and Swap + Failed to load the information about the referral program. Please try again later. + Failed to load the information about the referral program. Reason: %s. Please try again later. + Your participation request could not be processed. Reason: %s. Please try again later. If the problem persists — feel free to contact our support. + Personal code copied! + Buy Tangem Wallet with discount!\n%s + %s network has a concept of Existential Deposit. If your account drops below %s it will be deactivated and any remaining funds will be destroyed. + Ungültige Adresse + This wallet has already been saved, you can add another one + Entfernen + Privacy policy + Enable biometric authorization + It looks like you have biometric authentication disabled, it is necessary to save wallets + Enable + %d selected + biometric authentication + biometrics Tangem Änderungen speichern Warnung @@ -61,7 +279,7 @@ Diese Karte ist für die Zusammenarbeit mit Tangem nicht geeignet Die von Ihnen gescannte Karte ist eine Entwicklungskarte. Akzeptieren Sie sie nicht als Zahlungsmittel. Tippen um zu signieren - To create wallet, connect your phone and the card exactly as it shown above + To create the wallet tap the card as shown above and do not remove until the end of the operation Tippen Sie um den Zugangscode zu ändern Tippen Sie um den Passcode zu ändern Nutzungsbedingungen @@ -97,7 +315,6 @@ Can\'t send a transaction Reason: %s Are you having difficulty scanning your card? - I\'m okay Request support Send feedback Really cool! @@ -133,8 +350,7 @@ Success! Your card is activated and ready to be used Balance - Send only %s (%s) to this address. Using other tokens and networks may result in loss of funds. - Send only %s (%s) from %s network to this address. Using other tokens and networks may result in loss of funds. + Send only %s (%s) from %s network to this address. Using other tokens and networks may result in loss of funds. If the process of creating the wallet gets interrupted in any way, you\'ll have to start over. The twinning process is partly complete. You can\'t exit it now. OK, ich hab\'s! @@ -217,7 +433,7 @@ Tangem feedback Feedback Tell us what functions you are missing, and we will try to help you. - Please tell us what card do you have? + Please tell us what card do you have Please tell us more about your issue. Every small detail can help. Hi support team, The following information is optional. You can erase it if you don\'t want to share it. @@ -227,6 +443,12 @@ Fehler OK Failed to establish WalletConnect session. Please, try again later. + Would you like to use biometrics? + Biometrics will be requested instead of the access code for interactions with your wallet + Allow to use biometrics + Enable biometric authentication + Go to settings to enable biometric authentication in the Tangem App + Scan the card %s diff --git a/core/res/src/main/res/values-de/strings_final.xml b/core/res/src/main/res/values-de/strings_final.xml deleted file mode 100644 index 046904ddaf..0000000000 --- a/core/res/src/main/res/values-de/strings_final.xml +++ /dev/null @@ -1,233 +0,0 @@ - - - Tangem Wallet - 3 cards - 2 cards - Shipping - Free - I have a promo code… - Total - Other payment methods - Buy now - Meet\nTangem - Buy - Store - Send - Pay - Exchange - Lend - Borrow - Revolutionary Hardware Wallet - Store your crypto assets secure while keeping private keys contained in your card - Ultra Secure Backup - Up to - 3 physical cards - to one wallet - Thousands of Currencies - A hardware wallet for your Bitcoin, Ethereum and many more currencies simultaneously – all in one card - DeFi Compatible - Exchange, buy NFT\'s, make loans and deposits in more than 100 different decentralized services - The Wallet for Everyone - Use it on the go, anywhere, anytime. No wires or batteries. Just tap the card to your phone when you need your crypto. - Order - Search tokens - You are currently running in Demo mode. All funds are not real. - This feature is disabled in Demo mode - Not enough funds for fee on your %s wallet to send a transaction. Top up your %s wallet first. - Available networks - %s network not found. Please, add it first and try again. - Attention - Note that tokens on different networks have different addresses. Double check that your address matches the network when you transfer funds. - Tokens in Solana network are not supported by this card due to firmware limitation. - Contract address copied! - Contract address - Required field - Please select the network - Decimal number must be a valid integer, no higher than %d - Contract address is invalid - Derivation path is invalid - Note that tokens can be created by anyone. Be aware of adding scam tokens, they can cost nothing. - This token/network has already been added to your list - Decimals - Network - Not selected - E.g. USD Coin - Name - E.g. USDC - Token symbol - BIP44 coin type - Default - The server is not available, please try again later - Total balance - The amount does not include some of your funds - Tokens - Manage tokens - No rate - Network is unreachable - Hide token - Hide %s - Hide - You are about to hide this token from the main screen. You can add it back anytime through the manage tokens page. - Unable to hide %s - The %s token is the main currency on the %s network and cannot be hidden as long as you have other tokens on this network in the list. - Your wallet has not been backed up - To protect your assets, we advise you to carry out this procedure - Erneut versuchen - Chat - %s network - I understand - Yes - No - Russian bank cards are not accepted at the moment - Do you have a bank card of another country or a UnionPay card? - This network is not supported. Please select another network. - Card Settings - Security Mode - Change access code - Access code will be changed on this card only - Continue - Tangem Bot - Get your card ready! - Scan the card to change its settings. The changes will impact only the card you\'ve scanned and will not affect other cards tied to your wallet. - Scan Card - App Settings - Keep the wallet in the app - Enable to link all the wallets to Tangem app. Biometric authentication will be required for unlocking the app. Transaction signing requires tapping your Tangem card. - Save Access Code - Biometric authentication will be requested instead of the access code for interactions with your card. - Removing the saved card deletes all the saved wallets and their access codes. - This will delete all the saved wallet access codes. Any further operation with the wallet will require submitting the access code. - WalletConnect - Connect to Dapps - This action will completely remove the wallet from the selected card. You will not be able to restore the current wallet or use the card to recover the access code. - I understand that after performing this action, I will no longer have access to the current wallet - Reset the card - Reset to Factory Settings - Factory Reset will completely delete the wallet from the selected card. You will not be able to restore the current wallet or use the card to recover the access code. - Select network - Scan your card - To access all the networks you need to scan the card - Connection with this Dapp cannot be established due to its technical implementation. - Welcome back! - Use %s or scan a card to access the app - Log in with %s - Karte scannen - Save your Wallet - Would you like to use %s? - Would you like to use biometrics? - Access the app - Log into the app and check your balance without scanning the card - Access code - %s will be requested instead of the access code for interactions with your wallet - Biometrics will be requested instead of the access code for interactions with your wallet - Note that making a transaction with your funds will still require your card - Allow to use %s - Allow to use biometrics - New feature - My Wallets - Multi-currency - Single-currency - Add new wallet - Rename Wallet - Wallet name - Unlock all with %s - Invalid Tag. It won\'t be added to the transaction. - Invalid Memo. It won\'t be added to the transaction. - Tag - Memo - Attention - Tap the card with the visa logo - No funds for activation - Please contact support - Four identical digits isn\'t safe - Such a PIN can be brute-forced easily - Pin code - Connect - KYC - Verify your identity - Set PIN code - Register - Verify via Utorg - Refresh - Connect your card - Verify your identity - KYC is in progress - Connect your card to the decentralized payment system - To start using your card you have to pass the KYC process - Please wait until the verification is completed. Usually it takes up to 1 hour. You can close the app and come back later. - PIN Code - Set PIN code for your SaltPay card - Chat with support - Please hold the card until the operation complete - To start the backup process you have to add Tangem card as your backup - No backup card - Backup card ready - Finalize the backup process by creating an access code - Prepare the SaltPay card - Tap the SaltPay card - Tap the Tangem card - Support - Claim %s - To get started, simply claim wxDAI to your wallet - Claim - Congratulations! Your first payment crypto card has been activated - Claiming - It will take a few seconds - Something went wrong - Please check you email for further instructions - Do you want to exit the activation process? - In this case, you will need to start from the beginning. - You have used a card from another wallet. Tap the card associated with this wallet - Referral program - Refer your friends to Tangem - You - Will get - for each wallet bought by your friend on your %s network address%s - Your friend - Will get a - %s discount - when buying a card on tangem.com - Your friends bought - Your personal code - Participate - terms and conditions - By tapping this button you accept - You\'ve accepted - of the referral program - Internal error: wallet manager not found - Bilanz: %s - Share - Copy - Erfolg - Give Permission - To continue you need to allow 1inch smart contracts to use your %s - Amount %s - Your Wallet - Spender - Approve - Swap of %s to - Swap - Insufficient funds - Give Permission - Permit and Swap - Failed to load the information about the referral program. Please try again later. - Failed to load the information about the referral program. Reason: %s. Please try again later. - Your participation request could not be processed. Reason: %s. Please try again later. If the problem persists — feel free to contact our support. - Personal code copied! - Buy Tangem Wallet with discount!\n%s - %s network has a concept of Existential Deposit. If your account drops below %s it will be deactivated and any remaining funds will be destroyed. - Ungültige Adresse - This wallet has already been saved, you can add another one - Entfernen - Privacy policy - Enable biometric authorization - It looks like you have biometric authentication disabled, it is necessary to save wallets - Enable - Save your Wallet feature allows you to use your wallet with biometric auth without tapping your card to the phone to gain access. - %d selected - biometric authentication - biometrics - Enable biometric authentication - Go to settings to enable biometric authentication in the Tangem App - Scan the card - diff --git a/core/res/src/main/res/values-de/strings_plurals_final.xml b/core/res/src/main/res/values-de/strings_plurals.xml similarity index 100% rename from core/res/src/main/res/values-de/strings_plurals_final.xml rename to core/res/src/main/res/values-de/strings_plurals.xml diff --git a/core/res/src/main/res/values-fr/strings-app.xml b/core/res/src/main/res/values-fr/strings.xml similarity index 51% rename from core/res/src/main/res/values-fr/strings-app.xml rename to core/res/src/main/res/values-fr/strings.xml index 8d7d037a69..237e0965f6 100644 --- a/core/res/src/main/res/values-fr/strings-app.xml +++ b/core/res/src/main/res/values-fr/strings.xml @@ -1,5 +1,223 @@ - + + Tangem Wallet + 3 cards + 2 cards + Shipping + Free + I have a promo code… + Total + Other payment methods + Buy now + Meet\nTangem + Buy + Store + Send + Pay + Exchange + Lend + Borrow + Revolutionary Hardware Wallet + Store your crypto assets secure while keeping private keys contained in your card + Ultra Secure Backup + Up to + 3 physical cards + to one wallet + Thousands of Currencies + A hardware wallet for your Bitcoin, Ethereum and many more currencies simultaneously – all in one card + DeFi Compatible + Exchange, buy NFT\'s, make loans and deposits in more than 100 different decentralized services + The Wallet for Everyone + Use it on the go, anywhere, anytime. No wires or batteries. Just tap the card to your phone when you need your crypto. + Order + Search tokens + You are currently running in Demo mode. All funds are not real. + This feature is disabled in Demo mode + Not enough funds for fee on your %s wallet to send a transaction. Top up your %s wallet first. + Available networks + %s network not found. Please, add it first and try again. + Attention + Note that tokens on different networks have different addresses. Double check that your address matches the network when you transfer funds. + Tokens in Solana network are not supported by this card due to firmware limitation. + Contract address copied! + Contract address + Required field + Please select the network + Decimal number must be a valid integer, no higher than %d + Contract address is invalid + Derivation path is invalid + Note that tokens can be created by anyone. Be aware of adding scam tokens, they can cost nothing. + This token/network has already been added to your list + Decimals + Network + Not selected + E.g. USD Coin + Name + E.g. USDC + Token symbol + BIP44 coin type + Default + The server is not available, please try again later + Total balance + The amount does not include some of your funds + Tokens + Manage tokens + No rate + Network is unreachable + Hide token + Hide %s + Hide + You are about to hide this token from the main screen. You can add it back anytime through the manage tokens page. + Unable to hide %s + The %s token is the main currency on the %s network and cannot be hidden as long as you have other tokens on this network in the list. + Your wallet has not been backed up + To protect your assets, we advise you to carry out this procedure + Réessayer + Chat + %s network + I understand + Yes + No + Russian bank cards are not accepted at the moment + Do you have a bank card of another country or a UnionPay card? + This network is not supported. Please select another network. + Card Settings + Security Mode + Change Access Code + Access code will be changed on this card only + Continue + Tangem Bot + Get your card ready! + Scan the card to change its settings. The changes will impact only the card you\'ve scanned and will not affect other cards tied to your wallet. + Scan Card + App Settings + Keep the wallet in the app + Enable to link all the wallets to Tangem app. Biometric authentication will be required for unlocking the app. Transaction signing requires tapping your Tangem card. + Save Access Code + Biometric authentication will be requested instead of the access code for interactions with your card. + Removing the saved card deletes all the saved wallets and their access codes. + This will delete all the saved wallet access codes. Any further operation with the wallet will require submitting the access code. + WalletConnect + Connect to Dapps + Factory Reset will completely delete the wallet from the selected card and remove it from the app. You will not be able to restore the current wallet. + Factory Reset will completely delete the wallet from the selected card and remove it from the app. You will not be able to restore the current wallet or use the card to recover the access code. + I understand that after performing this action, I will no longer have access to the current wallet + Reset the Card + Reset to Factory Settings + Select network + Scan your card + To access all the networks you need to scan the card + Connection with this Dapp cannot be established due to its technical implementation. + Welcome back! + Use %s or scan a card to access the app + Log in with %s + Scannez la carte + Access the app + Log into the app and check your balance without scanning the card + Access code + Note that making a transaction with your funds will still require your card + My Wallets + Multi-currency + Single-currency + Add new wallet + Rename Wallet + Wallet name + Unlock all with %s + Invalid Tag. It won\'t be added to the transaction. + Invalid Memo. It won\'t be added to the transaction. + Tag + Memo + Attention + Tap the card with the visa logo + No funds for activation + Please contact support + Four identical digits isn\'t safe + Such a PIN can be brute-forced easily + Pin code + Connect + KYC + Verify your identity + Set PIN code + Register + Verify via Utorg + Refresh + Connect your card + Verify your identity + KYC is in progress + Connect your card to the decentralized payment system + To start using your card you have to pass the KYC process + Please wait until the verification is completed. You\'ll be notified via email. Usually it takes up to 1 hour. You can close the app and come back later. + PIN Code + Set PIN code for your SaltPay card + Chat with support + Please hold the card until the operation complete + To start the backup process you have to add Tangem card as your backup + No backup card + Backup card ready + Finalize the backup process by creating an access code + Prepare the SaltPay card + Tap the SaltPay card + Tap the Tangem card + Support + Claim %s + To get started, simply claim wxDAI to your wallet + Claim + Congratulations! Your first payment crypto card has been activated + Claiming + It will take a few seconds + Something went wrong + Please check your email for further instructions + Do you want to exit the activation process? + In this case, you will need to start from the beginning. + You have used a card from another wallet. Tap the card associated with this wallet + Referral program + Refer your friends to Tangem + You + Will get + for each wallet bought by your friend on your %s network address%s + Your friend + Will get a + %s discount + when buying a card on tangem.com + Your friends bought + Your personal code + Participate + terms and conditions + By tapping this button you accept + You\'ve accepted + of the referral program + Internal error: wallet manager not found + Solde : %s + Share + Copy + Avec succès + Give Permission + To continue you need to allow 1inch smart contracts to use your %s + Amount %s + Your Wallet + Spender + Approve + Swap of %s to + Swap + Insufficient funds + Give Permission + Permit and Swap + Failed to load the information about the referral program. Please try again later. + Failed to load the information about the referral program. Reason: %s. Please try again later. + Your participation request could not be processed. Reason: %s. Please try again later. If the problem persists — feel free to contact our support. + Personal code copied! + Buy Tangem Wallet with discount!\n%s + %s network has a concept of Existential Deposit. If your account drops below %s it will be deactivated and any remaining funds will be destroyed. + Adresse incorrecte + This wallet has already been saved, you can add another one + Supprimer + Privacy policy + Enable biometric authorization + It looks like you have biometric authentication disabled, it is necessary to save wallets + Enable + %d selected + biometric authentication + biometrics Tangem Sauvegarder les modifications Alerte @@ -61,7 +279,7 @@ Cette carte n\'est pas conçue pour fonctionner avec Tangem La carte que vous avez scannée est une carte de développement. Ne l\'acceptez pas comme paiement. Touchez pour signer - To create wallet, connect your phone and the card exactly as it shown above + To create the wallet tap the card as shown above and do not remove until the end of the operation Touchez, pour modifier le code d\'accès Touchez, pour modifier le mot de passe Conditions d\'utilisation @@ -97,7 +315,6 @@ Can\'t send a transaction Reason: %s Are you having difficulty scanning your card? - I\'m okay Request support Send feedback Really cool! @@ -133,8 +350,7 @@ Success! Your card is activated and ready to be used Balance - Send only %s (%s) to this address. Using other tokens and networks may result in loss of funds. - Send only %s (%s) from %s network to this address. Using other tokens and networks may result in loss of funds. + Send only %s (%s) from %s network to this address. Using other tokens and networks may result in loss of funds. If the process of creating the wallet gets interrupted in any way, you\'ll have to start over. The twinning process is partly complete. You can\'t exit it now. Ok, je l\'ai! @@ -217,7 +433,7 @@ Tangem feedback Feedback Tell us what functions you are missing, and we will try to help you. - Please tell us what card do you have? + Please tell us what card do you have Please tell us more about your issue. Every small detail can help. Hi support team, The following information is optional. You can erase it if you don\'t want to share it. @@ -227,6 +443,12 @@ Erreur OK Failed to establish WalletConnect session. Please, try again later. + Would you like to use biometrics? + Biometrics will be requested instead of the access code for interactions with your wallet + Allow to use biometrics + Enable biometric authentication + Go to settings to enable biometric authentication in the Tangem App + Scan the card %s diff --git a/core/res/src/main/res/values-fr/strings_final.xml b/core/res/src/main/res/values-fr/strings_final.xml deleted file mode 100644 index 4b3d386d9b..0000000000 --- a/core/res/src/main/res/values-fr/strings_final.xml +++ /dev/null @@ -1,233 +0,0 @@ - - - Tangem Wallet - 3 cards - 2 cards - Shipping - Free - I have a promo code… - Total - Other payment methods - Buy now - Meet\nTangem - Buy - Store - Send - Pay - Exchange - Lend - Borrow - Revolutionary Hardware Wallet - Store your crypto assets secure while keeping private keys contained in your card - Ultra Secure Backup - Up to - 3 physical cards - to one wallet - Thousands of Currencies - A hardware wallet for your Bitcoin, Ethereum and many more currencies simultaneously – all in one card - DeFi Compatible - Exchange, buy NFT\'s, make loans and deposits in more than 100 different decentralized services - The Wallet for Everyone - Use it on the go, anywhere, anytime. No wires or batteries. Just tap the card to your phone when you need your crypto. - Order - Search tokens - You are currently running in Demo mode. All funds are not real. - This feature is disabled in Demo mode - Not enough funds for fee on your %s wallet to send a transaction. Top up your %s wallet first. - Available networks - %s network not found. Please, add it first and try again. - Attention - Note that tokens on different networks have different addresses. Double check that your address matches the network when you transfer funds. - Tokens in Solana network are not supported by this card due to firmware limitation. - Contract address copied! - Contract address - Required field - Please select the network - Decimal number must be a valid integer, no higher than %d - Contract address is invalid - Derivation path is invalid - Note that tokens can be created by anyone. Be aware of adding scam tokens, they can cost nothing. - This token/network has already been added to your list - Decimals - Network - Not selected - E.g. USD Coin - Name - E.g. USDC - Token symbol - BIP44 coin type - Default - The server is not available, please try again later - Total balance - The amount does not include some of your funds - Tokens - Manage tokens - No rate - Network is unreachable - Hide token - Hide %s - Hide - You are about to hide this token from the main screen. You can add it back anytime through the manage tokens page. - Unable to hide %s - The %s token is the main currency on the %s network and cannot be hidden as long as you have other tokens on this network in the list. - Your wallet has not been backed up - To protect your assets, we advise you to carry out this procedure - Réessayer - Chat - %s network - I understand - Yes - No - Russian bank cards are not accepted at the moment - Do you have a bank card of another country or a UnionPay card? - This network is not supported. Please select another network. - Card Settings - Security Mode - Change access code - Access code will be changed on this card only - Continue - Tangem Bot - Get your card ready! - Scan the card to change its settings. The changes will impact only the card you\'ve scanned and will not affect other cards tied to your wallet. - Scan Card - App Settings - Keep the wallet in the app - Enable to link all the wallets to Tangem app. Biometric authentication will be required for unlocking the app. Transaction signing requires tapping your Tangem card. - Save Access Code - Biometric authentication will be requested instead of the access code for interactions with your card. - Removing the saved card deletes all the saved wallets and their access codes. - This will delete all the saved wallet access codes. Any further operation with the wallet will require submitting the access code. - WalletConnect - Connect to Dapps - This action will completely remove the wallet from the selected card. You will not be able to restore the current wallet or use the card to recover the access code. - I understand that after performing this action, I will no longer have access to the current wallet - Reset the card - Reset to Factory Settings - Factory Reset will completely delete the wallet from the selected card. You will not be able to restore the current wallet or use the card to recover the access code. - Select network - Scan your card - To access all the networks you need to scan the card - Connection with this Dapp cannot be established due to its technical implementation. - Welcome back! - Use %s or scan a card to access the app - Log in with %s - Scannez la carte - Save your Wallet - Would you like to use %s? - Would you like to use biometrics? - Access the app - Log into the app and check your balance without scanning the card - Access code - %s will be requested instead of the access code for interactions with your wallet - Biometrics will be requested instead of the access code for interactions with your wallet - Note that making a transaction with your funds will still require your card - Allow to use %s - Allow to use biometrics - New feature - My Wallets - Multi-currency - Single-currency - Add new wallet - Rename Wallet - Wallet name - Unlock all with %s - Invalid Tag. It won\'t be added to the transaction. - Invalid Memo. It won\'t be added to the transaction. - Tag - Memo - Attention - Tap the card with the visa logo - No funds for activation - Please contact support - Four identical digits isn\'t safe - Such a PIN can be brute-forced easily - Pin code - Connect - KYC - Verify your identity - Set PIN code - Register - Verify via Utorg - Refresh - Connect your card - Verify your identity - KYC is in progress - Connect your card to the decentralized payment system - To start using your card you have to pass the KYC process - Please wait until the verification is completed. Usually it takes up to 1 hour. You can close the app and come back later. - PIN Code - Set PIN code for your SaltPay card - Chat with support - Please hold the card until the operation complete - To start the backup process you have to add Tangem card as your backup - No backup card - Backup card ready - Finalize the backup process by creating an access code - Prepare the SaltPay card - Tap the SaltPay card - Tap the Tangem card - Support - Claim %s - To get started, simply claim wxDAI to your wallet - Claim - Congratulations! Your first payment crypto card has been activated - Claiming - It will take a few seconds - Something went wrong - Please check you email for further instructions - Do you want to exit the activation process? - In this case, you will need to start from the beginning. - You have used a card from another wallet. Tap the card associated with this wallet - Referral program - Refer your friends to Tangem - You - Will get - for each wallet bought by your friend on your %s network address%s - Your friend - Will get a - %s discount - when buying a card on tangem.com - Your friends bought - Your personal code - Participate - terms and conditions - By tapping this button you accept - You\'ve accepted - of the referral program - Internal error: wallet manager not found - Solde : %s - Share - Copy - Avec succès - Give Permission - To continue you need to allow 1inch smart contracts to use your %s - Amount %s - Your Wallet - Spender - Approve - Swap of %s to - Swap - Insufficient funds - Give Permission - Permit and Swap - Failed to load the information about the referral program. Please try again later. - Failed to load the information about the referral program. Reason: %s. Please try again later. - Your participation request could not be processed. Reason: %s. Please try again later. If the problem persists — feel free to contact our support. - Personal code copied! - Buy Tangem Wallet with discount!\n%s - %s network has a concept of Existential Deposit. If your account drops below %s it will be deactivated and any remaining funds will be destroyed. - Adresse incorrecte - This wallet has already been saved, you can add another one - Supprimer - Privacy policy - Enable biometric authorization - It looks like you have biometric authentication disabled, it is necessary to save wallets - Enable - Save your Wallet feature allows you to use your wallet with biometric auth without tapping your card to the phone to gain access. - %d selected - biometric authentication - biometrics - Enable biometric authentication - Go to settings to enable biometric authentication in the Tangem App - Scan the card - diff --git a/core/res/src/main/res/values-fr/strings_plurals_final.xml b/core/res/src/main/res/values-fr/strings_plurals.xml similarity index 100% rename from core/res/src/main/res/values-fr/strings_plurals_final.xml rename to core/res/src/main/res/values-fr/strings_plurals.xml diff --git a/core/res/src/main/res/values-it/strings-app.xml b/core/res/src/main/res/values-it/strings.xml similarity index 51% rename from core/res/src/main/res/values-it/strings-app.xml rename to core/res/src/main/res/values-it/strings.xml index c5e3134366..d6b0c63ee0 100644 --- a/core/res/src/main/res/values-it/strings-app.xml +++ b/core/res/src/main/res/values-it/strings.xml @@ -1,5 +1,223 @@ - + + Tangem Wallet + 3 cards + 2 cards + Shipping + Free + I have a promo code… + Total + Other payment methods + Buy now + Meet\nTangem + Buy + Store + Send + Pay + Exchange + Lend + Borrow + Revolutionary Hardware Wallet + Store your crypto assets secure while keeping private keys contained in your card + Ultra Secure Backup + Up to + 3 physical cards + to one wallet + Thousands of Currencies + A hardware wallet for your Bitcoin, Ethereum and many more currencies simultaneously – all in one card + DeFi Compatible + Exchange, buy NFT\'s, make loans and deposits in more than 100 different decentralized services + The Wallet for Everyone + Use it on the go, anywhere, anytime. No wires or batteries. Just tap the card to your phone when you need your crypto. + Order + Search tokens + You are currently running in Demo mode. All funds are not real. + This feature is disabled in Demo mode + Not enough funds for fee on your %s wallet to send a transaction. Top up your %s wallet first. + Available networks + %s network not found. Please, add it first and try again. + Attention + Note that tokens on different networks have different addresses. Double check that your address matches the network when you transfer funds. + Tokens in Solana network are not supported by this card due to firmware limitation. + Contract address copied! + Contract address + Required field + Please select the network + Decimal number must be a valid integer, no higher than %d + Contract address is invalid + Derivation path is invalid + Note that tokens can be created by anyone. Be aware of adding scam tokens, they can cost nothing. + This token/network has already been added to your list + Decimals + Network + Not selected + E.g. USD Coin + Name + E.g. USDC + Token symbol + BIP44 coin type + Default + The server is not available, please try again later + Total balance + The amount does not include some of your funds + Tokens + Manage tokens + No rate + Network is unreachable + Hide token + Hide %s + Hide + You are about to hide this token from the main screen. You can add it back anytime through the manage tokens page. + Unable to hide %s + The %s token is the main currency on the %s network and cannot be hidden as long as you have other tokens on this network in the list. + Your wallet has not been backed up + To protect your assets, we advise you to carry out this procedure + Riprova + Chat + %s network + I understand + Yes + No + Russian bank cards are not accepted at the moment + Do you have a bank card of another country or a UnionPay card? + This network is not supported. Please select another network. + Card Settings + Security Mode + Change Access Code + Access code will be changed on this card only + Continue + Tangem Bot + Get your card ready! + Scan the card to change its settings. The changes will impact only the card you\'ve scanned and will not affect other cards tied to your wallet. + Scan Card + App Settings + Keep the wallet in the app + Enable to link all the wallets to Tangem app. Biometric authentication will be required for unlocking the app. Transaction signing requires tapping your Tangem card. + Save Access Code + Biometric authentication will be requested instead of the access code for interactions with your card. + Removing the saved card deletes all the saved wallets and their access codes. + This will delete all the saved wallet access codes. Any further operation with the wallet will require submitting the access code. + WalletConnect + Connect to Dapps + Factory Reset will completely delete the wallet from the selected card and remove it from the app. You will not be able to restore the current wallet. + Factory Reset will completely delete the wallet from the selected card and remove it from the app. You will not be able to restore the current wallet or use the card to recover the access code. + I understand that after performing this action, I will no longer have access to the current wallet + Reset the Card + Reset to Factory Settings + Select network + Scan your card + To access all the networks you need to scan the card + Connection with this Dapp cannot be established due to its technical implementation. + Welcome back! + Use %s or scan a card to access the app + Log in with %s + Scansiona carta + Access the app + Log into the app and check your balance without scanning the card + Access code + Note that making a transaction with your funds will still require your card + My Wallets + Multi-currency + Single-currency + Add new wallet + Rename Wallet + Wallet name + Unlock all with %s + Invalid Tag. It won\'t be added to the transaction. + Invalid Memo. It won\'t be added to the transaction. + Tag + Memo + Attention + Tap the card with the visa logo + No funds for activation + Please contact support + Four identical digits isn\'t safe + Such a PIN can be brute-forced easily + Pin code + Connect + KYC + Verify your identity + Set PIN code + Register + Verify via Utorg + Refresh + Connect your card + Verify your identity + KYC is in progress + Connect your card to the decentralized payment system + To start using your card you have to pass the KYC process + Please wait until the verification is completed. You\'ll be notified via email. Usually it takes up to 1 hour. You can close the app and come back later. + PIN Code + Set PIN code for your SaltPay card + Chat with support + Please hold the card until the operation complete + To start the backup process you have to add Tangem card as your backup + No backup card + Backup card ready + Finalize the backup process by creating an access code + Prepare the SaltPay card + Tap the SaltPay card + Tap the Tangem card + Support + Claim %s + To get started, simply claim wxDAI to your wallet + Claim + Congratulations! Your first payment crypto card has been activated + Claiming + It will take a few seconds + Something went wrong + Please check your email for further instructions + Do you want to exit the activation process? + In this case, you will need to start from the beginning. + You have used a card from another wallet. Tap the card associated with this wallet + Referral program + Refer your friends to Tangem + You + Will get + for each wallet bought by your friend on your %s network address%s + Your friend + Will get a + %s discount + when buying a card on tangem.com + Your friends bought + Your personal code + Participate + terms and conditions + By tapping this button you accept + You\'ve accepted + of the referral program + Internal error: wallet manager not found + Saldo: %s + Share + Copy + Con successo + Give Permission + To continue you need to allow 1inch smart contracts to use your %s + Amount %s + Your Wallet + Spender + Approve + Swap of %s to + Swap + Insufficient funds + Give Permission + Permit and Swap + Failed to load the information about the referral program. Please try again later. + Failed to load the information about the referral program. Reason: %s. Please try again later. + Your participation request could not be processed. Reason: %s. Please try again later. If the problem persists — feel free to contact our support. + Personal code copied! + Buy Tangem Wallet with discount!\n%s + %s network has a concept of Existential Deposit. If your account drops below %s it will be deactivated and any remaining funds will be destroyed. + Indirizzo non valido + This wallet has already been saved, you can add another one + Rimuovere + Privacy policy + Enable biometric authorization + It looks like you have biometric authentication disabled, it is necessary to save wallets + Enable + %d selected + biometric authentication + biometrics Tangem Mantieni le modifiche Avviso @@ -61,7 +279,7 @@ Questa carta non è progettata per funzionare con Tangem La carta che hai scansionato è una carta di sviluppo. Non utilizzarla come strumento di pagamento. Avvicina per firmare - To create wallet, connect your phone and the card exactly as it shown above + To create the wallet tap the card as shown above and do not remove until the end of the operation Avvicina per modificare il codice di accesso Avvicina per modificare la password Termini del servizio @@ -97,7 +315,6 @@ Can\'t send a transaction Reason: %s Are you having difficulty scanning your card? - I\'m okay Request support Send feedback Really cool! @@ -133,8 +350,7 @@ Success! Your card is activated and ready to be used Balance - Send only %s (%s) to this address. Using other tokens and networks may result in loss of funds. - Send only %s (%s) from %s network to this address. Using other tokens and networks may result in loss of funds. + Send only %s (%s) from %s network to this address. Using other tokens and networks may result in loss of funds. If the process of creating the wallet gets interrupted in any way, you\'ll have to start over. The twinning process is partly complete. You can\'t exit it now. Ok, ho capito! @@ -217,7 +433,7 @@ Tangem feedback Feedback Tell us what functions you are missing, and we will try to help you. - Please tell us what card do you have? + Please tell us what card do you have Please tell us more about your issue. Every small detail can help. Hi support team, The following information is optional. You can erase it if you don\'t want to share it. @@ -227,6 +443,12 @@ Errore OK Failed to establish WalletConnect session. Please, try again later. + Would you like to use biometrics? + Biometrics will be requested instead of the access code for interactions with your wallet + Allow to use biometrics + Enable biometric authentication + Go to settings to enable biometric authentication in the Tangem App + Scan the card %s diff --git a/core/res/src/main/res/values-it/strings_final.xml b/core/res/src/main/res/values-it/strings_final.xml deleted file mode 100644 index 53bd5f260c..0000000000 --- a/core/res/src/main/res/values-it/strings_final.xml +++ /dev/null @@ -1,233 +0,0 @@ - - - Tangem Wallet - 3 cards - 2 cards - Shipping - Free - I have a promo code… - Total - Other payment methods - Buy now - Meet\nTangem - Buy - Store - Send - Pay - Exchange - Lend - Borrow - Revolutionary Hardware Wallet - Store your crypto assets secure while keeping private keys contained in your card - Ultra Secure Backup - Up to - 3 physical cards - to one wallet - Thousands of Currencies - A hardware wallet for your Bitcoin, Ethereum and many more currencies simultaneously – all in one card - DeFi Compatible - Exchange, buy NFT\'s, make loans and deposits in more than 100 different decentralized services - The Wallet for Everyone - Use it on the go, anywhere, anytime. No wires or batteries. Just tap the card to your phone when you need your crypto. - Order - Search tokens - You are currently running in Demo mode. All funds are not real. - This feature is disabled in Demo mode - Not enough funds for fee on your %s wallet to send a transaction. Top up your %s wallet first. - Available networks - %s network not found. Please, add it first and try again. - Attention - Note that tokens on different networks have different addresses. Double check that your address matches the network when you transfer funds. - Tokens in Solana network are not supported by this card due to firmware limitation. - Contract address copied! - Contract address - Required field - Please select the network - Decimal number must be a valid integer, no higher than %d - Contract address is invalid - Derivation path is invalid - Note that tokens can be created by anyone. Be aware of adding scam tokens, they can cost nothing. - This token/network has already been added to your list - Decimals - Network - Not selected - E.g. USD Coin - Name - E.g. USDC - Token symbol - BIP44 coin type - Default - The server is not available, please try again later - Total balance - The amount does not include some of your funds - Tokens - Manage tokens - No rate - Network is unreachable - Hide token - Hide %s - Hide - You are about to hide this token from the main screen. You can add it back anytime through the manage tokens page. - Unable to hide %s - The %s token is the main currency on the %s network and cannot be hidden as long as you have other tokens on this network in the list. - Your wallet has not been backed up - To protect your assets, we advise you to carry out this procedure - Riprova - Chat - %s network - I understand - Yes - No - Russian bank cards are not accepted at the moment - Do you have a bank card of another country or a UnionPay card? - This network is not supported. Please select another network. - Card Settings - Security Mode - Change access code - Access code will be changed on this card only - Continue - Tangem Bot - Get your card ready! - Scan the card to change its settings. The changes will impact only the card you\'ve scanned and will not affect other cards tied to your wallet. - Scan Card - App Settings - Keep the wallet in the app - Enable to link all the wallets to Tangem app. Biometric authentication will be required for unlocking the app. Transaction signing requires tapping your Tangem card. - Save Access Code - Biometric authentication will be requested instead of the access code for interactions with your card. - Removing the saved card deletes all the saved wallets and their access codes. - This will delete all the saved wallet access codes. Any further operation with the wallet will require submitting the access code. - WalletConnect - Connect to Dapps - This action will completely remove the wallet from the selected card. You will not be able to restore the current wallet or use the card to recover the access code. - I understand that after performing this action, I will no longer have access to the current wallet - Reset the card - Reset to Factory Settings - Factory Reset will completely delete the wallet from the selected card. You will not be able to restore the current wallet or use the card to recover the access code. - Select network - Scan your card - To access all the networks you need to scan the card - Connection with this Dapp cannot be established due to its technical implementation. - Welcome back! - Use %s or scan a card to access the app - Log in with %s - Scansiona carta - Save your Wallet - Would you like to use %s? - Would you like to use biometrics? - Access the app - Log into the app and check your balance without scanning the card - Access code - %s will be requested instead of the access code for interactions with your wallet - Biometrics will be requested instead of the access code for interactions with your wallet - Note that making a transaction with your funds will still require your card - Allow to use %s - Allow to use biometrics - New feature - My Wallets - Multi-currency - Single-currency - Add new wallet - Rename Wallet - Wallet name - Unlock all with %s - Invalid Tag. It won\'t be added to the transaction. - Invalid Memo. It won\'t be added to the transaction. - Tag - Memo - Attention - Tap the card with the visa logo - No funds for activation - Please contact support - Four identical digits isn\'t safe - Such a PIN can be brute-forced easily - Pin code - Connect - KYC - Verify your identity - Set PIN code - Register - Verify via Utorg - Refresh - Connect your card - Verify your identity - KYC is in progress - Connect your card to the decentralized payment system - To start using your card you have to pass the KYC process - Please wait until the verification is completed. Usually it takes up to 1 hour. You can close the app and come back later. - PIN Code - Set PIN code for your SaltPay card - Chat with support - Please hold the card until the operation complete - To start the backup process you have to add Tangem card as your backup - No backup card - Backup card ready - Finalize the backup process by creating an access code - Prepare the SaltPay card - Tap the SaltPay card - Tap the Tangem card - Support - Claim %s - To get started, simply claim wxDAI to your wallet - Claim - Congratulations! Your first payment crypto card has been activated - Claiming - It will take a few seconds - Something went wrong - Please check you email for further instructions - Do you want to exit the activation process? - In this case, you will need to start from the beginning. - You have used a card from another wallet. Tap the card associated with this wallet - Referral program - Refer your friends to Tangem - You - Will get - for each wallet bought by your friend on your %s network address%s - Your friend - Will get a - %s discount - when buying a card on tangem.com - Your friends bought - Your personal code - Participate - terms and conditions - By tapping this button you accept - You\'ve accepted - of the referral program - Internal error: wallet manager not found - Saldo: %s - Share - Copy - Con successo - Give Permission - To continue you need to allow 1inch smart contracts to use your %s - Amount %s - Your Wallet - Spender - Approve - Swap of %s to - Swap - Insufficient funds - Give Permission - Permit and Swap - Failed to load the information about the referral program. Please try again later. - Failed to load the information about the referral program. Reason: %s. Please try again later. - Your participation request could not be processed. Reason: %s. Please try again later. If the problem persists — feel free to contact our support. - Personal code copied! - Buy Tangem Wallet with discount!\n%s - %s network has a concept of Existential Deposit. If your account drops below %s it will be deactivated and any remaining funds will be destroyed. - Indirizzo non valido - This wallet has already been saved, you can add another one - Rimuovere - Privacy policy - Enable biometric authorization - It looks like you have biometric authentication disabled, it is necessary to save wallets - Enable - Save your Wallet feature allows you to use your wallet with biometric auth without tapping your card to the phone to gain access. - %d selected - biometric authentication - biometrics - Enable biometric authentication - Go to settings to enable biometric authentication in the Tangem App - Scan the card - diff --git a/core/res/src/main/res/values-ru/strings-app.xml b/core/res/src/main/res/values-ru/strings.xml similarity index 50% rename from core/res/src/main/res/values-ru/strings-app.xml rename to core/res/src/main/res/values-ru/strings.xml index 34b9be72db..177687671b 100644 --- a/core/res/src/main/res/values-ru/strings-app.xml +++ b/core/res/src/main/res/values-ru/strings.xml @@ -1,5 +1,223 @@ - + + Tangem Wallet + 3 карты + 2 карты + Доставка + Бесплатно + У меня есть промо-код… + Итого + Другие способы оплаты + Купить сейчас + Встречайте\nTangem + Покупайте + Храните + Отправляйте + Расплачивайтесь + Обменивайте + Вкладывайте + Занимайте + Революционный аппаратный кошелек + Держите свои криптосбережения в безопасности. Приватные ключи надежно хранятся на карте. + Все ключи в безопасности + До + трех карт + с одним кошельком + Тысячи криптовалют + Аппаратный кошелек для ваших биткоинов, эфира и многих других валют одновременно — все в одной карте + Поддержка DeFi + Обменивайте, покупайте NFT, получайте займы и делайте вклады в более чем 100 различных децентрализованных сервисах + Кошелек для каждого + Используйте его на ходу, в любом месте, в любое время. Без проводов и батареек. Как только понадобится крипта, просто приложите карту к телефону. + Купить + Поиск токенов + Приложение работает в демонстрационном режиме. Средства на всех счетах ненастоящие. + Эта функция недоступна в демонстрационном режиме + Недостаточно средств для комиссии на вашем %s кошельке для отправки транзакции. Сначала пополните свой %s кошелек. + Доступные сети + Сеть %s не найдена. Пожалуйста, добавьте её и попробуйте заново. + Внимание + Внимание! Валюты на разных сетях имеют разные адреса. Убедитесь, что адрес соответствует сети, в которой вы отправляете средства. + Токены в сети Solana не поддерживаются этой картой из-за ограничений прошивки. + Адрес контракта скопирован! + Адрес контракта + Обязательное поле + Пожалуйста, выберите сеть + Количество знаков после запятой должно быть корректным числом не больше %d + Адрес контракта некорректен + Путь деривации некорректен + Токены могут быть созданы кем угодно. Остерегайтесь мошеннических токенов, они могут ничего не стоить + Этот токен/сеть уже находится в вашем списке + Знаков после запятой + Сеть + Не выбрано + Например, USD Coin + Название токена + Например, USDC + Символ токена + Деривация по BIP44 + По-умолчанию + Сервер недоступен, повторите попытку позднее + Баланс + В сумме учтены не все монеты + Токены + Управление токенами + Нет цены + Сеть недоступна + Скрыть токен + Скрыть %s + Скрыть + Вы скрываете токен с главного экрана, но в любой момент сможете добавить его обратно через страницу управления токенами. + Невозможно скрыть %s + Токен %s является основной валютой в сети %s и не может быть скрыт до тех пор, пока у вас в списке есть другие токены этой сети. + Бэкап кошелька не был произведен + Чтобы защитить свои активы, мы советуем вам выполнить эту процедуру + Повторить + Чат + Сеть %s + Я понял + Да + Нет + Карты банков РФ в данный момент не принимаются + У вас есть карта банка другой страны или платежной системы UnionPay? + Сеть не поддерживается. Пожалуйста, выберите другую сеть. + Настройки карты + Тип безопасности + Смена кода доступа + Код доступа будет изменен только на данной карте + Продолжить + Tangem Bot + Приготовьте свою карту + Отсканируйте карту, чтобы изменить ее настройки. Изменения затронут только ту карту, которую вы отсканировали, и не повлияют на другие карты, привязанные к вашему кошельку. + Сканировать + Настройки приложения + Cохранение кошелька + Подключите функцию привязки карты в приложении, а также возможность биометрической аутентификации. Подпись транзакции все так же потребует карту. + Сохранение кода доступа + Подключите функцию хранения кодов доступа от карт на телефоне в зашифрованном виде, и при работе с картой вместо кода доступа будет запрашиваться биометрическая аутентификация. + При отключении функции сохранения кошелька все ранее сохраненные кошельки будут удалены. + Все сохраненные коды доступа будут удалены. Вам потребуется вводить код доступа при работе с кошельком. + WalletConnect + Подключение к Dapps + Сброс к заводским настройкам приведет к полному удалению кошелька на этой карте, а также отвязыванию карты из приложения. Кошелек невозможно будет восстановить. + Сброс к заводским настройкам приведет к полному удалению кошелька на этой карте, а также отвязыванию карты из приложения. Кошелек невозможно будет восстановить или использовать данную карту для восстановления кода доступа. + Я понимаю, что после выполнения этого действия у меня больше не будет доступа к текущему кошельку + Сбросить карту + Сброс к заводским настройкам + Выберите сеть + Отсканируйте карту + Чтобы получить доступ ко всем сетям, вам необходимо отсканировать карту + Cоединение с этим Dapp сервисом не может быть установлено из-за его технической реализации. + C возвращением! + Используйте %s или код доступа для входа в приложение + Войти с %s + Сканировать карту + Доступ в приложение + Войдите в приложение и следите за своим балансом без сканирования карты + Код доступа + Обратите внимание, что для совершения транзакции с вашими средствами по-прежнему потребуется ваша карта + Мои кошельки + Мультивалютные + Одновалютные + Добавить новый кошелек + Переименование кошелька + Имя кошелька + Разблокировать все с %s + Недопустимый Tag. Он не будет добавлен в транзакцию. + Недопустимый Memo. Он не будет добавлен в транзакцию. + Tag + Memo + Внимание + Приложите карту с логотипом Visa + Недостаточно средств для активации + Пожалуйста обратитесь в службу поддержки + Ввод одинаковых цифр является не безопасным + Данный Код доступа может быть легко взломан + Код доступа + Подключиться + Верификация клиента + Подтвердите свою личность + Установить Код доступа + Зарегистрироваться + Верифицировать (Utorg) + Обновить + Подключите свою карту + Подтвердите свою личность + Подтверждение личности в процессе + Подключите вашу карту к децентрализованной платежной системе + Для начала работы с картой вам необходимо завершить процесс подтверждения личности + Пожалуйста дождитесь завершения процесса подтверждения личности. Вы будете уведомлены через e-mail. Обычно это занимает не более часа. Вы можете закрыть приложение и вернуться позже. + Код доступа + Установите Код доступа для вашей SaltPay карты + Чат поддержки + Пожалуйста, удерживайте карту до завершения операции + Для начала процесса бэкапа вам необходимо добавить Tangem карту + Бэкап карта не добавлена + Бэкап карта создана + Завершите процесс бэкапа создав код доступа + Приготовьте SaltPay карту + Приложите SaltPay карту + Приложите Tangem карту + Чат + Запросить %s + Для начала работы просто запросите начисление wxDai на свой кошелек + Запросить + Поздравляем! Ваша платежная крипто карта теперь активирована! + Запрашивается + Это займет несколько секунд + Что-то пошло не так + Более подробная информация отправлена на ваш адрес электронной почты. + Вы хотите выйти из процесса активации? + В этом случае вам будет необходимо начать процесс заново. + Вы использовали карту от другого кошелька. Приложите карту, связанную с этим кошельком. + Реферальная программа + Приведи друга в Tangem + Вы + Получите + на ваш адрес в сети %s%s за каждый кошелек, который купит ваш друг + Ваш друг + Получит + %s скидку + при покупке карточки на сайте tangem.com + Ваши друзья купили + Ваш персональный код + Участвовать + условия участия + Нажимая на эту кнопку вы принимаете + Вы приняли + в реферальной программе + Внутренняя ошибка: не удается найти менеджер кошельков + Баланс: %s + Поделиться + Копировать + Успешно + Дать разрешение + Чтобы продолжить, вам нужно разрешить смарт-контракту 1inch использовать ваш %s + Количество %s + Ваш кошелек + Отправитель + Подтвердить + Обмен %s на + Обмен + Недостаточно средств + Дать разрешение + Разрешить и обменять + Не удалось загрузить информацию по реферальной программе. Пожалуйста, попробуйте позже. + Не удалось загрузить информацию по реферальной программе. Причина: %s. Пожалуйста, попробуйте позже. + Не удалось обработать вашу заявку на участие. Причина: %s. Пожалуйста, попробуйте позже. Если проблема сохранится, вы можете обратиться в техподдержку. + Персональный код скопирован! + Купи Tangem Wallet со скидкой!\n%s + Сеть %s использует концепцию экзистенциального депозита. Если баланс вашего счета опустится ниже %s, он будет деактивирован, а все оставшиеся средства будут уничтожены. + Неверный адрес + Этот кошелек уже был сохранен, вы можете добавить другой + Удалить + Privacy policy + Включите биометрическую аутентификацию + Похоже, что у вас отключена биометрическая аутентификация, она необходима для сохранения кошельков + Включить + %d выбрано + биометрическую аутентификацию + биометрией Tangem Сохранить изменения Предупреждение @@ -61,9 +279,9 @@ Эта карта не предназначена для работы с этим приложением Карта, которую вы отсканировали, является картой разработчика. Не принимайте её в качестве оплаты. Нажмите, чтобы подписать - Чтобы создать кошелек, соедините телефон и карту в точности, как показано выше. - Чтобы изменить код доступа, соедините телефон и карту в точности, как показано выше. - Чтобы изменить пароль, соедините телефон и карту в точности, как показано выше. + Чтобы создать кошелек, приложите карту как показано выше и не убирайте до окончания операции + Чтобы изменить код доступа, приложите карту как показано выше и не убирайте до окончания операции + Чтобы изменить пароль, приложите карту как показано выше и не убирайте до окончания операции Условия использования Нет соединения с интернетом PayString не поддерживается блокчейном @@ -97,7 +315,6 @@ Не могу отправить транзакцию Причина: %s У вас возникли трудности со сканированием карты? - Отмена Обратиться в поддержку Отправить отзыв Очень круто! @@ -133,8 +350,7 @@ Успешно! Ваша карта активирована и готова к использованию Баланс - Отправляйте только %s (%s) на этот адрес. Иначе это может привести к утрате средств. - Отправляйте только %s (%s) из сети %s на этот адрес. Иначе это может привести к утрате средств. + Отправляйте только %s (%s) в сети %s на этот адрес. Использование другой сети может привести к утрате средств. Если процесc создания кошелька каким-либо образом прервется, вам придется начинать сначала. Процесс связывания карт частично завершен. Вы не можете выйти из него сейчас. Понятно! @@ -227,6 +443,12 @@ Ошибка Ок Не удалось установить сессию WalletConnect. Пожалуйста, повторите попытку позже. + Вы хотите использовать биометрию? + Для операций с вашим кошельком будет запрашиваться биометрия вместо кода доступа карты + Использовать биометрию + Включите биометрическую аутентификацию + Перейдите в настройки, чтобы включить биометрическую аутентификацию в приложении Tangem App + Отсканируйте карту %s diff --git a/core/res/src/main/res/values-ru/strings_final.xml b/core/res/src/main/res/values-ru/strings_final.xml deleted file mode 100644 index c56fea1dcc..0000000000 --- a/core/res/src/main/res/values-ru/strings_final.xml +++ /dev/null @@ -1,233 +0,0 @@ - - - Tangem Wallet - 3 карты - 2 карты - Доставка - Бесплатно - У меня есть промо-код… - Итого - Другие способы оплаты - Купить сейчас - Встречайте\nTangem - Покупайте - Храните - Отправляйте - Расплачивайтесь - Обменивайте - Вкладывайте - Занимайте - Революционный аппаратный кошелек - Держите свои криптосбережения в безопасности. Приватные ключи надежно хранятся на карте. - Все ключи в безопасности - До - трех карт - с одним кошельком - Тысячи криптовалют - Аппаратный кошелек для ваших биткоинов, эфира и многих других валют одновременно — все в одной карте - Поддержка DeFi - Обменивайте, покупайте NFT, получайте займы и делайте вклады в более чем 100 различных децентрализованных сервисах - Кошелек для каждого - Используйте его на ходу, в любом месте, в любое время. Без проводов и батареек. Как только понадобится крипта, просто приложите карту к телефону. - Купить - Поиск токенов - Приложение работает в демонстрационном режиме. Средства на всех счетах ненастоящие. - Эта функция недоступна в демонстрационном режиме - Недостаточно средств для комиссии на вашем %s кошельке для отправки транзакции. Сначала пополните свой %s кошелек. - Доступные сети - Сеть %s не найдена. Пожалуйста, добавьте её и попробуйте заново. - Внимание - Внимание! Валюты на разных сетях имеют разные адреса. Убедитесь, что адрес соответствует сети, в которой вы отправляете средства. - Токены в сети Solana не поддерживаются этой картой из-за ограничений прошивки. - Адрес контракта скопирован! - Адрес контракта - Обязательное поле - Пожалуйста, выберите сеть - Количество знаков после запятой должно быть корректным числом не больше %d - Адрес контракта некорректен - Путь деривации некорректен - Токены могут быть созданы кем угодно. Остерегайтесь мошеннических токенов, они могут ничего не стоить - Этот токен/сеть уже находится в вашем списке - Знаков после запятой - Сеть - Не выбрано - Например, USD Coin - Название токена - Например, USDC - Символ токена - Деривация по BIP44 - По-умолчанию - Сервер недоступен, повторите попытку позднее - Баланс - В сумме учтены не все монеты - Токены - Управление токенами - Нет цены - Сеть недоступна - Скрыть токен - Скрыть %s - Скрыть - Вы скрываете токен с главного экрана, но в любой момент сможете добавить его обратно через страницу управления токенами. - Невозможно скрыть %s - Токен %s является основной валютой в сети %s и не может быть скрыт до тех пор, пока у вас в списке есть другие токены этой сети. - Бэкап кошелька не был произведен - Чтобы защитить свои активы, мы советуем вам выполнить эту процедуру - Повторить - Чат - Сеть %s - Я понял - Да - Нет - Карты банков РФ в данный момент не принимаются - У вас есть карта банка другой страны или платежной системы UnionPay? - Сеть не поддерживается. Пожалуйста, выберите другую сеть. - Настройки карты - Тип безопасности - Смена кода доступа - Код доступа будет изменен только на данной карте - Продолжить - Tangem Bot - Приготовьте свою карту - Отсканируйте карту, чтобы изменить ее настройки. Изменения затронут только ту карту, которую вы отсканировали, и не повлияют на другие карты, привязанные к вашему кошельку. - Сканировать - Настройки приложения - Cохранение кошелька - Подключите функцию привязки карты в приложении, а также возможность биометрической аутентификации. Подпись транзакции все так же потребует карту. - Сохранение кода доступа - Подключите функцию хранения кодов доступа от карт на телефоне в зашифрованном виде, и при работе с картой вместо кода доступа будет запрашиваться биометрическая аутентификация. - При отключении функции сохранения кошелька все ранее сохраненные кошельки будут удалены. - Все сохраненные коды доступа будут удалены. Вам потребуется вводить код доступа при работе с кошельком. - WalletConnect - Подключение к Dapps - Это действие приведет к полному удалению кошелька на этой карте. Кошелек невозможно будет восстановить или использовать данную карту для восстановления кода доступа - Я понимаю, что после выполнения этого действия у меня больше не будет доступа к текущему кошельку - Сбросить карту - Сброс к заводским настройкам - Сброс к заводским настройкам приведет к полному удалению кошелька с выбранной карты. Вы не сможете восстановить текущий кошелек или использовать эту карту для восстановления кода доступа. - Выберите сеть - Отсканируйте карту - Чтобы получить доступ ко всем сетям, вам необходимо отсканировать карту - Cоединение с этим Dapp сервисом не может быть установлено из-за его технической реализации. - C возвращением! - Используйте %s или код доступа для входа в приложение - Войти с %s - Сканировать карту - Сохраните ваш кошелек - Вы хотите использовать %s? - Вы хотите использовать биометрию? - Доступ в приложение - Войдите в приложение и следите за своим балансом без сканирования карты - Код доступа - Для операций с вашим кошельком будет запрашиваться %s вместо кода доступа карты - Для операций с вашим кошельком будет запрашиваться биометрия вместо кода доступа карты - Обратите внимание, что для совершения транзакции с вашими средствами по-прежнему потребуется ваша карта - Использовать %s - Использовать биометрию - Новый функционал - Мои кошельки - Мультивалютные - Одновалютные - Добавить новый кошелек - Переименование кошелька - Имя кошелька - Разблокировать все с %s - Недопустимый Tag. Он не будет добавлен в транзакцию. - Недопустимый Memo. Он не будет добавлен в транзакцию. - Tag - Memo - Внимание - Приложите карту с логотипом Visa - Недостаточно средств для активации - Пожалуйста обратитесь в службу поддержки - Ввод одинаковых цифр является не безопасным - Данный Код доступа может быть легко взломан - Код доступа - Подключиться - Верификация клиента - Подтвердите свою личность - Установить Код доступа - Зарегистрироваться - Верифицировать (Utorg) - Обновить - Подключите свою карту - Подтвердите свою личность - Подтверждение личности в процессе - Подключите вашу карту к децентрализованной платежной системе - Для начала работы с картой вам необходимо завершить процесс подтверждения личности - Пожалуйста дождитесь завершения процесса подтверждения личности. Вы будете уведомлены через e-mail. Обычно это занимает не более часа. Вы можете закрыть приложение и вернуться позже. - Код доступа - Установите Код доступа для вашей SaltPay карты - Чат поддержки - Пожалуйста, удерживайте карту до завершения операции - Для начала процесса бэкапа вам необходимо добавить Tangem карту - Бэкап карта не добавлена - Бэкап карта создана - Завершите процесс бэкапа создав код доступа - Приготовьте SaltPay карту - Приложите SaltPay карту - Приложите Tangem карту - Чат - Запросить %s - Для начала работы просто запросите начисление wxDai на свой кошелек - Запросить - Поздравляем! Ваша платежная крипто карта теперь активирована! - Запрашивается - Это займет несколько секунд - Что-то пошло не так - Более подробная информация отправлена на ваш адрес электронной почты. - Вы хотите выйти из процесса активации? - В этом случае вам будет необходимо начать процесс заново. - Вы использовали карту от другого кошелька. Приложите карту, связанную с этим кошельком. - Реферальная программа - Приведи друга в Tangem - Вы - Получите - на ваш адрес в сети %s%s за каждый кошелек, который купит ваш друг - Ваш друг - Получит - %s скидку - при покупке карточки на сайте tangem.com - Ваши друзья купили - Ваш персональный код - Участвовать - условия участия - Нажимая на эту кнопку вы принимаете - Вы приняли - в реферальной программе - Внутренняя ошибка: не удается найти менеджер кошельков - Баланс: %s - Поделиться - Копировать - Успешно - Дать разрешение - Чтобы продолжить, вам нужно разрешить смарт-контракту 1inch использовать ваш %s - Количество %s - Ваш кошелек - Отправитель - Подтвердить - Обмен %s на - Обмен - Недостаточно средств - Дать разрешение - Разрешить и обменять - Не удалось загрузить информацию по реферальной программе. Пожалуйста, попробуйте позже. - Не удалось загрузить информацию по реферальной программе. Причина: %s. Пожалуйста, попробуйте позже. - Не удалось обработать вашу заявку на участие. Причина: %s. Пожалуйста, попробуйте позже. Если проблема сохранится, вы можете обратиться в техподдержку. - Персональный код скопирован! - Купи Tangem Wallet со скидкой!\n%s - Сеть %s использует концепцию экзистенциального депозита. Если баланс вашего счета опустится ниже %s, он будет деактивирован, а все оставшиеся средства будут уничтожены. - Неверный адрес - Этот кошелек уже был сохранен, вы можете добавить другой - Удалить - Privacy policy - Включите биометрическую аутентификацию - Похоже, что у вас отключена биометрическая аутентификация, она необходима для сохранения кошельков - Включить - Функция сохранения кошелька позволяет вам использовать свой кошелек с биометрической аутентификацией, не прикладывая карту к телефону для получения доступа. - %d выбрано - биометрическую аутентификацию - биометрией - Включите биометрическую аутентификацию - Перейдите в настройки, чтобы включить биометрическую аутентификацию в приложении Tangem App - Отсканируйте карту - diff --git a/core/res/src/main/res/values/strings-app.xml b/core/res/src/main/res/values/strings.xml similarity index 50% rename from core/res/src/main/res/values/strings-app.xml rename to core/res/src/main/res/values/strings.xml index 4d8976d15d..f2d00c18af 100644 --- a/core/res/src/main/res/values/strings-app.xml +++ b/core/res/src/main/res/values/strings.xml @@ -1,5 +1,223 @@ - + + Tangem Wallet + 3 cards + 2 cards + Shipping + Free + I have a promo code… + Total + Other payment methods + Buy now + Meet\nTangem + Buy + Store + Send + Pay + Exchange + Lend + Borrow + Revolutionary Hardware Wallet + Store your crypto assets secure while keeping private keys contained in your card + Ultra Secure Backup + Up to + 3 physical cards + to one wallet + Thousands of Currencies + A hardware wallet for your Bitcoin, Ethereum and many more currencies simultaneously – all in one card + DeFi Compatible + Exchange, buy NFT\'s, make loans and deposits in more than 100 different decentralized services + The Wallet for Everyone + Use it on the go, anywhere, anytime. No wires or batteries. Just tap the card to your phone when you need your crypto. + Order + Search tokens + You are currently running in Demo mode. All funds are not real. + This feature is disabled in Demo mode + Not enough funds for fee on your %s wallet to send a transaction. Top up your %s wallet first. + Available networks + %s network not found. Please, add it first and try again. + Attention + Note that tokens on different networks have different addresses. Double check that your address matches the network when you transfer funds. + Tokens in Solana network are not supported by this card due to firmware limitation. + Contract address copied! + Contract address + Required field + Please select the network + Decimal number must be a valid integer, no higher than %d + Contract address is invalid + Derivation path is invalid + Note that tokens can be created by anyone. Be aware of adding scam tokens, they can cost nothing. + This token/network has already been added to your list + Decimals + Network + Not selected + E.g. USD Coin + Name + E.g. USDC + Token symbol + BIP44 coin type + Default + The server is not available, please try again later + Total balance + The amount does not include some of your funds + Tokens + Manage tokens + No rate + Network is unreachable + Hide token + Hide %s + Hide + You are about to hide this token from the main screen. You can add it back anytime through the manage tokens page. + Unable to hide %s + The %s token is the main currency on the %s network and cannot be hidden as long as you have other tokens on this network in the list. + Your wallet has not been backed up + To protect your assets, we advise you to carry out this procedure + Retry + Chat + %s network + I understand + Yes + No + Russian bank cards are not accepted at the moment + Do you have a bank card of another country or a UnionPay card? + This network is not supported. Please select another network. + Card Settings + Security Mode + Change Access Code + Access code will be changed on this card only + Continue + Tangem Bot + Get your card ready! + Scan the card to change its settings. The changes will impact only the card you\'ve scanned and will not affect other cards tied to your wallet. + Scan Card + App Settings + Keep the wallet in the app + Enable to link all the wallets to Tangem app. Biometric authentication will be required for unlocking the app. Transaction signing requires tapping your Tangem card. + Save Access Code + Biometric authentication will be requested instead of the access code for interactions with your card. + Removing the saved card deletes all the saved wallets and their access codes. + This will delete all the saved wallet access codes. Any further operation with the wallet will require submitting the access code. + WalletConnect + Connect to Dapps + Factory Reset will completely delete the wallet from the selected card and remove it from the app. You will not be able to restore the current wallet. + Factory Reset will completely delete the wallet from the selected card and remove it from the app. You will not be able to restore the current wallet or use the card to recover the access code. + I understand that after performing this action, I will no longer have access to the current wallet + Reset the Card + Reset to Factory Settings + Select network + Scan your card + To access all the networks you need to scan the card + Connection with this Dapp cannot be established due to its technical implementation. + Welcome back! + Use %s or scan a card to access the app + Log in with %s + Scan card + Access the app + Log into the app and check your balance without scanning the card + Access code + Note that making a transaction with your funds will still require your card + My Wallets + Multi-currency + Single-currency + Add new wallet + Rename Wallet + Wallet name + Unlock all with %s + Invalid Tag. It won\'t be added to the transaction. + Invalid Memo. It won\'t be added to the transaction. + Tag + Memo + Attention + Tap the card with the visa logo + No funds for activation + Please contact support + Four identical digits isn\'t safe + Such a PIN can be brute-forced easily + Pin code + Connect + KYC + Verify your identity + Set PIN code + Register + Verify via Utorg + Refresh + Connect your card + Verify your identity + KYC is in progress + Connect your card to the decentralized payment system + To start using your card you have to pass the KYC process + Please wait until the verification is completed. You\'ll be notified via email. Usually it takes up to 1 hour. You can close the app and come back later. + PIN Code + Set PIN code for your SaltPay card + Chat with support + Please hold the card until the operation complete + To start the backup process you have to add Tangem card as your backup + No backup card + Backup card ready + Finalize the backup process by creating an access code + Prepare the SaltPay card + Tap the SaltPay card + Tap the Tangem card + Support + Claim %s + To get started, simply claim wxDAI to your wallet + Claim + Congratulations! Your first payment crypto card has been activated! + Claiming + It will take a few seconds + Something went wrong + Please check your email for further instructions + Do you want to exit the activation process? + In this case, you will need to start from the beginning. + You have used a card from another wallet. Tap the card associated with this wallet + Referral program + Refer your friends to Tangem + You + Will get + for each wallet bought by your friend on your %s network address%s + Your friend + Will get a + %s discount + when buying a card on tangem.com + Your friends bought + Your personal code + Participate + terms and conditions + By tapping this button you accept + You\'ve accepted + of the referral program + Internal error: wallet manager not found + Balance: %s + Share + Copy + Success + Give Permission + To continue you need to allow 1inch smart contracts to use your %s + Amount %s + Your Wallet + Spender + Approve + Swap of %s to + Swap + Insufficient funds + Give Permission + Permit and Swap + Failed to load the information about the referral program. Please try again later. + Failed to load the information about the referral program. Reason: %s. Please try again later. + Your participation request could not be processed. Reason: %s. Please try again later. If the problem persists — feel free to contact our support. + Personal code copied! + Buy Tangem Wallet with discount!\n%s + %s network has a concept of Existential Deposit. If your account drops below %s it will be deactivated and any remaining funds will be destroyed. + Invalid address + This wallet has already been saved, you can add another one + Delete + Privacy policy + Enable biometric authorization + It looks like you have biometric authentication disabled, it is necessary to save wallets + Enable + %d selected + biometric authentication + biometrics Tangem Save changes Warning @@ -54,16 +272,16 @@ Long Tap This mechanism protects against proximity attacks on a card. It will enforce a delay between reception and execution of a command. Passcode - Before executing any command entailing a change of the card state you will have to enter the passcode. + Before executing any command entailing a change of the card state, you will have to enter the passcode. Access code You will have to submit the correct access code before scanning the card This card has been already topped up and signed transactions in the past. Consider immediate withdrawal of all funds if you have received this card from an untrusted source. If it\'s your card, there is nothing to worry about. This card is not designed to work with this app The card you scanned is a development card. Don\'t accept it as a payment. Tap to sign - To create wallet, connect your phone and the card exactly as it shown above - To change the access code, connect your phone and the card exactly as it shown above - To change the passcode, connect your phone and the card exactly as it shown above + To create the wallet tap the card as shown above and do not remove until the end of the operation + To change the access code tap the card as shown above and do not remove until the end of the operation + To change the passcode tap the card as shown above and do not remove until the end of the operation Terms of Service No internet connection PayString unsupported by blockchain @@ -97,7 +315,6 @@ Can\'t send a transaction Reason: %s Are you having difficulty scanning your card? - I\'m okay Request support Send feedback Really cool! @@ -133,8 +350,7 @@ Success! Your card is activated and ready to be used Balance - Send only %s (%s) to this address. Using other tokens and networks may result in loss of funds. - Send only %s (%s) from %s network to this address. Using other tokens and networks may result in loss of funds. + Send only %s (%s) from %s network to this address. Using other tokens and networks may result in loss of funds. If the process of creating the wallet gets interrupted in any way, you\'ll have to start over. The twinning process is partly complete. You can\'t exit it now. Ok, Got it! @@ -217,7 +433,7 @@ Tangem feedback Feedback Tell us what functions you are missing, and we will try to help you. - Please tell us what card do you have? + Please tell us what card do you have Please tell us more about your issue. Every small detail can help. Hi support team, The following information is optional. You can erase it if you don\'t want to share it. @@ -227,6 +443,12 @@ Error OK Failed to establish WalletConnect session. Please, try again later. + Would you like to use biometrics? + Biometrics will be requested instead of the access code for interactions with your wallet + Allow to use biometrics + Enable biometric authentication + Go to settings to enable biometric authentication in the Tangem App + Scan the card %s diff --git a/core/res/src/main/res/values/strings_final.xml b/core/res/src/main/res/values/strings_final.xml deleted file mode 100644 index 673ced5d90..0000000000 --- a/core/res/src/main/res/values/strings_final.xml +++ /dev/null @@ -1,233 +0,0 @@ - - - Tangem Wallet - 3 cards - 2 cards - Shipping - Free - I have a promo code… - Total - Other payment methods - Buy now - Meet\nTangem - Buy - Store - Send - Pay - Exchange - Lend - Borrow - Revolutionary Hardware Wallet - Store your crypto assets secure while keeping private keys contained in your card - Ultra Secure Backup - Up to - 3 physical cards - to one wallet - Thousands of Currencies - A hardware wallet for your Bitcoin, Ethereum and many more currencies simultaneously – all in one card - DeFi Compatible - Exchange, buy NFT\'s, make loans and deposits in more than 100 different decentralized services - The Wallet for Everyone - Use it on the go, anywhere, anytime. No wires or batteries. Just tap the card to your phone when you need your crypto. - Order - Search tokens - You are currently running in Demo mode. All funds are not real. - This feature is disabled in Demo mode - Not enough funds for fee on your %s wallet to send a transaction. Top up your %s wallet first. - Available networks - %s network not found. Please, add it first and try again. - Attention - Note that tokens on different networks have different addresses. Double check that your address matches the network when you transfer funds. - Tokens in Solana network are not supported by this card due to firmware limitation. - Contract address copied! - Contract address - Required field - Please select the network - Decimal number must be a valid integer, no higher than %d - Contract address is invalid - Derivation path is invalid - Note that tokens can be created by anyone. Be aware of adding scam tokens, they can cost nothing. - This token/network has already been added to your list - Decimals - Network - Not selected - E.g. USD Coin - Name - E.g. USDC - Token symbol - BIP44 coin type - Default - The server is not available, please try again later - Total balance - The amount does not include some of your funds - Tokens - Manage tokens - No rate - Network is unreachable - Hide token - Hide %s - Hide - You are about to hide this token from the main screen. You can add it back anytime through the manage tokens page. - Unable to hide %s - The %s token is the main currency on the %s network and cannot be hidden as long as you have other tokens on this network in the list. - Your wallet has not been backed up - To protect your assets, we advise you to carry out this procedure - Retry - Chat - %s network - I understand - Yes - No - Russian bank cards are not accepted at the moment - Do you have a bank card of another country or a UnionPay card? - This network is not supported. Please select another network. - Card Settings - Security Mode - Change Access Code - Access code will be changed on this card only - Continue - Tangem Bot - Get your card ready! - Scan the card to change its settings. The changes will impact only the card you\'ve scanned and will not affect other cards tied to your wallet. - Scan Card - App Settings - Keep the wallet in the app - Enable to link all the wallets to Tangem app. Biometric authentication will be required for unlocking the app. Transaction signing requires tapping your Tangem card. - Save Access Code - Biometric authentication will be requested instead of the access code for interactions with your card. - Removing the saved card deletes all the saved wallets and their access codes. - This will delete all the saved wallet access codes. Any further operation with the wallet will require submitting the access code. - WalletConnect - Connect to Dapps - This action will completely remove the wallet from the selected card. You will not be able to restore the current wallet or use the card to recover the access code. - I understand that after performing this action, I will no longer have access to the current wallet - Reset the Card - Reset to Factory Settings - Factory Reset will completely delete the wallet from the selected card. You will not be able to restore the current wallet or use the card to recover the access code. - Select network - Scan your card - To access all the networks you need to scan the card - Connection with this Dapp cannot be established due to its technical implementation. - Welcome back! - Use %s or scan a card to access the app - Log in with %s - Scan card - Save your wallet - Would you like to use %s? - Would you like to use biometrics? - Access the app - Log into the app and check your balance without scanning the card - Access code - %s will be requested instead of the access code for interactions with your wallet - Biometrics will be requested instead of the access code for interactions with your wallet - Note that making a transaction with your funds will still require your card - Allow to use %s - Allow to use biometrics - New feature - My Wallets - Multi-currency - Single-currency - Add new wallet - Rename Wallet - Wallet name - Unlock all with %s - Invalid Tag. It won\'t be added to the transaction. - Invalid Memo. It won\'t be added to the transaction. - Tag - Memo - Attention - Tap the card with the visa logo - No funds for activation - Please contact support - Four identical digits isn\'t safe - Such a PIN can be brute-forced easily - Pin code - Connect - KYC - Verify your identity - Set PIN code - Register - Verify via Utorg - Refresh - Connect your card - Verify your identity - KYC is in progress - Connect your card to the decentralized payment system - To start using your card you have to pass the KYC process - Please wait until the verification is completed. Usually it takes up to 1 hour. You can close the app and come back later. - PIN Code - Set PIN code for your SaltPay card - Chat with support - Please hold the card until the operation complete - To start the backup process you have to add Tangem card as your backup - No backup card - Backup card ready - Finalize the backup process by creating an access code - Prepare the SaltPay card - Tap the SaltPay card - Tap the Tangem card - Support - Claim %s - To get started, simply claim wxDAI to your wallet - Claim - Congratulations! Your first payment crypto card has been activated - Claiming - It will take a few seconds - Something went wrong - Please check your email for further instructions - Do you want to exit the activation process? - In this case, you will need to start from the beginning. - You have used a card from another wallet. Tap the card associated with this wallet - Referral program - Refer your friends to Tangem - You - Will get - for each wallet bought by your friend on your %s network address%s - Your friend - Will get a - %s discount - when buying a card on tangem.com - Your friends bought - Your personal code - Participate - terms and conditions - By tapping this button you accept - You\'ve accepted - of the referral program - Internal error: wallet manager not found - Balance: %s - Share - Copy - Success - Give Permission - To continue you need to allow 1inch smart contracts to use your %s - Amount %s - Your Wallet - Spender - Approve - Swap of %s to - Swap - Insufficient funds - Give Permission - Permit and Swap - Failed to load the information about the referral program. Please try again later. - Failed to load the information about the referral program. Reason: %s. Please try again later. - Your participation request could not be processed. Reason: %s. Please try again later. If the problem persists — feel free to contact our support. - Personal code copied! - Buy Tangem Wallet with discount!\n%s - %s network has a concept of Existential Deposit. If your account drops below %s it will be deactivated and any remaining funds will be destroyed. - Invalid address - This wallet has already been saved, you can add another one - Delete - Privacy policy - Enable biometric authorization - It looks like you have biometric authentication disabled, it is necessary to save wallets - Enable - Save your Wallet feature allows you to use your wallet with biometric auth without tapping your card to the phone to gain access. - %d selected - biometric authentication - biometrics - Enable biometric authentication - Go to settings to enable biometric authentication in the Tangem App - Scan the card - diff --git a/core/res/src/main/res/values/strings_plurals_final.xml b/core/res/src/main/res/values/strings_plurals.xml similarity index 100% rename from core/res/src/main/res/values/strings_plurals_final.xml rename to core/res/src/main/res/values/strings_plurals.xml diff --git a/core/ui/src/main/java/com/tangem/core/ui/utils/OneTouchClickListener.kt b/core/ui/src/main/java/com/tangem/core/ui/utils/OneTouchClickListener.kt new file mode 100644 index 0000000000..210c57805c --- /dev/null +++ b/core/ui/src/main/java/com/tangem/core/ui/utils/OneTouchClickListener.kt @@ -0,0 +1,25 @@ +package com.tangem.core.ui.utils + +import android.os.SystemClock +import android.view.View + +/** + * Implementation of click listener for preventing multiple click events + * + * @property action action that called when a view has been clicked + */ +class OneTouchClickListener(private val action: () -> Unit) : View.OnClickListener { + + private var lastClickTimeMs: Long = 0L + + override fun onClick(v: View?) { + if (SystemClock.elapsedRealtime() - lastClickTimeMs > CLICK_DELAY_MS) { + lastClickTimeMs = SystemClock.elapsedRealtime() + action() + } + } + + companion object { + private const val CLICK_DELAY_MS = 500L + } +} \ No newline at end of file diff --git a/domain/src/main/java/com/tangem/domain/common/util/UserWalletId.kt b/domain/src/main/java/com/tangem/domain/common/util/UserWalletId.kt index 4e83c913ca..abf878da69 100644 --- a/domain/src/main/java/com/tangem/domain/common/util/UserWalletId.kt +++ b/domain/src/main/java/com/tangem/domain/common/util/UserWalletId.kt @@ -1,17 +1,15 @@ package com.tangem.domain.common.util -import com.tangem.common.extensions.calculateSha256 import com.tangem.common.extensions.hexToBytes import com.tangem.common.extensions.toHexString -import com.tangem.domain.common.extensions.calculateHmacSha256 class UserWalletId( val stringValue: String, ) { val value = stringValue.hexToBytes() - constructor(walletPublicKey: ByteArray?) : this( - stringValue = walletPublicKey?.let { calculateUserWalletId(it).toHexString() } ?: "", + constructor(value: ByteArray?) : this( + stringValue = value?.toHexString() ?: "", ) override fun equals(other: Any?): Boolean { @@ -32,12 +30,4 @@ class UserWalletId( "UserWalletId(${take(3)}...${takeLast(3)})" } } -} - -private fun calculateUserWalletId(publicKey: ByteArray): ByteArray { - val message = MESSAGE_FOR_WALLET_ID.toByteArray() - val keyHash = publicKey.calculateSha256() - return message.calculateHmacSha256(keyHash) -} - -private const val MESSAGE_FOR_WALLET_ID = "UserWalletID" \ No newline at end of file +} \ No newline at end of file