From fd31a96384b19a7e123c69e583c1724401743d97 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 4 May 2023 00:05:26 +0300 Subject: [PATCH 01/13] Updated on 2026-08-14 --- .../di/UserWalletsListManagerProvider.kt | 3 +- .../DerivationPathAdapterWithMigration.kt | 35 +++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 app/src/main/java/com/tangem/tap/domain/userWalletList/utils/json/DerivationPathAdapterWithMigration.kt diff --git a/app/src/main/java/com/tangem/tap/domain/userWalletList/di/UserWalletsListManagerProvider.kt b/app/src/main/java/com/tangem/tap/domain/userWalletList/di/UserWalletsListManagerProvider.kt index ec4c83c460..71255de6a0 100644 --- a/app/src/main/java/com/tangem/tap/domain/userWalletList/di/UserWalletsListManagerProvider.kt +++ b/app/src/main/java/com/tangem/tap/domain/userWalletList/di/UserWalletsListManagerProvider.kt @@ -17,6 +17,7 @@ import com.tangem.tap.domain.userWalletList.repository.implementation.DefaultUse import com.tangem.tap.domain.userWalletList.repository.implementation.DefaultUserWalletsSensitiveInformationRepository import com.tangem.tap.domain.userWalletList.utils.json.ByteArrayKeyAdapter import com.tangem.tap.domain.userWalletList.utils.json.CardBackupStatusAdapter +import com.tangem.tap.domain.userWalletList.utils.json.DerivationPathAdapterWithMigration import com.tangem.tap.domain.userWalletList.utils.json.ExtendedPublicKeysMapAdapter import com.tangem.tap.domain.userWalletList.utils.json.ScanResponseDerivedKeysMapAdapter import com.tangem.tap.domain.userWalletList.utils.json.WalletDerivedKeysMapAdapter @@ -33,8 +34,8 @@ fun UserWalletsListManager.Companion.provideBiometricImplementation( .add(ByteArrayKeyAdapter()) .add(ExtendedPublicKeysMapAdapter()) .add(CardBackupStatusAdapter()) + .add(DerivationPathAdapterWithMigration()) .add(TangemSdkAdapter.DateAdapter()) - .add(TangemSdkAdapter.DerivationPathAdapter()) .add(TangemSdkAdapter.DerivationNodeAdapter()) .add(TangemSdkAdapter.FirmwareVersionAdapter()) // For PrimaryCard model .add(KotlinJsonAdapterFactory()) diff --git a/app/src/main/java/com/tangem/tap/domain/userWalletList/utils/json/DerivationPathAdapterWithMigration.kt b/app/src/main/java/com/tangem/tap/domain/userWalletList/utils/json/DerivationPathAdapterWithMigration.kt new file mode 100644 index 0000000000..4719b96b49 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/domain/userWalletList/utils/json/DerivationPathAdapterWithMigration.kt @@ -0,0 +1,35 @@ +package com.tangem.tap.domain.userWalletList.utils.json + +import com.squareup.moshi.FromJson +import com.squareup.moshi.ToJson +import com.tangem.common.extensions.guard +import com.tangem.common.json.MoshiJsonConverter +import com.tangem.crypto.hdWallet.DerivationNode +import com.tangem.crypto.hdWallet.DerivationPath +import timber.log.Timber + +class DerivationPathAdapterWithMigration { + @ToJson + fun toJson(src: DerivationPath): String = src.rawPath + + @FromJson + fun fromJson(json: String): DerivationPath { + val jsonMap = MoshiJsonConverter.default().toMap(json) + return if (jsonMap.isEmpty()) { + DerivationPath(json) + } else { + fromLegacyScheme(jsonMap) + } + } + + @Suppress("UNCHECKED_CAST") + private fun fromLegacyScheme(jsonMap: Map): DerivationPath { + val rawPath = jsonMap["rawPath"] as String + val nodeIndexes = (jsonMap["nodes"] as? List).guard { + Timber.e("Unable to convert derivation path nodes from JSON") + return DerivationPath(rawPath) + } + val nodes = nodeIndexes.map { DerivationNode.fromIndex(it.toLong()) } + return DerivationPath(rawPath, nodes) + } +} \ No newline at end of file From 519b40621cc5f4a089d2b4d59a695d604257ecc9 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 4 May 2023 12:59:56 +0300 Subject: [PATCH 02/13] Updated on 2026-08-14 --- .../userWalletList/UserWalletsListError.kt | 5 ++ .../BiometricUserWalletsKeysRepository.kt | 2 + .../ui/WalletSelectorBottomSheetFragment.kt | 2 + .../ui/WalletSelectorViewModel.kt | 4 ++ .../BiometricsDisabledWarningContent.kt | 55 +++++++++++++++++++ .../walletSelector/ui/model/DialogModel.kt | 5 ++ .../features/welcome/ui/WelcomeViewModel.kt | 4 ++ .../welcome/ui/components/WarningDialog.kt | 36 +++++++++++- .../features/welcome/ui/model/WarningModel.kt | 5 ++ core/res/src/main/res/values-ru/strings.xml | 1 + core/res/src/main/res/values/strings.xml | 1 + 11 files changed, 118 insertions(+), 2 deletions(-) create mode 100644 app/src/main/java/com/tangem/tap/features/walletSelector/ui/components/BiometricsDisabledWarningContent.kt diff --git a/app/src/main/java/com/tangem/tap/domain/userWalletList/UserWalletsListError.kt b/app/src/main/java/com/tangem/tap/domain/userWalletList/UserWalletsListError.kt index c73891df83..ec9ed95b1d 100644 --- a/app/src/main/java/com/tangem/tap/domain/userWalletList/UserWalletsListError.kt +++ b/app/src/main/java/com/tangem/tap/domain/userWalletList/UserWalletsListError.kt @@ -4,6 +4,7 @@ import com.tangem.common.core.TangemError import com.tangem.wallet.R sealed class UserWalletsListError(code: Int) : TangemError(code) { + override val silent: Boolean get() = (cause as? TangemError)?.silent == true @@ -18,6 +19,10 @@ sealed class UserWalletsListError(code: Int) : TangemError(code) { override var customMessage: String = "Encryption key invalidated" } + object BiometricsAuthenticationDisabled : UserWalletsListError(code = 60005) { + override var customMessage: String = "Biometrics authentication disabled" + } + data class BiometricsAuthenticationLockout(val isPermanent: Boolean) : UserWalletsListError(code = 60003) { override var customMessage: String = "Biometric authentication lockout, permanent: $isPermanent" } 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 5d39710ca7..affeeeef57 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 @@ -48,6 +48,8 @@ internal class BiometricUserWalletsKeysRepository( UserWalletsListError.BiometricsAuthenticationLockout(isPermanent = true) is TangemSdkError.BiometricCryptographyKeyInvalidated -> UserWalletsListError.EncryptionKeyInvalidated + is TangemSdkError.BiometricsUnavailable -> + UserWalletsListError.BiometricsAuthenticationDisabled else -> error } } diff --git a/app/src/main/java/com/tangem/tap/features/walletSelector/ui/WalletSelectorBottomSheetFragment.kt b/app/src/main/java/com/tangem/tap/features/walletSelector/ui/WalletSelectorBottomSheetFragment.kt index 9f82671d66..4f24d56193 100644 --- a/app/src/main/java/com/tangem/tap/features/walletSelector/ui/WalletSelectorBottomSheetFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/walletSelector/ui/WalletSelectorBottomSheetFragment.kt @@ -25,6 +25,7 @@ import com.tangem.core.ui.fragments.ComposeBottomSheetFragment import com.tangem.core.ui.res.TangemTheme import com.tangem.tap.common.analytics.events.MyWallets import com.tangem.tap.features.details.ui.cardsettings.resolveReference +import com.tangem.tap.features.walletSelector.ui.components.BiometricsDisabledWarningContent import com.tangem.tap.features.walletSelector.ui.components.BiometricsLockoutWarningContent import com.tangem.tap.features.walletSelector.ui.components.KeyInvalidatedWarningContent import com.tangem.tap.features.walletSelector.ui.components.RemoveWalletDialogContent @@ -96,6 +97,7 @@ internal class WalletSelectorBottomSheetFragment : ComposeBottomSheetFragment RenameWalletDialogContent(dialog) is WarningModel.BiometricsLockoutWarning -> BiometricsLockoutWarningContent(dialog) is WarningModel.KeyInvalidatedWarning -> KeyInvalidatedWarningContent(dialog) + is WarningModel.BiometricsDisabledWarning -> BiometricsDisabledWarningContent(dialog) } } } \ No newline at end of file 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 2637e44e3b..99024dad35 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 @@ -199,6 +199,10 @@ internal class WalletSelectorViewModel : ViewModel(), StoreSubscriber WarningModel.KeyInvalidatedWarning( onDismiss = this::dismissWarningDialog, ) + is UserWalletsListError.BiometricsAuthenticationDisabled -> WarningModel.BiometricsDisabledWarning( + onConfirm = { /* [REDACTED_TODO_COMMENT] */ }, + onDismiss = this::dismissWarningDialog, + ) else -> currentDialog } } diff --git a/app/src/main/java/com/tangem/tap/features/walletSelector/ui/components/BiometricsDisabledWarningContent.kt b/app/src/main/java/com/tangem/tap/features/walletSelector/ui/components/BiometricsDisabledWarningContent.kt new file mode 100644 index 0000000000..429ab5471e --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/walletSelector/ui/components/BiometricsDisabledWarningContent.kt @@ -0,0 +1,55 @@ +package com.tangem.tap.features.walletSelector.ui.components + +import androidx.compose.foundation.layout.Column +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview +import com.tangem.core.ui.components.BasicDialog +import com.tangem.core.ui.components.DialogButton +import com.tangem.core.ui.res.TangemTheme +import com.tangem.tap.features.walletSelector.ui.model.WarningModel +import com.tangem.wallet.R + +@Composable +internal fun BiometricsDisabledWarningContent(warning: WarningModel.BiometricsDisabledWarning) { + BasicDialog( + title = stringResource(id = R.string.common_warning), + message = stringResource(id = R.string.biometric_unavailable_warning), + onDismissDialog = warning.onDismiss, + confirmButton = DialogButton( + title = stringResource(id = R.string.common_ok), + onClick = warning.onDismiss, + ), + ) +} + +// region Preview +@Composable +private fun BiometricsDisabledWarningContentSample(modifier: Modifier = Modifier) { + Column(modifier = modifier) { + BiometricsDisabledWarningContent( + warning = WarningModel.BiometricsDisabledWarning( + onConfirm = {}, + onDismiss = {}, + ), + ) + } +} + +@Preview(showBackground = true, widthDp = 360) +@Composable +private fun BiometricsDisabledWarningContentPreview_Light() { + TangemTheme { + BiometricsDisabledWarningContentSample() + } +} + +@Preview(showBackground = true, widthDp = 360) +@Composable +private fun BiometricsDisabledWarningContentPreview_Dark() { + TangemTheme(isDark = true) { + BiometricsDisabledWarningContentSample() + } +} +// endregion Preview \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/walletSelector/ui/model/DialogModel.kt b/app/src/main/java/com/tangem/tap/features/walletSelector/ui/model/DialogModel.kt index 8024e00021..b458313c35 100644 --- a/app/src/main/java/com/tangem/tap/features/walletSelector/ui/model/DialogModel.kt +++ b/app/src/main/java/com/tangem/tap/features/walletSelector/ui/model/DialogModel.kt @@ -22,4 +22,9 @@ internal sealed interface WarningModel : DialogModel { data class KeyInvalidatedWarning( val onDismiss: () -> Unit, ) : WarningModel + + data class BiometricsDisabledWarning( + val onConfirm: () -> Unit, + val onDismiss: () -> Unit, + ) : WarningModel } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/welcome/ui/WelcomeViewModel.kt b/app/src/main/java/com/tangem/tap/features/welcome/ui/WelcomeViewModel.kt index 3687df607c..e19891caa2 100644 --- a/app/src/main/java/com/tangem/tap/features/welcome/ui/WelcomeViewModel.kt +++ b/app/src/main/java/com/tangem/tap/features/welcome/ui/WelcomeViewModel.kt @@ -71,6 +71,10 @@ internal class WelcomeViewModel : ViewModel(), StoreSubscriber { is UserWalletsListError.EncryptionKeyInvalidated -> WarningModel.KeyInvalidatedWarning( onDismiss = this::dismissWarning, ) + is UserWalletsListError.BiometricsAuthenticationDisabled -> WarningModel.BiometricsDisabledWarning( + onConfirm = { /* [REDACTED_TODO_COMMENT] */ }, + onDismiss = this::dismissWarning, + ) else -> null } } diff --git a/app/src/main/java/com/tangem/tap/features/welcome/ui/components/WarningDialog.kt b/app/src/main/java/com/tangem/tap/features/welcome/ui/components/WarningDialog.kt index 40ae13b0cb..d8a78e246f 100644 --- a/app/src/main/java/com/tangem/tap/features/welcome/ui/components/WarningDialog.kt +++ b/app/src/main/java/com/tangem/tap/features/welcome/ui/components/WarningDialog.kt @@ -43,6 +43,17 @@ internal fun WarningDialog(warning: WarningModel?) { ), ) } + is WarningModel.BiometricsDisabledWarning -> { + BasicDialog( + title = stringResource(id = R.string.common_warning), + message = stringResource(id = R.string.biometric_unavailable_warning), + onDismissDialog = warning.onDismiss, + confirmButton = DialogButton( + title = stringResource(id = R.string.common_ok), + onClick = warning.onConfirm, + ), + ) + } } } @@ -103,7 +114,6 @@ private fun BiometricsLockoutDialog_Permanent_Preview_Dark() { } } -// region Preview @Composable private fun KeyInvalidatedWarningSample(modifier: Modifier = Modifier) { Column(modifier = modifier) { @@ -126,5 +136,27 @@ private fun KeyInvalidatedWarningPreview_Dark() { KeyInvalidatedWarningSample() } } -// endregion Preview + +@Composable +private fun BiometricDisabledWarningSample(modifier: Modifier = Modifier) { + Column(modifier = modifier) { + WarningDialog(warning = WarningModel.BiometricsDisabledWarning(onConfirm = {}, onDismiss = {})) + } +} + +@Preview(showBackground = true, widthDp = 360) +@Composable +private fun BiometricDisabledWarningPreview_Light() { + TangemTheme { + BiometricDisabledWarningSample() + } +} + +@Preview(showBackground = true, widthDp = 360) +@Composable +private fun BiometricDisabledWarningPreview_Dark() { + TangemTheme(isDark = true) { + BiometricDisabledWarningSample() + } +} // endregion Preview \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/welcome/ui/model/WarningModel.kt b/app/src/main/java/com/tangem/tap/features/welcome/ui/model/WarningModel.kt index 4940a4642a..55789fe91a 100644 --- a/app/src/main/java/com/tangem/tap/features/welcome/ui/model/WarningModel.kt +++ b/app/src/main/java/com/tangem/tap/features/welcome/ui/model/WarningModel.kt @@ -9,4 +9,9 @@ internal sealed interface WarningModel { data class KeyInvalidatedWarning( val onDismiss: () -> Unit, ) : WarningModel + + data class BiometricsDisabledWarning( + val onConfirm: () -> Unit, + val onDismiss: () -> Unit, + ) : WarningModel } \ No newline at end of file diff --git a/core/res/src/main/res/values-ru/strings.xml b/core/res/src/main/res/values-ru/strings.xml index f4d33131ce..e27b0df710 100644 --- a/core/res/src/main/res/values-ru/strings.xml +++ b/core/res/src/main/res/values-ru/strings.xml @@ -27,6 +27,7 @@ Пожалуйста, отсканируйте карту Пожалуйста, попробуйте снова через 30 секунд или отсканируйте карту Слишком много попыток + Вы отключили биометрическую аутентификацию на вашем телефоне и не сможете сохранять кошельки в приложении. Для сохранения кошельков, пожалуйста, включите функцию биометрической аутентификации в настройках телефона. Отключите эту опцию, если не хотите, чтобы эта карта использовалась для сброса кодов доступа на другие карты этого кошелька. Обратите внимание, сброс кода также не будет доступен на этой карте. Использовать эту карту для сброса кода доступа на других картах в этом кошельке Отключить возможность сброса кода доступа на этой карте или других картах этого кошелька diff --git a/core/res/src/main/res/values/strings.xml b/core/res/src/main/res/values/strings.xml index d2534d92f5..eda6534993 100644 --- a/core/res/src/main/res/values/strings.xml +++ b/core/res/src/main/res/values/strings.xml @@ -27,6 +27,7 @@ Please scan the card Please try again in 30 seconds or scan the card Too many attempts + You have disabled biometric authentication on your phone and will not be able to save wallets in the app. To save wallets, please enable the biometric authentication function in your phone settings. You have disabled biometric authentication on your phone and will not be able to save wallets in the app. To save wallets, please enable the biometric authentication function in your phone settings. Disable this option if you don\'t want this card to be used to reset access codes on other cards of this wallet. Note that you will not be able to reset access code on this card as well. Allows you to use this card to reset access code on other cards in this wallet Disable the ability to reset access code on this card or other cards in this wallet From 86db132f1fa19cdef36361fc9c70cdec041a1895 Mon Sep 17 00:00:00 2001 From: Tangem Date: Sat, 6 May 2023 00:07:41 +0300 Subject: [PATCH 03/13] Updated on 2026-08-14 --- app/src/main/java/com/tangem/tap/domain/TangemSigner.kt | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/domain/TangemSigner.kt b/app/src/main/java/com/tangem/tap/domain/TangemSigner.kt index f8361ffae1..f316b2e433 100644 --- a/app/src/main/java/com/tangem/tap/domain/TangemSigner.kt +++ b/app/src/main/java/com/tangem/tap/domain/TangemSigner.kt @@ -5,6 +5,7 @@ import com.tangem.TangemSdk import com.tangem.blockchain.common.TransactionSigner import com.tangem.blockchain.common.Wallet import com.tangem.common.CompletionResult +import com.tangem.domain.common.TapWorkarounds.isTangemTwins import com.tangem.domain.models.scan.CardDTO import com.tangem.tap.domain.tasks.SignHashesTask import kotlinx.coroutines.suspendCancellableCoroutine @@ -23,12 +24,12 @@ class TangemSigner( publicKey: Wallet.PublicKey, ): CompletionResult> { return suspendCancellableCoroutine { continuation -> - val cardId = if (card.backupStatus?.isActive == true) null else card.cardId - + val isCardNotBackedUp = card.backupStatus?.isActive != true && !card.isTangemTwins val task = SignHashesTask(hashes, publicKey) + tangemSdk.startSessionWithRunnable( runnable = task, - cardId = cardId, + cardId = card.cardId.takeIf { isCardNotBackedUp }, initialMessage = initialMessage, accessCode = accessCode, ) { result -> From 49dc0d6e0f03dd90af2a402abb28bc00d04173ec Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 8 May 2023 19:11:19 +0300 Subject: [PATCH 04/13] Updated on 2026-08-14 --- .../src/main/java/com/tangem/domain/common/SaltPayWorkaround.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/domain/src/main/java/com/tangem/domain/common/SaltPayWorkaround.kt b/domain/src/main/java/com/tangem/domain/common/SaltPayWorkaround.kt index b18597f282..39df7c6984 100644 --- a/domain/src/main/java/com/tangem/domain/common/SaltPayWorkaround.kt +++ b/domain/src/main/java/com/tangem/domain/common/SaltPayWorkaround.kt @@ -45,7 +45,7 @@ object SaltPayWorkaround { ) + attachTestWalletCardIds() val walletCardIdRanges = listOf( - CardIdRange("AC05000000000003", "AC05000000023997")!!, + CardIdRange("AC05000000000003", "AC05000000015993")!!, ) + attachTestWalletCardIdRanges() fun tokenFrom(blockchain: Blockchain): Token { From 05cbec8ced2d9638e2e62811fea17bef98b33dfc Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 10 May 2023 19:42:50 +0800 Subject: [PATCH 05/13] Updated on 2026-08-14 --- .../src/main/assets/configs/feature_toggles_config.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/featuretoggles/src/main/assets/configs/feature_toggles_config.json b/core/featuretoggles/src/main/assets/configs/feature_toggles_config.json index 738e44b33c..96fb733d71 100644 --- a/core/featuretoggles/src/main/assets/configs/feature_toggles_config.json +++ b/core/featuretoggles/src/main/assets/configs/feature_toggles_config.json @@ -9,6 +9,6 @@ }, { "name": "REDESIGNED_CUSTOM_TOKEN_SCREEN_ENABLED", - "version": "4.5.0" + "version": "undefined" } ] \ No newline at end of file From 6a81ef8a37637d715931a823f5bd810933812cf2 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 10 May 2023 15:37:04 +0300 Subject: [PATCH 06/13] Updated on 2026-08-14 --- .../tap/features/details/ui/details/DetailsScreen.kt | 5 ++++- .../features/details/ui/details/DetailsScreenState.kt | 7 +++++-- app/src/main/res/drawable/ic_discord.xml | 9 +++++++++ core/res/src/main/res/values/strings.xml | 2 +- 4 files changed, 19 insertions(+), 4 deletions(-) create mode 100644 app/src/main/res/drawable/ic_discord.xml diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/details/DetailsScreen.kt b/app/src/main/java/com/tangem/tap/features/details/ui/details/DetailsScreen.kt index d4526edf76..f2fd8a3b07 100644 --- a/app/src/main/java/com/tangem/tap/features/details/ui/details/DetailsScreen.kt +++ b/app/src/main/java/com/tangem/tap/features/details/ui/details/DetailsScreen.kt @@ -160,7 +160,10 @@ fun DetailsItem(item: SettingsElement, appCurrency: String, onItemsClick: () -> @Composable fun TangemSocialAccounts(links: List, onSocialNetworkClick: (SocialNetworkLink) -> Unit) { - LazyRow(modifier = Modifier.padding(start = 8.dp, end = 8.dp)) { + LazyRow( + modifier = Modifier.padding(start = 8.dp, end = 8.dp), + verticalAlignment = Alignment.CenterVertically, + ) { items(links) { Icon( painter = painterResource(id = it.network.iconRes), diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/details/DetailsScreenState.kt b/app/src/main/java/com/tangem/tap/features/details/ui/details/DetailsScreenState.kt index ce8e1fd1ef..c0dd629819 100644 --- a/app/src/main/java/com/tangem/tap/features/details/ui/details/DetailsScreenState.kt +++ b/app/src/main/java/com/tangem/tap/features/details/ui/details/DetailsScreenState.kt @@ -55,25 +55,28 @@ sealed class SocialNetwork(val id: String, val iconRes: Int) { object GitHub : SocialNetwork("GitHub", R.drawable.ic_github) object YouTube : SocialNetwork("YouTube", R.drawable.ic_youtube) object LinkedIn : SocialNetwork("LinkedIn", R.drawable.ic_linkedin) + object Discord : SocialNetwork("Discord", R.drawable.ic_discord) } object TangemSocialAccounts { val accountsEn: List = listOf( - SocialNetworkLink(SocialNetwork.Telegram, "https://t.me/TangemCards"), + SocialNetworkLink(SocialNetwork.Telegram, "https://t.me/tangem_chat"), SocialNetworkLink(SocialNetwork.Twitter, "https://twitter.com/tangem"), SocialNetworkLink(SocialNetwork.Facebook, "https://m.facebook.com/TangemCards/"), SocialNetworkLink(SocialNetwork.Instagram, "https://instagram.com/tangemcards"), SocialNetworkLink(SocialNetwork.GitHub, "https://github.com/tangem"), SocialNetworkLink(SocialNetwork.YouTube, "https://youtube.com/channel/UCFGwLS7yggzVkP6ozte0m1w"), SocialNetworkLink(SocialNetwork.LinkedIn, "https://www.linkedin.com/company/tangem"), + SocialNetworkLink(SocialNetwork.Discord, "https://discord.gg/7AqTVyqdGS"), ) val accountsRu: List = listOf( - SocialNetworkLink(SocialNetwork.Telegram, "https://t.me/tangem_ru"), + SocialNetworkLink(SocialNetwork.Telegram, "https://t.me/tangem_chat_ru"), SocialNetworkLink(SocialNetwork.Twitter, "https://twitter.com/tangem"), SocialNetworkLink(SocialNetwork.Facebook, "https://m.facebook.com/TangemCards/"), SocialNetworkLink(SocialNetwork.Instagram, "https://instagram.com/tangemcards"), SocialNetworkLink(SocialNetwork.GitHub, "https://github.com/tangem"), SocialNetworkLink(SocialNetwork.YouTube, "https://youtube.com/channel/UCFGwLS7yggzVkP6ozte0m1w"), SocialNetworkLink(SocialNetwork.LinkedIn, "https://www.linkedin.com/company/tangem"), + SocialNetworkLink(SocialNetwork.Discord, "https://discord.gg/7AqTVyqdGS"), ) } \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_discord.xml b/app/src/main/res/drawable/ic_discord.xml new file mode 100644 index 0000000000..88444ddf14 --- /dev/null +++ b/app/src/main/res/drawable/ic_discord.xml @@ -0,0 +1,9 @@ + + + diff --git a/core/res/src/main/res/values/strings.xml b/core/res/src/main/res/values/strings.xml index eda6534993..0bf985e22a 100644 --- a/core/res/src/main/res/values/strings.xml +++ b/core/res/src/main/res/values/strings.xml @@ -27,7 +27,7 @@ Please scan the card Please try again in 30 seconds or scan the card Too many attempts - You have disabled biometric authentication on your phone and will not be able to save wallets in the app. To save wallets, please enable the biometric authentication function in your phone settings. You have disabled biometric authentication on your phone and will not be able to save wallets in the app. To save wallets, please enable the biometric authentication function in your phone settings. + You have disabled biometric authentication on your phone and will not be able to save wallets in the app. To save wallets, please enable the biometric authentication function in your phone settings. Disable this option if you don\'t want this card to be used to reset access codes on other cards of this wallet. Note that you will not be able to reset access code on this card as well. Allows you to use this card to reset access code on other cards in this wallet Disable the ability to reset access code on this card or other cards in this wallet From 565cbbe2b3ae63a5fdfb5eeda2769668a1960d0f Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 10 May 2023 19:17:40 +0400 Subject: [PATCH 07/13] Updated on 2026-08-14 --- gradle/dependencies.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/dependencies.toml b/gradle/dependencies.toml index 47877b952c..5aa61ccaef 100644 --- a/gradle/dependencies.toml +++ b/gradle/dependencies.toml @@ -72,7 +72,7 @@ arrow = "1.2.0-RC" # endregion Other libraries # region Tangem -tangemBlockchainSdk = "develop-209" +tangemBlockchainSdk = "release-app_4.5-212" #tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds tangemCardSdk = "develop-229" #tangemCardSdk = "0.0.1" # Keep it! - used for local builds From f669f626ebea52a9b990c3ac55c57660842248ba Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 10 May 2023 19:36:41 +0300 Subject: [PATCH 08/13] Updated on 2026-08-14 --- .../tap/features/details/redux/DetailsMiddleware.kt | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) 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 cfcc202a2b..b10ad9de2a 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 @@ -10,6 +10,7 @@ import com.tangem.core.analytics.Analytics import com.tangem.domain.common.TapWorkarounds.isTangemTwins import com.tangem.domain.common.util.cardTypesResolver import com.tangem.domain.models.scan.ScanResponse +import com.tangem.tap.* import com.tangem.tap.common.analytics.events.AnalyticsParam import com.tangem.tap.common.analytics.events.Settings import com.tangem.tap.common.extensions.dispatchDialogShow @@ -31,14 +32,6 @@ 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 import com.tangem.tap.features.wallet.redux.WalletAction -import com.tangem.tap.foregroundActivityObserver -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 import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay @@ -84,6 +77,8 @@ class DetailsMiddleware { allowsRequestAccessCodeFromRepository = true, ) .doOnSuccess { scanResponse -> + // if we use biometric, scanResponse in GlobalState is null, and crashes NPE on twin cards + store.dispatch(GlobalAction.SaveScanResponse(scanResponse)) val currentUserWalletId = state.scanResponse ?.let { UserWalletIdBuilder.scanResponse(it).build() } val scannedUserWalletId = UserWalletIdBuilder.scanResponse(scanResponse) From c65798e39fe16900ab6ec3b6c036712e75cef01a Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 11 May 2023 11:42:24 +0300 Subject: [PATCH 09/13] Updated on 2026-08-14 --- .../java/com/tangem/tap/features/wallet/ui/WalletFragment.kt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) 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 1d0cc810ed..4522299310 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 @@ -239,7 +239,10 @@ class WalletFragment : Fragment(R.layout.fragment_wallet), SafeStoreSubscriber Date: Thu, 11 May 2023 13:39:03 +0300 Subject: [PATCH 10/13] Updated on 2026-08-14 --- app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt b/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt index fc1d4e3958..7d5a21594a 100644 --- a/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt @@ -14,6 +14,7 @@ import com.tangem.tap.common.extensions.dispatchOnMain import com.tangem.tap.common.extensions.setContext import com.tangem.tap.common.redux.global.GlobalAction import com.tangem.tap.domain.model.UserWallet +import com.tangem.tap.domain.model.builders.UserWalletIdBuilder import com.tangem.tap.domain.walletStores.WalletStoresError import com.tangem.tap.features.details.redux.walletconnect.WalletConnectAction import com.tangem.tap.features.disclaimer.createDisclaimer @@ -36,7 +37,10 @@ class TapWalletManager { suspend fun onWalletSelected(userWallet: UserWallet, refresh: Boolean, sendAnalyticsEvent: Boolean) { // do nothing if its the same wallet - if (store.state.globalState.scanResponse == userWallet.scanResponse) return + store.state.globalState.scanResponse?.let { + val userWalletId = UserWalletIdBuilder.scanResponse(it).build() + if (userWalletId == userWallet.walletId) return + } Analytics.setContext(userWallet.scanResponse) if (sendAnalyticsEvent) { From 35566a092c044894bc62c2353cf3628b7a768269 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 11 May 2023 13:26:54 +0400 Subject: [PATCH 11/13] Updated on 2026-08-14 --- .../tangem/tap/features/wallet/redux/WalletState.kt | 6 +++++- .../tap/features/wallet/ui/WalletDetailsFragment.kt | 12 +++++++++--- .../features/wallet/ui/utils/WalletDataOperations.kt | 7 +++---- .../features/wallet/ui/wallet/SingleWalletView.kt | 10 ++-------- 4 files changed, 19 insertions(+), 16 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletState.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletState.kt index a7e540bf43..772bd7a3e7 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletState.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletState.kt @@ -1,6 +1,7 @@ package com.tangem.tap.features.wallet.redux import android.graphics.Bitmap +import com.tangem.blockchain.common.AmountType import com.tangem.blockchain.common.Blockchain import com.tangem.blockchain.common.WalletManager import com.tangem.domain.common.util.cardTypesResolver @@ -16,6 +17,7 @@ import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsState import com.tangem.tap.features.wallet.models.Currency import com.tangem.tap.store import org.rekotlin.StateType +import java.math.BigDecimal import kotlin.properties.ReadOnlyProperty data class WalletState( @@ -103,6 +105,9 @@ data class WalletState( it.derivationPath?.rawPath == currency.derivationPath } } + + fun getBlockchainAmount(currency: Currency): BigDecimal = + getWalletManager(currency)?.wallet?.amounts?.get(AmountType.Coin)?.value ?: BigDecimal.ZERO } enum class ProgressState : WidgetState { Loading, Refreshing, Done, Error } @@ -114,7 +119,6 @@ enum class ErrorType { sealed class WalletMainButton(enabled: Boolean) : Button(enabled) { class SendButton(enabled: Boolean) : WalletMainButton(enabled) - class CreateWalletButton(enabled: Boolean) : WalletMainButton(enabled) } data class Artwork( 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 d4556d790b..4ea69fa30f 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 @@ -55,6 +55,7 @@ import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import timber.log.Timber +import java.math.BigDecimal import javax.inject.Inject /** @@ -128,7 +129,8 @@ class WalletDetailsFragment : (WalletState::selectedWalletData or WalletState::isExchangeServiceFeatureOn) { state -> val selectedWallet = state.selectedWalletData if (selectedWallet != null) { - setupButtonsRow(selectedWallet, state.isExchangeServiceFeatureOn) + val blockchainAmount: BigDecimal = state.getBlockchainAmount(selectedWallet.currency) + setupButtonsRow(selectedWallet, state.isExchangeServiceFeatureOn, blockchainAmount) } } (WalletState::state or WalletState::error) { state -> @@ -281,7 +283,11 @@ class WalletDetailsFragment : } } - private fun setupButtonsRow(selectedWallet: WalletDataModel, isExchangeServiceFeatureOn: Boolean) { + private fun setupButtonsRow( + selectedWallet: WalletDataModel, + isExchangeServiceFeatureOn: Boolean, + blockchainAmount: BigDecimal, + ) { val exchangeManager = store.state.globalState.exchangeManager binding.rowButtons.apply { onBuyClick = { store.dispatch(WalletAction.TradeCryptoAction.Buy()) } @@ -310,7 +316,7 @@ class WalletDetailsFragment : binding.rowButtons.updateButtonsVisibility( actions = actions, exchangeServiceFeatureOn = isExchangeServiceFeatureOn, - sendAllowed = selectedWallet.mainButton.enabled, + sendAllowed = selectedWallet.mainButton(blockchainAmount).enabled, ) } diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/utils/WalletDataOperations.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/utils/WalletDataOperations.kt index 042426cb80..f40f0d38dc 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/utils/WalletDataOperations.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/utils/WalletDataOperations.kt @@ -18,10 +18,9 @@ import com.tangem.tap.features.wallet.redux.utils.UNKNOWN_AMOUNT_SIGN import com.tangem.tap.network.exchangeServices.CurrencyExchangeManager import java.math.BigDecimal -internal val WalletDataModel.mainButton: WalletMainButton - get() = WalletMainButton.SendButton( - enabled = !status.amount.isZero() && status.pendingTransactions.isEmpty(), - ) +internal fun WalletDataModel.mainButton(blockchainAmount: BigDecimal): WalletMainButton = WalletMainButton.SendButton( + enabled = !isEmptyAmount && status.pendingTransactions.isEmpty() && !blockchainAmount.isZero(), +) internal fun WalletDataModel.getFormattedAmount(): String { return status.amount.toFormattedCurrencyString( 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 5668693e18..5d49193785 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 @@ -12,7 +12,6 @@ import com.tangem.tap.features.wallet.models.Currency import com.tangem.tap.features.wallet.models.PendingTransaction import com.tangem.tap.features.wallet.models.PendingTransactionType import com.tangem.tap.features.wallet.redux.WalletAction -import com.tangem.tap.features.wallet.redux.WalletMainButton import com.tangem.tap.features.wallet.redux.WalletState import com.tangem.tap.features.wallet.ui.BalanceWidget import com.tangem.tap.features.wallet.ui.MultipleAddressUiHelper @@ -175,15 +174,10 @@ class SingleWalletView : WalletView() { binding?.rowButtons?.updateButtonsVisibility( actions = actions, exchangeServiceFeatureOn = isExchangeServiceFeatureEnabled, - sendAllowed = walletData.mainButton.enabled, + sendAllowed = walletData.mainButton(walletData.status.amount).enabled, ) - rowButtons.onSendClick = { - when (walletData.mainButton) { - is WalletMainButton.SendButton -> store.dispatch(WalletAction.Send()) - is WalletMainButton.CreateWalletButton -> store.dispatch(WalletAction.CreateWallet) - } - } + rowButtons.onSendClick = { store.dispatch(WalletAction.Send()) } } private fun setupAddressCard(state: WalletState, binding: FragmentWalletBinding) = with(binding.lAddress) { From 111ef128250bc40c88b356f6c8740aeb4f4aef94 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 11 May 2023 16:39:12 +0500 Subject: [PATCH 12/13] Updated on 2026-08-14 --- gradle/dependencies.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/dependencies.toml b/gradle/dependencies.toml index 5aa61ccaef..050f3cb2bc 100644 --- a/gradle/dependencies.toml +++ b/gradle/dependencies.toml @@ -72,7 +72,7 @@ arrow = "1.2.0-RC" # endregion Other libraries # region Tangem -tangemBlockchainSdk = "release-app_4.5-212" +tangemBlockchainSdk = "release-app_4.5-215" #tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds tangemCardSdk = "develop-229" #tangemCardSdk = "0.0.1" # Keep it! - used for local builds From c8e900df8fd29f6ac0812c196605e82b878d356e Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 12 May 2023 17:53:20 +0800 Subject: [PATCH 13/13] Updated on 2026-08-14 --- .../BiometricUserWalletsListManager.kt | 25 +------------------ gradle/dependencies.toml | 2 +- 2 files changed, 2 insertions(+), 25 deletions(-) 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 ad29020162..78f74e0c3d 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 @@ -54,16 +54,6 @@ internal class BiometricUserWalletsListManager( get() = keysRepository.hasSavedEncryptionKeys() override suspend fun unlock(): CompletionResult { - // Occurs when user has locked user wallets after unlocking with biometry - // e.g. after biometric storage master key invalidation - if (state.value.hasLockedUserWalletsAfterUnlock) { - return CompletionResult.Failure( - error = UserWalletsListError.UnableToUnlockUserWallets( - cause = IllegalStateException("Permanently locked"), - ), - ) - } - return unlockWithBiometryInternal() .mapFailure { error -> if (error is UserWalletsListError) { @@ -216,10 +206,7 @@ internal class BiometricUserWalletsListManager( .map { state.update { prevState -> val hasLockedUserWallets = prevState.userWallets.any { it.isLocked } - prevState.copy( - isLocked = hasLockedUserWallets, - hasLockedUserWalletsAfterUnlock = hasLockedUserWallets, - ) + prevState.copy(isLocked = hasLockedUserWallets) } } } @@ -262,15 +249,6 @@ internal class BiometricUserWalletsListManager( } } } - .doOnFailure { error -> - if (error is UserWalletsListError.EncryptionKeyInvalidated) { - state.update { prevState -> - prevState.copy( - hasLockedUserWalletsAfterUnlock = true, - ) - } - } - } } private suspend fun getSavedUserWallets(): CompletionResult> { @@ -334,6 +312,5 @@ internal class BiometricUserWalletsListManager( val userWallets: List = emptyList(), val selectedUserWalletId: UserWalletId? = null, val isLocked: Boolean = true, - val hasLockedUserWalletsAfterUnlock: Boolean = false, ) } \ No newline at end of file diff --git a/gradle/dependencies.toml b/gradle/dependencies.toml index 050f3cb2bc..91c34ec357 100644 --- a/gradle/dependencies.toml +++ b/gradle/dependencies.toml @@ -74,7 +74,7 @@ arrow = "1.2.0-RC" # region Tangem tangemBlockchainSdk = "release-app_4.5-215" #tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds -tangemCardSdk = "develop-229" +tangemCardSdk = "develop-235" #tangemCardSdk = "0.0.1" # Keep it! - used for local builds # endregion Tangem