diff --git a/app/src/main/java/com/tangem/tap/common/DialogManager.kt b/app/src/main/java/com/tangem/tap/common/DialogManager.kt index 7f3bf094a7..56ce4dd062 100644 --- a/app/src/main/java/com/tangem/tap/common/DialogManager.kt +++ b/app/src/main/java/com/tangem/tap/common/DialogManager.kt @@ -15,6 +15,7 @@ import com.tangem.tap.features.onboarding.products.wallet.ui.dialogs.BackupInPro import com.tangem.tap.features.onboarding.products.wallet.ui.dialogs.ConfirmDiscardingBackupDialog import com.tangem.tap.features.onboarding.products.wallet.ui.dialogs.UnfinishedBackupFoundDialog import com.tangem.tap.features.wallet.ui.dialogs.ScanFailsDialog +import com.tangem.tap.features.wallet.ui.dialogs.SimpleOkDialog import com.tangem.tap.store import com.tangem.wallet.R import org.rekotlin.StoreSubscriber @@ -48,6 +49,8 @@ class DialogManager : StoreSubscriber { if (dialog != null) return dialog = when (state.dialog) { + is AppDialog.SimpleOkDialog -> SimpleOkDialog.create(state.dialog, context) + is AppDialog.SimpleOkDialogRes -> SimpleOkDialog.create(state.dialog, context) is AppDialog.ScanFailsDialog -> ScanFailsDialog.create(context) is AppDialog.AddressInfoDialog -> AddressInfoBottomSheetDialog(state.dialog, context) is TwinCardsAction.Wallet.ShowInterruptDialog -> CreateWalletInterruptDialog.create(state.dialog, context) diff --git a/app/src/main/java/com/tangem/tap/common/extensions/WalletManager.kt b/app/src/main/java/com/tangem/tap/common/extensions/WalletManager.kt index 3d33ceeaa8..f552e835f6 100644 --- a/app/src/main/java/com/tangem/tap/common/extensions/WalletManager.kt +++ b/app/src/main/java/com/tangem/tap/common/extensions/WalletManager.kt @@ -8,18 +8,26 @@ import com.tangem.tap.domain.TapError import com.tangem.tap.domain.extensions.amountToCreateAccount import com.tangem.tap.domain.getFirstToken import com.tangem.tap.domain.topup.TradeCryptoHelper +import com.tangem.tap.features.demo.isDemoWallet import com.tangem.tap.features.wallet.redux.AddressData import com.tangem.tap.features.wallet.redux.reducers.createAddressesData import com.tangem.tap.network.NetworkConnectivity import com.tangem.tap.store +import timber.log.Timber /** [REDACTED_AUTHOR] */ suspend fun WalletManager.safeUpdate(): Result = try { - update() - Result.Success(wallet) + if (isDemoWallet()) { + Result.Success(wallet) + } else { + update() + Result.Success(wallet) + } } catch (exception: Exception) { + Timber.e(exception) + if (!NetworkConnectivity.getInstance().isOnlineOrConnecting()) { Result.Failure(TapError.NoInternetConnection) } else { diff --git a/app/src/main/java/com/tangem/tap/common/redux/StateDialog.kt b/app/src/main/java/com/tangem/tap/common/redux/StateDialog.kt index bcde7dae3b..ecec5fb9f4 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/StateDialog.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/StateDialog.kt @@ -9,6 +9,8 @@ import com.tangem.tap.features.wallet.redux.Currency interface StateDialog sealed class AppDialog : StateDialog { + data class SimpleOkDialog(val header: String, val message: String) : AppDialog() + data class SimpleOkDialogRes(val headerId: Int, val messageId: Int) : AppDialog() object ScanFailsDialog : AppDialog() data class AddressInfoDialog( val currency: Currency, 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 cfd628a093..9e6fcd5de6 100644 --- a/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt @@ -10,9 +10,11 @@ import com.tangem.tap.currenciesRepository import com.tangem.tap.domain.TapWorkarounds.isStart2Coin import com.tangem.tap.domain.TapWorkarounds.isTestCard import com.tangem.tap.domain.configurable.config.ConfigManager -import com.tangem.tap.domain.extensions.* +import com.tangem.tap.domain.extensions.makePrimaryWalletManager +import com.tangem.tap.domain.extensions.makeWalletManagersForApp import com.tangem.tap.domain.tasks.product.ScanResponse import com.tangem.tap.domain.tokens.CardCurrencies +import com.tangem.tap.features.demo.isDemoCard import com.tangem.tap.features.wallet.redux.Currency import com.tangem.tap.features.wallet.redux.WalletAction import com.tangem.tap.network.NetworkConnectivity @@ -207,6 +209,9 @@ class TapWalletManager { data.getBlockchain() == Blockchain.Unknown && !data.card.isMultiwalletAllowed -> { WalletAction.LoadData.Failure(TapError.UnknownBlockchain) } + data.isDemoCard() -> { + return null + } data.card.wallets.isEmpty() -> { WalletAction.EmptyWallet } diff --git a/app/src/main/java/com/tangem/tap/domain/configurable/warningMessage/WarningMessagesManager.kt b/app/src/main/java/com/tangem/tap/domain/configurable/warningMessage/WarningMessagesManager.kt index 3cdf14dfea..663bbcbd6c 100644 --- a/app/src/main/java/com/tangem/tap/domain/configurable/warningMessage/WarningMessagesManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/configurable/warningMessage/WarningMessagesManager.kt @@ -10,7 +10,7 @@ import com.tangem.wallet.R [REDACTED_AUTHOR] */ class WarningMessagesManager( - private val warningLoader: RemoteWarningLoader, + private val warningLoader: RemoteWarningLoader, ) { private val warningsList: MutableList = mutableListOf() @@ -33,15 +33,15 @@ class WarningMessagesManager( fun getWarnings(location: WarningMessage.Location, forBlockchains: List = emptyList()): List { return warningsList - .filter { !it.isHidden && it.location.contains(location) } - .filter { - val list = it.blockchainList - when { - list == null -> true - list.containsAny(forBlockchains) -> true - else -> false - } + .filter { !it.isHidden && it.location.contains(location) } + .filter { + val list = it.blockchainList + when { + list == null -> true + list.containsAny(forBlockchains) -> true + else -> false } + } } fun hideWarning(warning: WarningMessage): Boolean { @@ -49,7 +49,7 @@ class WarningMessagesManager( return when { foundWarning == null -> false foundWarning.type == WarningMessage.Type.Temporary - || foundWarning.type == WarningMessage.Type.AppRating -> { + || foundWarning.type == WarningMessage.Type.AppRating -> { if (foundWarning.isHidden) { false } else { @@ -80,32 +80,32 @@ class WarningMessagesManager( companion object { fun devCardWarning(): WarningMessage = WarningMessage( - "", - "", - type = WarningMessage.Type.Permanent, - priority = WarningMessage.Priority.Critical, - listOf(WarningMessage.Location.MainScreen), - null, - R.string.alert_title, - R.string.alert_developer_card, - WarningMessage.Origin.Local + "", + "", + type = WarningMessage.Type.Permanent, + priority = WarningMessage.Priority.Critical, + listOf(WarningMessage.Location.MainScreen), + null, + R.string.alert_title, + R.string.alert_developer_card, + WarningMessage.Origin.Local ) fun alreadySignedHashesWarning(): WarningMessage = WarningMessage( - "", - "", - type = WarningMessage.Type.Temporary, - priority = WarningMessage.Priority.Info, - listOf(WarningMessage.Location.MainScreen), - null, - R.string.alert_title, - R.string.alert_card_signed_transactions, - WarningMessage.Origin.Local + "", + "", + type = WarningMessage.Type.Temporary, + priority = WarningMessage.Priority.Info, + listOf(WarningMessage.Location.MainScreen), + null, + R.string.alert_title, + R.string.alert_card_signed_transactions, + WarningMessage.Origin.Local ) fun signedHashesMultiWalletWarning(): WarningMessage = WarningMessage( title = "", - message = "", + message = "", type = WarningMessage.Type.Temporary, priority = WarningMessage.Priority.Info, location = listOf(WarningMessage.Location.MainScreen), @@ -117,15 +117,15 @@ class WarningMessagesManager( ) fun appRatingWarning(): WarningMessage = WarningMessage( - "", - "", - WarningMessage.Type.AppRating, - WarningMessage.Priority.Info, - listOf(WarningMessage.Location.MainScreen), - null, - R.string.warning_rate_app_title, - R.string.warning_rate_app_message, - WarningMessage.Origin.Local + "", + "", + WarningMessage.Type.AppRating, + WarningMessage.Priority.Info, + listOf(WarningMessage.Location.MainScreen), + null, + R.string.warning_rate_app_title, + R.string.warning_rate_app_message, + WarningMessage.Origin.Local ) fun isAlreadySignedHashesWarning(warning: WarningMessage): Boolean { @@ -133,15 +133,15 @@ class WarningMessagesManager( } fun onlineVerificationFailed(): WarningMessage = WarningMessage( - "", - "", - type = WarningMessage.Type.Permanent, - priority = WarningMessage.Priority.Critical, - listOf(WarningMessage.Location.MainScreen), - null, - R.string.warning_failed_to_verify_card_title, - R.string.warning_failed_to_verify_card_message, - WarningMessage.Origin.Local + "", + "", + type = WarningMessage.Type.Permanent, + priority = WarningMessage.Priority.Critical, + listOf(WarningMessage.Location.MainScreen), + null, + R.string.warning_failed_to_verify_card_title, + R.string.warning_failed_to_verify_card_message, + WarningMessage.Origin.Local ) fun remainingSignaturesNotEnough(remainingSignatures: Int): WarningMessage = WarningMessage( @@ -169,6 +169,18 @@ class WarningMessagesManager( WarningMessage.Origin.Local ) + fun demoCardWarning(): WarningMessage = WarningMessage( + "", + "", + type = WarningMessage.Type.Permanent, + priority = WarningMessage.Priority.Critical, + listOf(WarningMessage.Location.MainScreen), + null, + R.string.alert_title, + R.string.alert_demo_message, + WarningMessage.Origin.Local + ) + const val REMAINING_SIGNATURES_WARNING = 10 } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/tasks/product/CreateProductWalletTask.kt b/app/src/main/java/com/tangem/tap/domain/tasks/product/CreateProductWalletTask.kt index e75da7ca72..29ae9d45bc 100644 --- a/app/src/main/java/com/tangem/tap/domain/tasks/product/CreateProductWalletTask.kt +++ b/app/src/main/java/com/tangem/tap/domain/tasks/product/CreateProductWalletTask.kt @@ -5,8 +5,10 @@ import com.tangem.common.card.EllipticCurve import com.tangem.common.core.CardSession import com.tangem.common.core.CardSessionRunnable import com.tangem.common.core.TangemSdkError +import com.tangem.common.extensions.ByteArrayKey import com.tangem.common.extensions.guard import com.tangem.common.extensions.toMapKey +import com.tangem.common.hdWallet.DerivationPath import com.tangem.operations.CommandResponse import com.tangem.operations.backup.PrimaryCard import com.tangem.operations.backup.StartPrimaryCardLinkingTask @@ -16,10 +18,12 @@ import com.tangem.operations.wallet.CreateWalletResponse import com.tangem.operations.wallet.CreateWalletTask import com.tangem.tap.domain.ProductType import com.tangem.tap.domain.TapWorkarounds.getTangemNoteBlockchain +import com.tangem.tap.domain.TapWorkarounds.isTestCard import com.tangem.tap.domain.tasks.product.CreateWalletsTask import com.tangem.tap.domain.tasks.product.KeyWalletPublicKey import com.tangem.tap.domain.tasks.product.ProductCommandProcessor import com.tangem.tap.domain.tasks.product.getCurvesForNonCreatedWallets +import com.tangem.tap.features.demo.DemoHelper data class CreateProductWalletTaskResponse( @@ -96,25 +100,27 @@ private class CreateWalletTangemNote : ProductCommandProcessor { + private lateinit var card: Card private var primaryCard: PrimaryCard? = null - private var createWalletResponse: CreateWalletResponse? = null override fun proceed( card: Card, session: CardSession, callback: (result: CompletionResult) -> Unit, ) { + this.card = card val curves = card.getCurvesForNonCreatedWallets() + CreateWalletsTask(curves).run(session) { result -> when (result) { is CompletionResult.Success -> { - createWalletResponse = result.data.createWalletResponses[0] + val createWalletResponses = result.data.createWalletResponses when { card.settings.isBackupAllowed -> { - linkPrimaryCard(session, callback) + linkPrimaryCard(createWalletResponses, session, callback) } card.settings.isHDWalletAllowed -> { - deriveKeys(session, callback) + deriveKeys(createWalletResponses, session, callback) } else -> { callback( @@ -124,7 +130,6 @@ private class CreateWalletTangemWallet : ProductCommandProcessor callback(CompletionResult.Failure(result.error)) } @@ -132,6 +137,7 @@ private class CreateWalletTangemWallet : ProductCommandProcessor, session: CardSession, callback: (result: CompletionResult) -> Unit, ) { @@ -139,7 +145,7 @@ private class CreateWalletTangemWallet : ProductCommandProcessor { primaryCard = result.data - deriveKeys(session, callback) + deriveKeys(createWalletResponse, session, callback) } is CompletionResult.Failure -> { callback(CompletionResult.Failure(result.error)) @@ -149,28 +155,26 @@ private class CreateWalletTangemWallet : ProductCommandProcessor, session: CardSession, callback: (result: CompletionResult) -> Unit, ) { - val derivationPaths = listOf(Blockchain.Bitcoin, Blockchain.Ethereum) - .mapNotNull { it.derivationPath() } - val response = createWalletResponse.guard { + val map = mutableMapOf>() + createWalletResponse.forEach { response -> + val blockchainsForCurve = getBlockchains(response.cardId).filter { + it.getSupportedCurves().contains(response.wallet.curve) + } + val derivationPaths = blockchainsForCurve.mapNotNull { it.derivationPath() } + if (derivationPaths.isNotEmpty()) { + map[response.wallet.publicKey.toMapKey()] = derivationPaths + } + } + if (map.isEmpty()) { callback(CompletionResult.Failure(TangemSdkError.UnknownError())) return } - if (derivationPaths.isNullOrEmpty()) { - callback( - CompletionResult.Success( - CreateProductWalletTaskResponse( - card = session.environment.card!!, primaryCard = primaryCard - ) - ) - ) - return - } - - DeriveMultipleWalletPublicKeysTask(mapOf(response.wallet.publicKey.toMapKey() to derivationPaths)) + DeriveMultipleWalletPublicKeysTask(map) .run(session) { result -> when (result) { is CompletionResult.Success -> { @@ -188,4 +192,12 @@ private class CreateWalletTangemWallet : ProductCommandProcessor { + return when { + DemoHelper.isDemoCardId(cardId) -> DemoHelper.config.demoBlockchains + card.isTestCard -> listOf(Blockchain.BitcoinTestnet, Blockchain.EthereumTestnet) + else -> listOf(Blockchain.Bitcoin, Blockchain.Ethereum) + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/tokens/CurrenciesRepository.kt b/app/src/main/java/com/tangem/tap/domain/tokens/CurrenciesRepository.kt index f56e6f7bc8..114746a06f 100644 --- a/app/src/main/java/com/tangem/tap/domain/tokens/CurrenciesRepository.kt +++ b/app/src/main/java/com/tangem/tap/domain/tokens/CurrenciesRepository.kt @@ -13,9 +13,11 @@ import com.tangem.tap.common.extensions.appendIf import com.tangem.tap.common.extensions.readJsonFileToString import com.tangem.tap.domain.extensions.getCustomIconUrl import com.tangem.tap.domain.extensions.setCustomIconUrl +import com.tangem.tap.features.demo.DemoHelper import com.tangem.tap.network.createMoshi class CurrenciesRepository(val context: Application) { + private val moshi = createMoshi() private val blockchainsAdapter: JsonAdapter> = moshi.adapter( Types.newParameterizedType(List::class.java, Blockchain::class.java) @@ -28,10 +30,13 @@ class CurrenciesRepository(val context: Application) { ) fun loadCardCurrencies(cardId: String): CardCurrencies? { - val blockchains = loadSavedBlockchains(cardId) + val blockchains = loadSavedBlockchains(cardId).toMutableSet() + if (DemoHelper.isDemoCardId(cardId)) { + blockchains.addAll(DemoHelper.config.demoBlockchains) + } if (blockchains.isEmpty()) return null - return CardCurrencies(loadSavedTokens(cardId), blockchains) + return CardCurrencies(loadSavedTokens(cardId), blockchains.toList()) } fun saveCardCurrencies(cardId: String, currencies: CardCurrencies) { @@ -164,11 +169,10 @@ class CurrenciesRepository(val context: Application) { return excludeUnsupportedBlockchains(blockchains) } - //TODO: move to the App settings private fun excludeUnsupportedBlockchains(blockchains: List): List { return blockchains.toMutableList().apply { removeAll(listOf( - Blockchain.Fantom, Blockchain.FantomTestnet +// Blockchain.Fantom, Blockchain.FantomTestnet )) } } diff --git a/app/src/main/java/com/tangem/tap/domain/topup/TopUpManager.kt b/app/src/main/java/com/tangem/tap/domain/topup/TopUpManager.kt index 9dd6e91960..c5e74334a4 100644 --- a/app/src/main/java/com/tangem/tap/domain/topup/TopUpManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/topup/TopUpManager.kt @@ -6,6 +6,7 @@ import com.tangem.blockchain.common.Amount import com.tangem.blockchain.common.AmountType import com.tangem.blockchain.common.Token import com.tangem.blockchain.extensions.Result +import com.tangem.tap.common.extensions.safeUpdate import com.tangem.tap.common.redux.global.GlobalAction import com.tangem.tap.domain.TangemSigner import com.tangem.tap.store @@ -14,7 +15,7 @@ import java.math.BigDecimal class TopUpManager { suspend fun topUpTestErc20Tokens(walletManager: EthereumWalletManager, token: Token) { - walletManager.update() + walletManager.safeUpdate() val amountToSend = Amount(walletManager.wallet.blockchain) val destinationAddress = token.contractAddress diff --git a/app/src/main/java/com/tangem/tap/domain/twins/TwinCardsWidget.kt b/app/src/main/java/com/tangem/tap/domain/twins/TwinCardsWidget.kt index becb529e80..3a163149e5 100644 --- a/app/src/main/java/com/tangem/tap/domain/twins/TwinCardsWidget.kt +++ b/app/src/main/java/com/tangem/tap/domain/twins/TwinCardsWidget.kt @@ -16,6 +16,7 @@ import com.tangem.tangem_sdk_new.ui.widget.leapfrogWidget.LeapfrogWidget */ class TwinsCardWidget( val leapfrogWidget: LeapfrogWidget, + private val deviceScaleFactor: Float = 1f, val getTopOfAnchorViewForActivateState: () -> Float ) { @@ -87,20 +88,20 @@ class TwinsCardWidget( return when (cardNumber) { TwinCardNumber.First -> { TwinsCardProperties( - xTranslation = -120f, - yTranslation = -220f, + xTranslation = -120f * deviceScaleFactor, + yTranslation = -220f * deviceScaleFactor, rotation = -3f, elevation = 1f, - scale = 1f, + scale = deviceScaleFactor, ) } TwinCardNumber.Second -> { TwinsCardProperties( - xTranslation = 170f, - yTranslation = 170f, + xTranslation = 170f * deviceScaleFactor, + yTranslation = 170f * deviceScaleFactor, rotation = -3f, elevation = 0f, - scale = 1f, + scale = deviceScaleFactor, ) } } @@ -123,7 +124,7 @@ class TwinsCardWidget( xTranslation = twinProperties.xTranslation, yTranslation = twinProperties.yTranslation - topOfAnchorView, rotation = 0f, - scale = twinProperties.scale - 0.5f, + scale = (twinProperties.scale - 0.5f) * deviceScaleFactor, ) } diff --git a/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectSdkHelper.kt b/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectSdkHelper.kt index e56b4d9ce6..1d0dc4a9e0 100644 --- a/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectSdkHelper.kt +++ b/app/src/main/java/com/tangem/tap/domain/walletconnect/WalletConnectSdkHelper.kt @@ -15,6 +15,7 @@ import com.tangem.common.extensions.toHexString import com.tangem.crypto.CryptoUtils import com.tangem.operations.sign.SignHashCommand import com.tangem.tap.common.analytics.Analytics +import com.tangem.tap.common.extensions.safeUpdate import com.tangem.tap.common.extensions.toFormattedString import com.tangem.tap.features.details.redux.walletconnect.* import com.tangem.tap.features.details.ui.walletconnect.dialogs.PersonalSignDialogData @@ -35,18 +36,11 @@ class WalletConnectSdkHelper { id: Long, type: WcTransactionType, ): WcTransactionData? { - val walletManager = getWalletManager(session) ?: return null - try { - walletManager.update() - } catch (exception: Exception) { - Timber.e(exception) - return null - } + walletManager.safeUpdate() val wallet = walletManager.wallet - val balance = - wallet.amounts[AmountType.Coin]?.value ?: return null + val balance = wallet.amounts[AmountType.Coin]?.value ?: return null val gas = transaction.gas?.hexToBigDecimal() ?: transaction.gasLimit?.hexToBigDecimal() diff --git a/app/src/main/java/com/tangem/tap/features/demo/DemoHelper.kt b/app/src/main/java/com/tangem/tap/features/demo/DemoHelper.kt new file mode 100644 index 0000000000..f80e04df6b --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/demo/DemoHelper.kt @@ -0,0 +1,153 @@ +package com.tangem.tap.features.demo + +import com.tangem.blockchain.common.* +import com.tangem.blockchain.extensions.Result +import com.tangem.blockchain.extensions.SimpleResult +import com.tangem.tap.common.extensions.dispatchNotification +import com.tangem.tap.common.redux.AppState +import com.tangem.tap.domain.tasks.product.ScanResponse +import com.tangem.tap.features.details.redux.walletconnect.WalletConnectAction +import com.tangem.tap.features.onboarding.products.wallet.redux.BackupAction +import com.tangem.tap.features.wallet.redux.WalletAction +import com.tangem.tap.store +import com.tangem.wallet.BuildConfig +import com.tangem.wallet.R +import org.rekotlin.Action +import java.math.BigDecimal + +/** +[REDACTED_AUTHOR] + */ +interface DemoMiddleware { + fun tryHandle(config: DemoConfig, scanResponse: ScanResponse, action: Action): Boolean +} + +object DemoHelper { + val config = DemoConfig() + + private val demoMiddlewares = listOf( + DemoOnboardingNoteMiddleware(), + ) + + private val disabledActionFeatures = listOf( + WalletConnectAction.StartWalletConnect::class.java, + WalletAction.TradeCryptoAction.Buy::class.java, + WalletAction.TradeCryptoAction.Sell::class.java, + BackupAction.StartBackup::class.java, + WalletAction.ExploreAddress::class.java + ) + + fun isDemoCard(scanResponse: ScanResponse): Boolean = isDemoCardId(scanResponse.card.cardId) + + fun isDemoCardId(cardId: String): Boolean = config.isDemoCardId(cardId) + + fun tryHandle(appState: () -> AppState?, action: Action): Boolean { + val scanResponse = getScanResponse(appState) ?: return false + if (!scanResponse.isDemoCard()) return false + + demoMiddlewares.forEach { + if (it.tryHandle(config, scanResponse, action)) return true + } + + disabledActionFeatures.firstOrNull { it == action::class.java }?.let { + store.dispatchNotification(R.string.alert_demo_feature_disabled) + return true + } + + return false + } + + fun injectDemoBalance(walletManager: WalletManager?) { + val manager = walletManager ?: return + + val blockchain = walletManager.wallet.blockchain + val amount = config.getBalance(blockchain) + manager.wallet.setAmount(amount) + } + + private fun getScanResponse(appState: () -> AppState?): ScanResponse? { + val state = appState() ?: return null + + return state.globalState.onboardingState.onboardingManager?.scanResponse + ?: state.globalState.scanResponse + } +} + +class DemoConfig { + + val demoBlockchains = listOf( + Blockchain.Bitcoin, + Blockchain.Ethereum, + Blockchain.Dogecoin, + Blockchain.Solana, + ) + + val demoCardIds: List by lazy { + val demoIds = (releaseDemoCardIds + testDemoCardIds).toMutableList() + if (BuildConfig.DEBUG) demoIds.addAll(debugTestDemoCardIds) + + return@lazy demoIds.distinct() + } + + private val walletBalances: Map = mapOf( + Blockchain.Bitcoin to Amount(0.028.toBigDecimal(), Blockchain.Bitcoin), + Blockchain.Ethereum to Amount(0.2311.toBigDecimal(), Blockchain.Ethereum), + Blockchain.Dogecoin to Amount(1450.025.toBigDecimal(), Blockchain.Dogecoin), + Blockchain.Solana to Amount(13.246.toBigDecimal(), Blockchain.Solana), + ) + + fun isDemoCardId(cardId: String): Boolean = demoCardIds.contains(cardId) + + fun getBalance(blockchain: Blockchain): Amount = walletBalances[blockchain]?.copy() + ?: Amount(BigDecimal.ZERO, blockchain).copy() + + private val releaseDemoCardIds = mutableListOf( + + ) + + private val testDemoCardIds = listOf( + "FB20000000000186", // Note ETH + "FB10000000000196", // Note BTC + "FB30000000000176", // Wallet + //TODO: delete bellow ids before 3.28 release + "AB01000000045060", // Note BTC + "AB02000000045028", // Note ETH + "AC79000000000004", // Wallet 4.46 + ) + + private val debugTestDemoCardIds = listOf( + "AB01000000045060", // Note BTC + "AB02000000045028", // Note ETH + "AC79000000000004", // Wallet 4.46 + ) +} + +class DemoTransactionSender( + private val walletManager: WalletManager, + private val sender: TransactionSender = walletManager as TransactionSender +) : TransactionSender { + + override suspend fun getFee(amount: Amount, destination: String): Result> = + sender.getFee(amount, destination) + + override suspend fun send(transactionData: TransactionData, signer: TransactionSigner): SimpleResult { + val dataToSign = randomString(32).toByteArray() + val signerResponse = signer.sign(dataToSign, walletManager.wallet.cardId, walletManager.wallet.publicKey) + return SimpleResult.Failure(Exception(ID)) + } + + private fun randomInt(from: Int, to: Int): Int = kotlin.random.Random.nextInt(from, to) + + private fun randomString(length: Int): String { + val charPool: List = ('a'..'z') + ('A'..'Z') + ('0'..'9') + return (1..length) + .map { randomInt(0, charPool.size) } + .map(charPool::get) + .joinToString("") + } + + companion object { + val ID = DemoTransactionSender::class.java.simpleName + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/demo/DemoMiddlewares.kt b/app/src/main/java/com/tangem/tap/features/demo/DemoMiddlewares.kt new file mode 100644 index 0000000000..7537d54bdd --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/demo/DemoMiddlewares.kt @@ -0,0 +1,58 @@ +package com.tangem.tap.features.demo + +import com.tangem.common.extensions.guard +import com.tangem.tap.common.extensions.withMainContext +import com.tangem.tap.domain.extensions.makePrimaryWalletManager +import com.tangem.tap.domain.tasks.product.ScanResponse +import com.tangem.tap.features.onboarding.products.note.redux.OnboardingNoteAction +import com.tangem.tap.features.wallet.redux.Currency +import com.tangem.tap.features.wallet.redux.ProgressState +import com.tangem.tap.scope +import com.tangem.tap.store +import kotlinx.coroutines.launch +import org.rekotlin.Action + +/** +[REDACTED_AUTHOR] + */ +internal class DemoOnboardingNoteMiddleware : DemoMiddleware { + + override fun tryHandle(config: DemoConfig, scanResponse: ScanResponse, action: Action): Boolean { + val globalState = store.state.globalState + val noteState = store.state.onboardingNoteState + + when (action) { + is OnboardingNoteAction.Balance.Update -> { + val walletManager = if (noteState.walletManager != null) { + noteState.walletManager + } else { + val wmFactory = globalState.tapWalletManager.walletManagerFactory + val walletManager = wmFactory.makePrimaryWalletManager(scanResponse).guard { + return false + } + store.dispatch(OnboardingNoteAction.SetWalletManager(walletManager)) + walletManager + } + val balanceAmount = config.getBalance(walletManager.wallet.blockchain) + val loadedBalance = noteState.walletBalance.copy( + value = balanceAmount.value!!, + currency = Currency.Blockchain(walletManager.wallet.blockchain), + state = ProgressState.Done, + error = null, + criticalError = null + ) + walletManager.wallet.setAmount(balanceAmount) + + scope.launch { + withMainContext { + store.dispatch(OnboardingNoteAction.Balance.Set(loadedBalance)) + store.dispatch(OnboardingNoteAction.Balance.SetCriticalError(loadedBalance.criticalError)) + store.dispatch(OnboardingNoteAction.Balance.SetNonCriticalError(loadedBalance.error)) + } + } + return true + } + } + return false + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/demo/Extentions.kt b/app/src/main/java/com/tangem/tap/features/demo/Extentions.kt new file mode 100644 index 0000000000..f32c079efc --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/demo/Extentions.kt @@ -0,0 +1,12 @@ +package com.tangem.tap.features.demo + +import com.tangem.blockchain.common.Wallet +import com.tangem.blockchain.common.WalletManager +import com.tangem.tap.domain.tasks.product.ScanResponse + +/** +[REDACTED_AUTHOR] + */ +fun ScanResponse.isDemoCard(): Boolean = DemoHelper.isDemoCardId(card.cardId) +fun WalletManager.isDemoWallet(): Boolean = DemoHelper.isDemoCardId(wallet.cardId) +fun Wallet.isDemoWallet(): Boolean = DemoHelper.isDemoCardId(cardId) \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectMiddleware.kt b/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectMiddleware.kt index a8517bd1bc..6db0bf1a97 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectMiddleware.kt @@ -17,9 +17,11 @@ import com.tangem.tap.domain.tasks.product.ScanResponse import com.tangem.tap.domain.walletconnect.BnbHelper import com.tangem.tap.domain.walletconnect.WalletConnectManager import com.tangem.tap.domain.walletconnect.WalletConnectNetworkUtils +import com.tangem.tap.features.demo.DemoHelper import com.tangem.tap.features.wallet.redux.WalletAction import com.tangem.tap.store import com.tangem.wallet.R +import org.rekotlin.Action import org.rekotlin.Middleware class WalletConnectMiddleware { @@ -28,138 +30,131 @@ class WalletConnectMiddleware { val walletConnectMiddleware: Middleware = { dispatch, state -> { next -> { action -> - when (action) { + handle(state, action) + next(action) + } + } + } - is WalletConnectAction.RestoreSessions -> { - walletConnectManager.restoreSessions() - } + private fun handle(state: () -> AppState?, action: Action) { + if (DemoHelper.tryHandle(state, action)) return - is WalletConnectAction.HandleDeepLink -> { - if (!action.wcUri.isNullOrBlank()) { - if (WalletConnectManager.isCorrectWcUri(action.wcUri)) { - store.dispatchOnMain(WalletConnectAction.OpenSession(action.wcUri)) - } - } - } - - is WalletConnectAction.StartWalletConnect -> { - val uri = action.activity.getFromClipboard()?.toString() - if (uri != null && WalletConnectManager.isCorrectWcUri(uri)) { - store.dispatchOnMain(WalletConnectAction.ShowClipboardOrScanQrDialog(uri)) - } else { - store.dispatchOnMain(NavigationAction.NavigateTo(AppScreen.QrScan)) - } - } - - is WalletConnectAction.ShowClipboardOrScanQrDialog -> { - store.dispatchOnMain( - GlobalAction.ShowDialog( - WalletConnectDialog.ClipboardOrScanQr( - action.wcUri - ) - ) - ) - } - - is WalletConnectAction.OpeningSessionTimeout -> { - store.dispatchOnMain(GlobalAction.ShowDialog(WalletConnectDialog.SessionTimeout)) - } - - is WalletConnectAction.FailureEstablishingSession -> { - if (action.session != null) { - walletConnectManager.disconnect(action.session) - } - } - - is WalletConnectAction.UnsupportedCard -> { - store.dispatchOnMain(GlobalAction.ShowDialog(WalletConnectDialog.UnsupportedCard)) - } - - is WalletConnectAction.OpenSession -> { - walletConnectManager.connect( - wcUri = action.wcUri, - ) - } - - is WalletConnectAction.RefuseOpeningSession -> { - store.dispatch( - GlobalAction.ShowDialog( - WalletConnectDialog.OpeningSessionRejected - ) - ) - } - - is WalletConnectAction.ScanCard -> { - scanCard(action.session, action.chainId) - } - - is WalletConnectAction.ApproveSession -> { - walletConnectManager.approve(action.session) - } - - is WalletConnectAction.DisconnectSession -> { - walletConnectManager.disconnect(action.session) - } - - is WalletConnectAction.HandleTransactionRequest -> { - walletConnectManager.handleTransactionRequest( - transaction = action.transaction, - session = action.session, - id = action.id, - type = action.type - ) - } - is WalletConnectAction.HandlePersonalSignRequest -> { - walletConnectManager.handlePersonalSignRequest( - message = action.message, - session = action.session, - id = action.id - ) - } - is WalletConnectAction.RejectRequest -> { - walletConnectManager.rejectRequest(action.session, action.id) - } - is WalletConnectAction.SendTransaction -> { - walletConnectManager.completeTransaction(action.session) - } - is WalletConnectAction.SignMessage -> { - walletConnectManager.sendSignedMessage(action.session) - } - is WalletConnectAction.BinanceTransaction.Trade -> { - val messageData = BnbHelper.createMessageData(action.order) - store.dispatchOnMain( - GlobalAction.ShowDialog( - WalletConnectDialog.BnbTransactionDialog( - data = messageData, - session = action.sessionData.session, - sessionId = action.id, - cardId = action.sessionData.wallet.cardId, - dAppName = action.sessionData.peerMeta.name - ) - ) - ) - } - is WalletConnectAction.BinanceTransaction.Transfer -> { - val messageData = BnbHelper.createMessageData(action.order) - store.dispatchOnMain( - GlobalAction.ShowDialog( - WalletConnectDialog.BnbTransactionDialog( - data = messageData, - session = action.sessionData.session, - sessionId = action.id, - cardId = action.sessionData.wallet.cardId, - dAppName = action.sessionData.peerMeta.name - ) - ) - ) - } - is WalletConnectAction.BinanceTransaction.Sign -> { - walletConnectManager.signBnb( - action.id, action.data, action.sessionData - ) + when (action) { + is WalletConnectAction.RestoreSessions -> { + walletConnectManager.restoreSessions() + } + is WalletConnectAction.HandleDeepLink -> { + if (!action.wcUri.isNullOrBlank()) { + if (WalletConnectManager.isCorrectWcUri(action.wcUri)) { + store.dispatchOnMain(WalletConnectAction.OpenSession(action.wcUri)) } } - next(action) + } + is WalletConnectAction.StartWalletConnect -> { + val uri = action.activity.getFromClipboard()?.toString() + if (uri != null && WalletConnectManager.isCorrectWcUri(uri)) { + store.dispatchOnMain(WalletConnectAction.ShowClipboardOrScanQrDialog(uri)) + } else { + store.dispatchOnMain(NavigationAction.NavigateTo(AppScreen.QrScan)) + } + } + is WalletConnectAction.ShowClipboardOrScanQrDialog -> { + store.dispatchOnMain( + GlobalAction.ShowDialog( + WalletConnectDialog.ClipboardOrScanQr( + action.wcUri + ) + ) + ) + } + is WalletConnectAction.OpeningSessionTimeout -> { + store.dispatchOnMain(GlobalAction.ShowDialog(WalletConnectDialog.SessionTimeout)) + } + is WalletConnectAction.FailureEstablishingSession -> { + if (action.session != null) { + walletConnectManager.disconnect(action.session) + } + } + is WalletConnectAction.UnsupportedCard -> { + store.dispatchOnMain(GlobalAction.ShowDialog(WalletConnectDialog.UnsupportedCard)) + } + is WalletConnectAction.OpenSession -> { + walletConnectManager.connect( + wcUri = action.wcUri, + ) + } + is WalletConnectAction.RefuseOpeningSession -> { + store.dispatch( + GlobalAction.ShowDialog( + WalletConnectDialog.OpeningSessionRejected + ) + ) + } + is WalletConnectAction.ScanCard -> { + scanCard(action.session, action.chainId) + } + is WalletConnectAction.ApproveSession -> { + walletConnectManager.approve(action.session) + } + is WalletConnectAction.DisconnectSession -> { + walletConnectManager.disconnect(action.session) + } + is WalletConnectAction.HandleTransactionRequest -> { + walletConnectManager.handleTransactionRequest( + transaction = action.transaction, + session = action.session, + id = action.id, + type = action.type + ) + } + is WalletConnectAction.HandlePersonalSignRequest -> { + walletConnectManager.handlePersonalSignRequest( + message = action.message, + session = action.session, + id = action.id + ) + } + is WalletConnectAction.RejectRequest -> { + walletConnectManager.rejectRequest(action.session, action.id) + } + is WalletConnectAction.SendTransaction -> { + walletConnectManager.completeTransaction(action.session) + } + is WalletConnectAction.SignMessage -> { + walletConnectManager.sendSignedMessage(action.session) + } + is WalletConnectAction.BinanceTransaction.Trade -> { + val messageData = BnbHelper.createMessageData(action.order) + store.dispatchOnMain( + GlobalAction.ShowDialog( + WalletConnectDialog.BnbTransactionDialog( + data = messageData, + session = action.sessionData.session, + sessionId = action.id, + cardId = action.sessionData.wallet.cardId, + dAppName = action.sessionData.peerMeta.name + ) + ) + ) + } + is WalletConnectAction.BinanceTransaction.Transfer -> { + val messageData = BnbHelper.createMessageData(action.order) + store.dispatchOnMain( + GlobalAction.ShowDialog( + WalletConnectDialog.BnbTransactionDialog( + data = messageData, + session = action.sessionData.session, + sessionId = action.id, + cardId = action.sessionData.wallet.cardId, + dAppName = action.sessionData.peerMeta.name + ) + ) + ) + } + is WalletConnectAction.BinanceTransaction.Sign -> { + walletConnectManager.signBnb( + action.id, action.data, action.sessionData + ) } } } @@ -190,9 +185,9 @@ class WalletConnectMiddleware { } val walletManager = getWalletManager(scanResponse, blockchain).guard { - store.dispatchOnMain(WalletConnectAction.FailureEstablishingSession(null)) - return - } + store.dispatchOnMain(WalletConnectAction.FailureEstablishingSession(null)) + return + } val wallet = walletManager.wallet val derivedKey = @@ -227,7 +222,7 @@ class WalletConnectMiddleware { ): WalletManager? { val card = scanResponse.card val factory = store.state.globalState.tapWalletManager.walletManagerFactory - val blockchainToMake = if (blockchain == Blockchain.Ethereum && card.isTestCard) { + val blockchainToMake = if (blockchain == Blockchain.Ethereum && card.isTestCard) { Blockchain.EthereumTestnet } else { blockchain diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteMiddleware.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteMiddleware.kt index 6a8cc146b7..a611b435d6 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/redux/OnboardingNoteMiddleware.kt @@ -13,6 +13,7 @@ import com.tangem.tap.domain.DELAY_SDK_DIALOG_CLOSE import com.tangem.tap.domain.TapError import com.tangem.tap.domain.extensions.hasWallets import com.tangem.tap.domain.extensions.makePrimaryWalletManager +import com.tangem.tap.features.demo.DemoHelper import com.tangem.tap.features.wallet.redux.Currency import com.tangem.tap.features.wallet.redux.ProgressState import com.tangem.tap.scope @@ -33,14 +34,16 @@ class OnboardingNoteMiddleware { private val onboardingNoteMiddleware: Middleware = { dispatch, state -> { next -> { action -> - handleNoteAction(action, dispatch) + handleNoteAction(state, action, dispatch) next(action) } } } -private fun handleNoteAction(action: Action, dispatch: DispatchFunction) { +private fun handleNoteAction(appState: () -> AppState?, action: Action, dispatch: DispatchFunction) { if (action !is OnboardingNoteAction) return + if (DemoHelper.tryHandle(appState, action)) return + val globalState = store.state.globalState val onboardingManager = globalState.onboardingState.onboardingManager ?: return diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/ui/TwinsCardsFragment.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/ui/TwinsCardsFragment.kt index f8051e67fd..c16bc7476d 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/ui/TwinsCardsFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/ui/TwinsCardsFragment.kt @@ -1,6 +1,7 @@ package com.tangem.tap.features.onboarding.products.twins.ui import android.os.Bundle +import android.util.TypedValue import android.view.View import android.view.animation.OvershootInterpolator import androidx.annotation.LayoutRes @@ -69,10 +70,15 @@ class TwinsCardsFragment : BaseOnboardingFragment() { addBackPressHandler(this) reconfigureLayoutForTwins(binding.onboardingTopContainer) - twinsWidget = - TwinsCardWidget(LeapfrogWidget(binding.onboardingTopContainer.cardsContainer)) { 285f } - btnRefreshBalanceWidget = - RefreshBalanceWidget(binding.onboardingTopContainer.onboardingMainContainer) + + val typedValue = TypedValue() + resources.getValue(R.dimen.device_scale_factor_for_twins_welcome, typedValue, true) + val deviceScaleFactorForWelcomeState = typedValue.float + + twinsWidget = TwinsCardWidget(LeapfrogWidget(binding.onboardingTopContainer.cardsContainer), deviceScaleFactorForWelcomeState) { + 285f * deviceScaleFactorForWelcomeState + } + btnRefreshBalanceWidget = RefreshBalanceWidget(binding.onboardingTopContainer.onboardingMainContainer) binding.toolbar.title = getText(R.string.twins_recreate_toolbar) diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletMiddleware.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletMiddleware.kt index 29cdd561b2..9c61e0c402 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletMiddleware.kt @@ -16,6 +16,7 @@ import com.tangem.tap.common.redux.navigation.AppScreen import com.tangem.tap.common.redux.navigation.NavigationAction import com.tangem.tap.domain.extensions.hasWallets import com.tangem.tap.domain.tasks.product.ScanResponse +import com.tangem.tap.features.demo.DemoHelper import com.tangem.tap.features.home.redux.HomeAction import com.tangem.tap.features.onboarding.products.wallet.redux.OnboardingWalletMiddleware.Companion.BUY_WALLET_URL import com.tangem.tap.features.wallet.redux.Artwork @@ -164,14 +165,16 @@ class BackupMiddleware { val backupMiddleware: Middleware = { dispatch, state -> { next -> { action -> - if (action is BackupAction) handleBackupAction(action) + if (action is BackupAction) handleBackupAction(state, action) next(action) } } } } -private fun handleBackupAction(action: BackupAction) { +private fun handleBackupAction(appState: () -> AppState?, action: BackupAction) { + if (DemoHelper.tryHandle(appState, action)) return + val backupState = store.state.onboardingWalletState.backupState val globalState = store.state.globalState diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/OnboardingWalletFragment.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/OnboardingWalletFragment.kt index 1d81fc9a80..fa2071d32d 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/OnboardingWalletFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/OnboardingWalletFragment.kt @@ -1,6 +1,7 @@ package com.tangem.tap.features.onboarding.products.wallet.ui import android.os.Bundle +import android.util.TypedValue import android.view.* import android.widget.ImageView import androidx.appcompat.app.AppCompatActivity @@ -14,6 +15,7 @@ import com.squareup.picasso.Picasso import com.tangem.common.CardIdFormatter import com.tangem.common.core.CardIdDisplayFormat import com.tangem.tangem_sdk_new.ui.widget.leapfrogWidget.LeapfrogWidget +import com.tangem.tangem_sdk_new.ui.widget.leapfrogWidget.PropertyCalculator import com.tangem.tap.common.extensions.hide import com.tangem.tap.common.extensions.show import com.tangem.tap.features.FragmentOnBackPressedHandler @@ -51,8 +53,15 @@ class OnboardingWalletFragment : Fragment(R.layout.fragment_onboarding_wallet), override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - val leapfrog = LeapfrogWidget(binding.flCardsContainer) - cardsWidget = WalletCardsWidget(leapfrog) { 200f } + val typedValue = TypedValue() + resources.getValue(R.dimen.device_scale_factor_for_twins_welcome, typedValue, true) + val deviceScaleFactor = typedValue.float + + val leapfrogCalculator = PropertyCalculator( + yTranslationFactor = 25f * deviceScaleFactor, + ) + val leapfrog = LeapfrogWidget(binding.flCardsContainer, leapfrogCalculator) + cardsWidget = WalletCardsWidget(leapfrog, deviceScaleFactor) { 200f * deviceScaleFactor } startPostponedEnterTransition() binding.viewPagerBackupInfo.adapter = BackupInfoAdapter() @@ -130,8 +139,7 @@ class OnboardingWalletFragment : Fragment(R.layout.fragment_onboarding_wallet), tvHeader.setText(R.string.onboarding_create_wallet_header) tvBody.setText(R.string.onboarding_create_wallet_body) - cardsWidget.toFolded() - startPostponedEnterTransition() + cardsWidget.toFolded(false) { startPostponedEnterTransition() } } @@ -394,7 +402,7 @@ class OnboardingWalletFragment : Fragment(R.layout.fragment_onboarding_wallet), val shopMenuShouldBeVisible = (backupStep == BackupStep.ScanOriginCard || backupStep == BackupStep.AddBackupCards) && - backupState.buyAdditionalCardsUrl != null + backupState.buyAdditionalCardsUrl != null menu.getItem(0).isVisible = shopMenuShouldBeVisible } diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/WalletCardsWidget.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/WalletCardsWidget.kt index 0efdbf3d0b..eb5ae173af 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/WalletCardsWidget.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/WalletCardsWidget.kt @@ -12,6 +12,7 @@ import com.tangem.tangem_sdk_new.ui.widget.leapfrogWidget.LeapfrogWidget class WalletCardsWidget( val leapfrogWidget: LeapfrogWidget, + private val deviceScaleFactor: Float = 1f, val getTopOfAnchorViewForActivateState: () -> Float, ) { @@ -42,7 +43,7 @@ class WalletCardsWidget( createAnimator(BackupCardType.FIRST_BACKUP, createLeapfrogProperties(BackupCardType.FIRST_BACKUP)), createAnimator(BackupCardType.SECOND_BACKUP, createLeapfrogProperties(BackupCardType.SECOND_BACKUP)) ) - leapfrogWidget.fold { animator.start() } + leapfrogWidget.fold(animate) { animator.start() } } fun toFan(animate: Boolean = true, onEnd: () -> Unit = {}) { @@ -70,7 +71,7 @@ class WalletCardsWidget( ) leapfrogWidget.fold { animator.doOnEnd { - leapfrogWidget.initViews() +// leapfrogWidget.initViews() leapfrogWidget.unfold() } animator.start() @@ -98,26 +99,26 @@ class WalletCardsWidget( private fun createWelcomeProperties(cardType: BackupCardType): CardProperties { return when (cardType) { BackupCardType.ORIGIN -> CardProperties( - xTranslation = 440f, - yTranslation = 0f, - rotation = 75f, - elevation = 2f, - scale = .9f, - ) + xTranslation = 440f * deviceScaleFactor, + yTranslation = 70f, + rotation = 75f, + elevation = 2f, + scale = .85f * deviceScaleFactor, + ) BackupCardType.FIRST_BACKUP -> CardProperties( - xTranslation = 10f, - yTranslation = -100f, - rotation = 100f, - elevation = 1f, - scale = .9f, -) + xTranslation = 10f * deviceScaleFactor, + yTranslation = -30f, + rotation = 100f, + elevation = 1f, + scale = .85f * deviceScaleFactor, + ) BackupCardType.SECOND_BACKUP -> CardProperties( - xTranslation = -440f, - yTranslation = -100f, - rotation = 70f, - elevation = 0f, - scale = .9f, - ) + xTranslation = -440f * deviceScaleFactor, + yTranslation = -30f, + rotation = 70f, + elevation = 0f, + scale = .85f * deviceScaleFactor, + ) } } @@ -125,24 +126,24 @@ class WalletCardsWidget( return when (cardType) { BackupCardType.ORIGIN -> CardProperties( xTranslation = 0f, - yTranslation = 0f, + yTranslation = 30f, rotation = 5f, elevation = 2f, - scale = 1f, + scale = 1f * deviceScaleFactor, ) BackupCardType.FIRST_BACKUP -> CardProperties( xTranslation = 0f, - yTranslation = -80f, + yTranslation = -50f, rotation = -5f, elevation = 1f, - scale = 0.9f, + scale = 0.9f * deviceScaleFactor, ) BackupCardType.SECOND_BACKUP -> CardProperties( xTranslation = 10f, - yTranslation = -170f, + yTranslation = -120f, rotation = -20f, elevation = 0f, - scale = 0.8f, + scale = 0.8f * deviceScaleFactor, ) } } @@ -154,21 +155,21 @@ class WalletCardsWidget( yTranslation = 0f, rotation = 0f, elevation = 2f, - scale = 1f, + scale = 1f * deviceScaleFactor, ) BackupCardType.FIRST_BACKUP -> CardProperties( xTranslation = 0f, yTranslation = 0f, rotation = 0f, elevation = 1f, - scale = 0.9f, + scale = 0.9f * deviceScaleFactor, ) BackupCardType.SECOND_BACKUP -> CardProperties( xTranslation = 0f, yTranslation = 0f, rotation = 0f, elevation = 0f, - scale = 0.8f, + scale = 0.8f * deviceScaleFactor, ) } } diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/SendMiddleware.kt b/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/SendMiddleware.kt index 572658b793..34a63dd905 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/SendMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/SendMiddleware.kt @@ -13,6 +13,7 @@ import com.tangem.tap.common.analytics.Analytics import com.tangem.tap.common.analytics.AnalyticsEvent import com.tangem.tap.common.analytics.AnalyticsParam import com.tangem.tap.common.extensions.* +import com.tangem.tap.common.redux.AppDialog import com.tangem.tap.common.redux.AppState import com.tangem.tap.common.redux.global.GlobalAction import com.tangem.tap.common.redux.navigation.NavigationAction @@ -22,6 +23,8 @@ import com.tangem.tap.domain.TapError import com.tangem.tap.domain.TapWorkarounds.isStart2Coin import com.tangem.tap.domain.configurable.warningMessage.WarningMessage import com.tangem.tap.domain.extensions.minimalAmount +import com.tangem.tap.features.demo.DemoTransactionSender +import com.tangem.tap.features.demo.isDemoWallet import com.tangem.tap.features.send.redux.* import com.tangem.tap.features.send.redux.FeeAction.RequestFee import com.tangem.tap.features.send.redux.states.ButtonState @@ -32,6 +35,7 @@ import com.tangem.tap.features.wallet.redux.WalletAction import com.tangem.tap.scope import com.tangem.tap.store import com.tangem.tap.tangemSdk +import com.tangem.wallet.R import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay import kotlinx.coroutines.launch @@ -164,9 +168,12 @@ private fun sendTransaction( ) ) } - val sendResult = try { - (walletManager as TransactionSender).send(txData, signer) + if (walletManager.isDemoWallet()) { + DemoTransactionSender(walletManager).send(txData, signer) + } else { + (walletManager as TransactionSender).send(txData, signer) + } } catch (ex: Exception) { FirebaseCrashlytics.getInstance().recordException(ex) delay(DELAY_SDK_DIALOG_CLOSE) @@ -238,6 +245,12 @@ private fun sendTransaction( message.contains("Target account is not created. To create account send 1+ XLM.") -> { dispatch(SendAction.SendError(TapError.XmlError.AssetAccountNotCreated)) } + message.contains(DemoTransactionSender.ID) -> { + store.dispatchDialogShow(AppDialog.SimpleOkDialogRes( + R.string.common_done, + R.string.alert_demo_tx_send + )) + } else -> { (sendResult.error as? TangemSdkError)?.let { error -> store.state.globalState.analyticsHandlers?.logCardSdkError( diff --git a/app/src/main/java/com/tangem/tap/features/wallet/models/PendingTransaction.kt b/app/src/main/java/com/tangem/tap/features/wallet/models/PendingTransaction.kt index a449bb17ea..722b2c7d03 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/models/PendingTransaction.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/models/PendingTransaction.kt @@ -6,10 +6,12 @@ import com.tangem.blockchain.common.TransactionStatus import com.tangem.blockchain.common.Wallet import com.tangem.tap.common.extensions.toFormattedString import com.tangem.tap.domain.extensions.toSendableAmounts +import java.math.BigDecimal data class PendingTransaction( val address: String?, - val amount: String?, + val amount: BigDecimal?, + val amountUi: String?, val currency: String, val type: PendingTransactionType ) @@ -38,6 +40,7 @@ fun TransactionData.toPendingTransaction(walletAddress: String): PendingTransact return PendingTransaction( if (address == "unknown") null else address, + this.amount.value, this.amount.value?.toFormattedString(amount.decimals), this.amount.currencySymbol, type @@ -61,8 +64,12 @@ fun List.toPendingTransactionsForToken(token: Token, walletAddr return this.mapNotNull { it.toPendingTransactionForToken(token, walletAddress) } } -fun Wallet.getPendingTransactions(): List { - return recentTransactions.toPendingTransactions(address) +fun Wallet.getPendingTransactions(type: PendingTransactionType? = null): List { + val txs = recentTransactions.toPendingTransactions(address) + return when(type) { + null -> txs + else -> txs.filter { it.type == type } + } } fun Wallet.hasPendingTransactions(): Boolean { 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 df4c9120f7..1d31def08c 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 @@ -13,6 +13,10 @@ import com.tangem.tap.common.redux.navigation.NavigationAction import com.tangem.tap.currenciesRepository import com.tangem.tap.domain.extensions.makeWalletManagerForApp import com.tangem.tap.domain.extensions.makeWalletManagersForApp +import com.tangem.tap.features.demo.DemoHelper +import com.tangem.tap.features.demo.isDemoCard +import com.tangem.tap.features.wallet.models.PendingTransactionType +import com.tangem.tap.features.wallet.models.getPendingTransactions import com.tangem.tap.features.wallet.redux.Currency import com.tangem.tap.features.wallet.redux.WalletAction import com.tangem.tap.features.wallet.redux.WalletState @@ -22,6 +26,7 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.async import kotlinx.coroutines.launch import kotlinx.coroutines.withContext +import java.math.BigDecimal class MultiWalletMiddleware { fun handle( @@ -34,6 +39,9 @@ class MultiWalletMiddleware { is WalletAction.MultiWallet.AddWalletManagers -> { globalState.feedbackManager?.infoHolder?.setWalletsInfo(action.walletManagers) action.walletManagers.forEach { checkForRentWarning(it) } + if (globalState.scanResponse?.isDemoCard() == true) { + addDummyBalances(action.walletManagers) + } } is WalletAction.MultiWallet.SelectWallet -> { if (action.walletData != null) { @@ -99,28 +107,25 @@ class MultiWalletMiddleware { scope.launch { walletManagers.map { walletManager -> async(Dispatchers.IO) { - try { - walletManager.update() - val wallet = walletManager.wallet - val coinAmount = wallet.amounts[AmountType.Coin]?.value - if (coinAmount != null && !coinAmount.isZero()) { - scope.launch(Dispatchers.Main) { - if (walletState?.getWalletData(wallet.blockchain) == null) { - store.dispatch( - WalletAction.MultiWallet.AddWalletManagers( - listOfNotNull(walletManager) - ) + walletManager.safeUpdate() + val wallet = walletManager.wallet + val coinAmount = wallet.amounts[AmountType.Coin]?.value + if (coinAmount != null && !coinAmount.isZero()) { + scope.launch(Dispatchers.Main) { + if (walletState?.getWalletData(wallet.blockchain) == null) { + store.dispatch( + WalletAction.MultiWallet.AddWalletManagers( + listOfNotNull(walletManager) ) - store.dispatch( - WalletAction.MultiWallet.AddBlockchain( - wallet.blockchain - ) + ) + store.dispatch( + WalletAction.MultiWallet.AddBlockchain( + wallet.blockchain ) - store.dispatch(WalletAction.LoadWallet.Success(wallet)) - } + ) + store.dispatch(WalletAction.LoadWallet.Success(wallet)) } } - } catch (exception: Exception) { } } } @@ -167,6 +172,14 @@ class MultiWalletMiddleware { } } + private fun addDummyBalances(walletManagers: List) { + walletManagers.forEach { + if (it.wallet.fundsAvailable(AmountType.Coin) == BigDecimal.ZERO) { + DemoHelper.injectDemoBalance(it) + } + } + } + private fun addToken(token: Token, walletState: WalletState?, globalState: GlobalState?) { val scanResponse = globalState?.scanResponse ?: return @@ -206,13 +219,25 @@ class MultiWalletMiddleware { scope.launch { when (val result = rentProvider.minimalBalanceForRentExemption()) { is Result.Success -> { + fun isNeedToShowWarning(balance: BigDecimal, rentExempt: BigDecimal): Boolean = balance >= rentExempt + + val balance = walletManager.wallet.fundsAvailable(AmountType.Coin) + val outgoingTxs = walletManager.wallet.getPendingTransactions(PendingTransactionType.Outgoing) + val rentExempt = result.data + val show = if (outgoingTxs.isEmpty()) { + isNeedToShowWarning(balance, rentExempt) + } else { + val outgoingAmount = outgoingTxs.sumOf { it.amount ?: BigDecimal.ZERO } + val rest = balance.minus(outgoingAmount) + isNeedToShowWarning(rest, rentExempt) + } + if (!show) return@launch + val currency = walletManager.wallet.blockchain.currency - val minRent = ("${rentProvider.rentAmount().stripZeroPlainString()} $currency") - val rentExempt = ("${result.data.stripZeroPlainString()} $currency") store.dispatchOnMain(WalletAction.SetWalletRent( blockchain = walletManager.wallet.blockchain, - minRent = minRent, - rentExempt = rentExempt + minRent = ("${rentProvider.rentAmount().stripZeroPlainString()} $currency"), + rentExempt = ("${rentExempt.stripZeroPlainString()} $currency") )) } is Result.Failure -> {} diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/TradeCryptoMiddleware.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/TradeCryptoMiddleware.kt index 2cd5627545..fdf3312ab0 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/TradeCryptoMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/TradeCryptoMiddleware.kt @@ -3,10 +3,12 @@ package com.tangem.tap.features.wallet.redux.middlewares import com.tangem.blockchain.blockchains.ethereum.EthereumWalletManager import com.tangem.blockchain.common.AmountType import com.tangem.tap.common.extensions.dispatchOnMain +import com.tangem.tap.common.redux.AppState import com.tangem.tap.common.redux.navigation.AppScreen import com.tangem.tap.common.redux.navigation.NavigationAction import com.tangem.tap.domain.topup.TopUpManager import com.tangem.tap.domain.topup.TradeCryptoHelper +import com.tangem.tap.features.demo.DemoHelper import com.tangem.tap.features.send.redux.PrepareSendScreen import com.tangem.tap.features.send.redux.SendAction import com.tangem.tap.features.wallet.redux.Currency @@ -18,7 +20,9 @@ import timber.log.Timber class TradeCryptoMiddleware { - fun handle(action: WalletAction.TradeCryptoAction) { + fun handle(state: () -> AppState?, action: WalletAction.TradeCryptoAction) { + if (DemoHelper.tryHandle(state, action)) return + when (action) { is WalletAction.TradeCryptoAction.Buy -> startExchange(action) is WalletAction.TradeCryptoAction.Sell -> startExchange(action) 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 37b71f1c2a..088fe72d40 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 @@ -3,13 +3,15 @@ package com.tangem.tap.features.wallet.redux.middlewares import android.content.Intent import android.net.Uri import androidx.core.content.ContextCompat -import com.tangem.blockchain.common.* +import com.tangem.blockchain.common.Amount +import com.tangem.blockchain.common.AmountType +import com.tangem.blockchain.common.Wallet +import com.tangem.blockchain.common.WalletManager import com.tangem.common.CompletionResult import com.tangem.common.core.TangemSdkError import com.tangem.common.extensions.isZero import com.tangem.common.services.Result import com.tangem.operations.attestation.OnlineCardVerifier -import com.tangem.tap.* import com.tangem.tap.common.analytics.Analytics import com.tangem.tap.common.extensions.* import com.tangem.tap.common.redux.AppDialog @@ -18,10 +20,14 @@ 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.extensions.toSendableAmounts +import com.tangem.tap.features.demo.DemoHelper import com.tangem.tap.features.home.redux.HomeAction import com.tangem.tap.features.send.redux.PrepareSendScreen import com.tangem.tap.features.wallet.redux.* import com.tangem.tap.network.NetworkStateChanged +import com.tangem.tap.scope +import com.tangem.tap.store +import com.tangem.tap.tangemSdkManager import kotlinx.coroutines.async import kotlinx.coroutines.awaitAll import kotlinx.coroutines.launch @@ -37,18 +43,20 @@ class WalletMiddleware { val walletMiddleware: Middleware = { dispatch, state -> { next -> { action -> - handleAction(action, dispatch) + handleAction(state, action, dispatch) next(action) } } } - private fun handleAction(action: Action, dispatch: DispatchFunction) { + private fun handleAction(state: () -> AppState?, action: Action, dispatch: DispatchFunction) { + if (DemoHelper.tryHandle(state, action)) return + val globalState = store.state.globalState val walletState = store.state.walletState when (action) { - is WalletAction.TradeCryptoAction -> tradeCryptoMiddleware.handle(action) + is WalletAction.TradeCryptoAction -> tradeCryptoMiddleware.handle(state, action) is WalletAction.Warnings -> warningsMiddleware.handle(action, globalState) is WalletAction.MultiWallet -> multiWalletMiddleware.handle(action, walletState, globalState) is WalletAction.LoadWallet -> { diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WarningsMiddleware.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WarningsMiddleware.kt index 408f0ef8a3..b7c104861d 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WarningsMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WarningsMiddleware.kt @@ -17,6 +17,7 @@ import com.tangem.tap.domain.extensions.hasSignedHashes import com.tangem.tap.domain.extensions.remainingSignatures import com.tangem.tap.domain.isMultiwalletAllowed import com.tangem.tap.domain.tasks.product.ScanResponse +import com.tangem.tap.features.demo.isDemoCard import com.tangem.tap.features.wallet.redux.WalletAction import com.tangem.tap.network.NetworkConnectivity import com.tangem.tap.preferencesStorage @@ -99,6 +100,9 @@ class WarningsMiddleware { addWarningMessage(WarningMessagesManager.onlineVerificationFailed()) } } + if (scanResponse.isDemoCard()){ + addWarningMessage(WarningMessagesManager.demoCardWarning()) + } setWarningMessages() } } diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/adapters/PendingTransactionsAdapter.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/adapters/PendingTransactionsAdapter.kt index 2e546ecf3a..6fc4e509b1 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/adapters/PendingTransactionsAdapter.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/adapters/PendingTransactionsAdapter.kt @@ -63,7 +63,7 @@ class PendingTransactionsAdapter } binding.tvPendingTransaction.text = binding.root.getString(transactionDescriptionRes) - transaction.amount?.let { binding.tvPendingTransactionAmount.text = "$it " } + transaction.amountUi?.let { binding.tvPendingTransactionAmount.text = "$it " } binding.tvPendingTransactionCurrency.text = "${transaction.currency}" if (transaction.address != null) { diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/dialogs/SimpleOkDialog.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/dialogs/SimpleOkDialog.kt new file mode 100644 index 0000000000..aa15f94e9f --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/dialogs/SimpleOkDialog.kt @@ -0,0 +1,35 @@ +package com.tangem.tap.features.wallet.ui.dialogs + +import android.content.Context +import androidx.appcompat.app.AlertDialog +import com.tangem.tap.common.extensions.dispatchDialogHide +import com.tangem.tap.common.redux.AppDialog +import com.tangem.tap.store +import com.tangem.wallet.R + +/** +[REDACTED_AUTHOR] + */ +class SimpleOkDialog { + + companion object { + fun create(dialog: AppDialog.SimpleOkDialog, context: Context): AlertDialog { + return AlertDialog.Builder(context).apply { + setTitle(dialog.header) + setMessage(dialog.message) + setPositiveButton(R.string.common_ok) { _, _ -> } + setOnDismissListener { store.dispatchDialogHide() } + }.create() + } + + fun create(dialog: AppDialog.SimpleOkDialogRes, context: Context): AlertDialog { + return AlertDialog.Builder(context).apply { + setTitle(context.getString(dialog.headerId)) + setMessage(dialog.messageId) + setPositiveButton(R.string.common_ok) { _, _ -> } + setOnDismissListener { store.dispatchDialogHide() } + }.create() + } + } + +} \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_onboarding_main.xml b/app/src/main/res/layout/fragment_onboarding_main.xml index 795b8ebe56..82b94a5def 100644 --- a/app/src/main/res/layout/fragment_onboarding_main.xml +++ b/app/src/main/res/layout/fragment_onboarding_main.xml @@ -38,7 +38,7 @@ layout="@layout/layout_onboarding_container_top" android:layout_width="match_parent" android:layout_height="0dp" - android:layout_weight="1.2" + android:layout_weight="@dimen/home_top_weight" android:clipChildren="false" /> + android:layout_weight="@dimen/home_bottom_weight" /> diff --git a/app/src/main/res/layout/fragment_onboarding_wallet.xml b/app/src/main/res/layout/fragment_onboarding_wallet.xml index f1c1983eb1..904ab50059 100644 --- a/app/src/main/res/layout/fragment_onboarding_wallet.xml +++ b/app/src/main/res/layout/fragment_onboarding_wallet.xml @@ -23,9 +23,9 @@ android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" + app:menu="@menu/shop" app:navigationIcon="@drawable/ic_baseline_arrow_back_24" - app:title="@string/onboarding_getting_started" - app:menu="@menu/shop"/> + app:title="@string/onboarding_getting_started" /> @@ -33,6 +33,7 @@ android:id="@+id/onboarding_main_container" android:layout_width="match_parent" android:layout_height="match_parent" + android:clipChildren="false" app:layout_behavior="@string/appbar_scrolling_view_behavior"> @@ -51,28 +51,25 @@ android:id="@+id/imv_card_background" android:layout_width="236dp" android:layout_height="236dp" - android:layout_gravity="center" android:adjustViewBounds="true" android:background="@drawable/shape_circle" android:backgroundTint="@color/lightGray0" android:elevation="0dp" android:scaleType="fitCenter" - app:layout_constraintStart_toStartOf="@id/fl_cards_container" - app:layout_constraintEnd_toEndOf="@id/fl_cards_container" - app:layout_constraintTop_toTopOf="@id/fl_cards_container" app:layout_constraintBottom_toBottomOf="@id/fl_cards_container" - android:layout_marginBottom="16dp" - /> + app:layout_constraintEnd_toEndOf="@id/fl_cards_container" + app:layout_constraintStart_toStartOf="@id/fl_cards_container" + app:layout_constraintTop_toTopOf="@id/fl_cards_container" /> @@ -81,36 +78,33 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:layout_gravity="center" - android:src="@drawable/card_placeholder_black" - android:visibility="gone" android:layout_marginStart="32dp" android:layout_marginEnd="32dp" android:scaleType="fitCenter" - - /> + android:src="@drawable/card_placeholder_black" + android:visibility="gone" /> + android:src="@drawable/card_placeholder_black" + android:visibility="gone" /> + android:src="@drawable/card_placeholder_black" + android:transitionName="imv_front_card" /> @@ -119,12 +113,12 @@ android:layout_width="142dp" android:layout_height="142dp" android:src="@drawable/ic_activation_success" + android:visibility="gone" app:layout_constraintBottom_toTopOf="@id/guideline2" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.495" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@id/pb_state" - android:visibility="gone"/> + app:layout_constraintTop_toBottomOf="@id/pb_state" /> + app:layout_constraintTop_toTopOf="@+id/guideline2" + tools:text="Добавлена одна резервная карта" /> + app:layout_constraintTop_toBottomOf="@+id/tv_header" + tools:text="Body" /> + app:layout_constraintTop_toTopOf="@+id/guideline2" /> + app:tabPaddingStart="8dp" /> + app:layout_constraintBottom_toBottomOf="parent" /> diff --git a/app/src/main/res/layout/fragment_shop.xml b/app/src/main/res/layout/fragment_shop.xml index ef4297ba3d..9ad04edbdd 100644 --- a/app/src/main/res/layout/fragment_shop.xml +++ b/app/src/main/res/layout/fragment_shop.xml @@ -26,7 +26,7 @@ android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" app:navigationIcon="@drawable/ic_clear_24" - app:title="@string/shop_title" + app:title="@string/home_button_order" app:titleCentered="true" /> diff --git a/app/src/main/res/layout/layout_backup_access_code.xml b/app/src/main/res/layout/layout_backup_access_code.xml index de45562ba3..d8c721b7f6 100644 --- a/app/src/main/res/layout/layout_backup_access_code.xml +++ b/app/src/main/res/layout/layout_backup_access_code.xml @@ -8,36 +8,36 @@ android:layout_width="match_parent" android:layout_height="match_parent"> - + - + - + diff --git a/app/src/main/res/layout/layout_backup_access_code_info.xml b/app/src/main/res/layout/layout_backup_access_code_info.xml index ac5a98171f..3ef7ba57fb 100644 --- a/app/src/main/res/layout/layout_backup_access_code_info.xml +++ b/app/src/main/res/layout/layout_backup_access_code_info.xml @@ -8,18 +8,18 @@ android:id="@+id/iv_access_code_icon" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_marginTop="25dp" + android:layout_marginTop="@dimen/access_code_info_protect_margin_top" android:src="@drawable/ic_access_code" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" /> \ No newline at end of file diff --git a/app/src/main/res/layout/layout_onboarding_buttons_common.xml b/app/src/main/res/layout/layout_onboarding_buttons_common.xml index ff0472d121..e99f7dfe4a 100644 --- a/app/src/main/res/layout/layout_onboarding_buttons_common.xml +++ b/app/src/main/res/layout/layout_onboarding_buttons_common.xml @@ -1,33 +1,33 @@ + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="wrap_content"> + app:layout_constraintStart_toStartOf="parent" + tools:text="Main Action" /> + app:layout_constraintStart_toStartOf="parent" + tools:text="Alternative action" /> \ No newline at end of file diff --git a/app/src/main/res/layout/layout_onboarding_container_bottom.xml b/app/src/main/res/layout/layout_onboarding_container_bottom.xml index d5e9dbcc4a..08c351d6fb 100644 --- a/app/src/main/res/layout/layout_onboarding_container_bottom.xml +++ b/app/src/main/res/layout/layout_onboarding_container_bottom.xml @@ -10,14 +10,14 @@ android:id="@+id/linearLayoutCompat" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_marginBottom="8dp" + android:layout_marginBottom="@dimen/home_bottom_llCompat_margin_bottom" android:gravity="center" android:orientation="vertical" app:layout_constraintBottom_toTopOf="@+id/fl_scan_card_container" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" - app:layout_constraintVertical_bias="0.1"> + app:layout_constraintVertical_bias="@dimen/home_bottom_llCompat_constraint_vertical_bias"> - + @@ -29,8 +35,10 @@ android:elevation="1dp" android:rotation="-3" android:scaleType="fitXY" + android:scaleX="@dimen/home_top_front_card_scaleX" + android:scaleY="@dimen/home_top_front_card_scaleY" android:src="@drawable/card_placeholder_black" - android:translationY="@dimen/home_front_card_translationY" + android:translationY="@dimen/home_top_front_card_translationY" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> diff --git a/app/src/main/res/layout/lp_onboarding_topup_wallet.xml b/app/src/main/res/layout/lp_onboarding_topup_wallet.xml index 6b924ea771..dc6efbcc96 100644 --- a/app/src/main/res/layout/lp_onboarding_topup_wallet.xml +++ b/app/src/main/res/layout/lp_onboarding_topup_wallet.xml @@ -25,7 +25,7 @@ + + One Wallet + 3 cards + 2 cards + Shipping + Free + I have a promo code… + Total + Other payment methods + Buy now + Placing your order + Your order + Meet\nTangem + Buy + Store + Send + Pay + Exchange + Lend + Borrow + Stake + 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 + Web3 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 card + You are currently running in Demo mode. All funds are not real. + This feature is disabled in Demo mode. + The transaction has been successfully signed, but not sent to the blockchain because of Demo mode. + \ No newline at end of file diff --git a/app/src/main/res/values-fr/strings_final.xml b/app/src/main/res/values-fr/strings_final.xml new file mode 100644 index 0000000000..339691d69b --- /dev/null +++ b/app/src/main/res/values-fr/strings_final.xml @@ -0,0 +1,37 @@ + + + One Wallet + 3 cards + 2 cards + Shipping + Free + I have a promo code… + Total + Other payment methods + Buy now + Placing your order + Your order + Meet\nTangem + Buy + Store + Send + Pay + Exchange + Lend + Borrow + Stake + 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 + Web3 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 card + You are currently running in Demo mode. All funds are not real. + This feature is disabled in Demo mode. + The transaction has been successfully signed, but not sent to the blockchain because of Demo mode. + \ No newline at end of file diff --git a/app/src/main/res/values-h680dp/dimens.xml b/app/src/main/res/values-h680dp/dimens.xml index b8c8d265ed..7a03c1b4cf 100644 --- a/app/src/main/res/values-h680dp/dimens.xml +++ b/app/src/main/res/values-h680dp/dimens.xml @@ -1,9 +1,38 @@ - -10dp - 0dp + 1.0 + + + 1.1 + -10dp + 1 + 1 + 0dp + 1 + 1 + + + 1 + 16dp + 0.4> + 14dp + 64dp + 64dp + 90dp + 14dp + + 16dp 106dp + 230dp 67dp + 0.56 + + + 80dp + 24dp + 20dp + 32dp + 80dp diff --git a/app/src/main/res/values-it/strings_final.xml b/app/src/main/res/values-it/strings_final.xml new file mode 100644 index 0000000000..339691d69b --- /dev/null +++ b/app/src/main/res/values-it/strings_final.xml @@ -0,0 +1,37 @@ + + + One Wallet + 3 cards + 2 cards + Shipping + Free + I have a promo code… + Total + Other payment methods + Buy now + Placing your order + Your order + Meet\nTangem + Buy + Store + Send + Pay + Exchange + Lend + Borrow + Stake + 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 + Web3 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 card + You are currently running in Demo mode. All funds are not real. + This feature is disabled in Demo mode. + The transaction has been successfully signed, but not sent to the blockchain because of Demo mode. + \ No newline at end of file diff --git a/app/src/main/res/values-ru/strings_final.xml b/app/src/main/res/values-ru/strings_final.xml new file mode 100644 index 0000000000..dac3254b83 --- /dev/null +++ b/app/src/main/res/values-ru/strings_final.xml @@ -0,0 +1,37 @@ + + + Один Кошелек + 3 карты + 2 карты + Доставка + Бесплатно + У меня есть промо-код… + Итого + Другие способы оплаты + Купить сейчас + Оформление вашего заказа + Ваш заказ + Встречайте\nTangem + Покупайте + Храните + Отправляйте + Расплачивайтесь + Обменивайте + Одалживайте + Занимайте + Вкладывайте + Революционный аппаратный кошелек + Держите свои криптосбережения в безопасности - приватные ключи надежно хранятся на карте + Ультрабезопасная резервная копия + До трех карт с одним кошельком + Тысячи криптовалют + Аппаратный кошелек для ваших биткоинов, эфира и многих других валют одновременно — все в одной карте + Поддерживает Web3 + Обменивайте, покупайте NFT, получайте займы и вклады в более чем 100 различных децентрализованных сервисах + Кошелек для каждого + Используйте его на ходу, в любом месте, в любое время. Без проводов и аккумуляторов. Как только понадобится крипта, просто приложите карту к телефону. + Заказать карту + Приложение работает в демонстрационном режиме. Средства на всех счетах ненастоящие. + Эта функция недоступна в демонстрационном режиме + Транзакция успешно подписана, но не отправлена в блокчейн, потому что активирован демонстрационный режим. + \ No newline at end of file diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml index 496ccebf23..ce2c35b5fb 100644 --- a/app/src/main/res/values/dimens.xml +++ b/app/src/main/res/values/dimens.xml @@ -35,10 +35,41 @@ 32sp 16dp - -30dp - -20dp - 8dp - 70dp + 0.85 + + + 0.9 + -60dp + 0.9 + 0.9 + -40dp + 0.9 + 0.9 + + + 1 + 8dp + 0.1 + 10dp + 34dp + 34dp + 72dp + 12dp + + + 0dp + 50dp + 180dp 37dp + 0.48> + + + 40dp + 12dp + 8dp + 16dp + 38dp + 78dp + 78dp diff --git a/app/src/main/res/values/strings_final.xml b/app/src/main/res/values/strings_final.xml new file mode 100644 index 0000000000..339691d69b --- /dev/null +++ b/app/src/main/res/values/strings_final.xml @@ -0,0 +1,37 @@ + + + One Wallet + 3 cards + 2 cards + Shipping + Free + I have a promo code… + Total + Other payment methods + Buy now + Placing your order + Your order + Meet\nTangem + Buy + Store + Send + Pay + Exchange + Lend + Borrow + Stake + 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 + Web3 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 card + You are currently running in Demo mode. All funds are not real. + This feature is disabled in Demo mode. + The transaction has been successfully signed, but not sent to the blockchain because of Demo mode. + \ No newline at end of file diff --git a/app/src/main/res/values/strings_untranslated.xml b/app/src/main/res/values/strings_untranslated.xml index c254fbd636..f746827108 100644 --- a/app/src/main/res/values/strings_untranslated.xml +++ b/app/src/main/res/values/strings_untranslated.xml @@ -331,38 +331,4 @@ Amount to pay: %s Please tell us more about your issue. Every small detail can help. Following information is optional. You can erase it if you don’t want to share it. - One Wallet - 3 cards - 2 cards - Shipping - Free - I have a promo code... - Total - Other payment methods - Buy now - Order card - Delivery (Free shipping) - - - Meet\nTangem - Buy - Store - Send - Pay - Exchange - Lend - Borrow - Stake - 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 - Web3 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. - -