From 2abbc1a36585e625c603667f650615329691b70f Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 23 Jul 2021 13:02:20 +0300 Subject: [PATCH] Updated on 2026-08-14 --- app/src/main/AndroidManifest.xml | 26 ++++- .../tangem/tap/common/CustomTabsManager.kt | 21 ++++ .../com/tangem/tap/common/IntentHandler.kt | 26 +++++ .../com/tangem/tap/common/extensions/UI.kt | 5 + .../tap/common/redux/global/GlobalAction.kt | 5 + .../common/redux/global/GlobalMidlleware.kt | 18 ++++ .../tap/common/redux/global/GlobalReducer.kt | 3 + .../tap/common/redux/global/GlobalState.kt | 4 +- .../redux/navigation/NavigationAction.kt | 2 + .../redux/navigation/NavigationMiddleware.kt | 15 ++- .../redux/navigation/NavigationReducer.kt | 1 + .../com/tangem/tap/domain/TapWalletManager.kt | 12 ++- .../com/tangem/tap/domain/TapWorkarounds.kt | 2 +- .../tangem/tap/domain/topup/TopUpHelper.kt | 55 ---------- .../tap/domain/topup/TradeCryptoHelper.kt | 102 ++++++++++++++++++ .../tangem/tap/features/home/HomeFragment.kt | 3 + .../tap/features/send/BaseStoreFragment.kt | 12 ++- .../features/send/redux/SendScreenAction.kt | 8 ++ .../send/redux/middlewares/SendMiddleware.kt | 32 +++++- .../send/redux/reducers/SendScreenReducer.kt | 21 ++++ .../send/redux/states/AddressPayIdState.kt | 3 +- .../features/send/redux/states/SendState.kt | 14 ++- .../tap/features/send/ui/SendFragment.kt | 3 +- .../stateSubscribers/SendStateSubscriber.kt | 14 +++ .../tap/features/wallet/redux/WalletAction.kt | 31 ++++-- .../tap/features/wallet/redux/WalletState.kt | 38 ++++--- .../redux/middlewares/TopUpMiddleware.kt | 53 --------- .../middlewares/TradeCryptoMiddleware.kt | 94 ++++++++++++++++ .../redux/middlewares/WalletMiddleware.kt | 4 +- .../redux/reducers/MultiWalletReducer.kt | 25 +++-- .../wallet/redux/reducers/WalletReducer.kt | 31 ++++-- .../wallet/ui/WalletDetailsFragment.kt | 16 +-- .../ui/dialogs/ChooseTradeActionDialog.kt | 32 ++++++ .../wallet/ui/wallet/SingleWalletView.kt | 37 +++++-- .../tangem/tap/network/moonpay/MoonpayApi.kt | 16 +++ .../tap/network/moonpay/MoonpayService.kt | 24 +++++ .../drawable/ic_arrows_up_down_short_btn.xml | 24 +++++ .../main/res/layout/dialog_wallet_trade.xml | 47 ++++++++ app/src/main/res/layout/fragment_topup.xml | 2 +- app/src/main/res/layout/fragment_wallet.xml | 2 +- .../res/layout/fragment_wallet_details.xml | 53 ++++++--- .../res/layout/layout_send_address_payid.xml | 3 + .../main/res/layout/layout_send_amount.xml | 1 + .../layout/layout_wallet_short_buttons.xml | 4 +- app/src/main/res/values/colors.xml | 2 + .../main/res/values/strings_untranslated.xml | 7 +- 46 files changed, 747 insertions(+), 206 deletions(-) create mode 100644 app/src/main/java/com/tangem/tap/common/CustomTabsManager.kt delete mode 100644 app/src/main/java/com/tangem/tap/domain/topup/TopUpHelper.kt create mode 100644 app/src/main/java/com/tangem/tap/domain/topup/TradeCryptoHelper.kt delete mode 100644 app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/TopUpMiddleware.kt create mode 100644 app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/TradeCryptoMiddleware.kt create mode 100644 app/src/main/java/com/tangem/tap/features/wallet/ui/dialogs/ChooseTradeActionDialog.kt create mode 100644 app/src/main/java/com/tangem/tap/network/moonpay/MoonpayApi.kt create mode 100644 app/src/main/java/com/tangem/tap/network/moonpay/MoonpayService.kt create mode 100644 app/src/main/res/drawable/ic_arrows_up_down_short_btn.xml create mode 100644 app/src/main/res/layout/dialog_wallet_trade.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 97e4157f97..c2d4929889 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -81,9 +81,33 @@ + - + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/java/com/tangem/tap/common/CustomTabsManager.kt b/app/src/main/java/com/tangem/tap/common/CustomTabsManager.kt new file mode 100644 index 0000000000..a30af0f5d6 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/common/CustomTabsManager.kt @@ -0,0 +1,21 @@ +package com.tangem.tap.common + +import android.content.Context +import android.net.Uri +import androidx.browser.customtabs.CustomTabColorSchemeParams +import androidx.browser.customtabs.CustomTabsIntent +import com.tangem.tap.common.extensions.getColorCompat +import com.tangem.wallet.R + +class CustomTabsManager { + fun openUrl(url: String, context: Context) { + val customTabsIntent = CustomTabsIntent.Builder() + .setDefaultColorSchemeParams( + CustomTabColorSchemeParams.Builder() + .setNavigationBarColor(context.getColorCompat(R.color.toolbarColor)) + .build() + ) + .build() + customTabsIntent.launchUrl(context, Uri.parse(url)) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/IntentHandler.kt b/app/src/main/java/com/tangem/tap/common/IntentHandler.kt index f30a481c98..a804d20624 100644 --- a/app/src/main/java/com/tangem/tap/common/IntentHandler.kt +++ b/app/src/main/java/com/tangem/tap/common/IntentHandler.kt @@ -5,16 +5,20 @@ import android.nfc.NfcAdapter import android.nfc.Tag import com.tangem.tap.common.redux.navigation.AppScreen import com.tangem.tap.common.redux.navigation.NavigationAction +import com.tangem.tap.domain.topup.TradeCryptoHelper import com.tangem.tap.domain.walletconnect.WalletConnectManager import com.tangem.tap.features.details.redux.walletconnect.WalletConnectAction import com.tangem.tap.features.home.redux.HomeAction +import com.tangem.tap.features.wallet.redux.WalletAction import com.tangem.tap.store +import timber.log.Timber class IntentHandler { fun handleIntent(intent: Intent?) { handleBackgroundScan(intent) handleWalletConnectLink(intent) + handleSellCurrencyCallback(intent) } private fun handleBackgroundScan(intent: Intent?) { @@ -38,4 +42,26 @@ class IntentHandler { } } + private fun handleSellCurrencyCallback(intent: Intent?) { + val transactionID = + intent?.data?.getQueryParameter(TradeCryptoHelper.TRANSACTION_ID_PARAM) ?: return + val currency = + intent.data?.getQueryParameter(TradeCryptoHelper.CURRENCY_CODE_PARAM) ?: return + val amount = + intent.data?.getQueryParameter(TradeCryptoHelper.CURRENCY_AMOUNT_PARAM) ?: return + val destinationAddress = + intent.data?.getQueryParameter(TradeCryptoHelper.DEPOSIT_WALLET_ADDRESS_PARAM) + ?: return + + Timber.d("MoonPay Sell: $amount $currency to $destinationAddress") + + store.dispatch(WalletAction.TradeCryptoAction.SendCrypto( + currencyId = currency, + amount = amount, + destinationAddress = destinationAddress, + transactionId = transactionID + )) + } + + } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/extensions/UI.kt b/app/src/main/java/com/tangem/tap/common/extensions/UI.kt index b43c49b307..d29b032ef3 100644 --- a/app/src/main/java/com/tangem/tap/common/extensions/UI.kt +++ b/app/src/main/java/com/tangem/tap/common/extensions/UI.kt @@ -30,6 +30,11 @@ fun Fragment.getColor(@ColorRes colorRes: Int): Int { return ContextCompat.getColor(requireContext(), colorRes) } +@ColorInt +fun Context.getColorCompat(@ColorRes colorRes: Int): Int { + return ContextCompat.getColor(this, colorRes) +} + fun View.getString(@StringRes id: Int): String { return context.getString(id) } diff --git a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalAction.kt b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalAction.kt index 419fa5737d..bb32bfa7b4 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalAction.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalAction.kt @@ -9,6 +9,7 @@ import com.tangem.tap.domain.tasks.ScanNoteResponse import com.tangem.tap.features.details.redux.SecurityOption import com.tangem.tap.features.feedback.EmailData import com.tangem.tap.features.feedback.FeedbackManager +import com.tangem.tap.network.moonpay.MoonPayUserStatus import org.rekotlin.Action sealed class GlobalAction : Action { @@ -43,4 +44,8 @@ sealed class GlobalAction : Action { data class ShowDialog(val stateDialog: StateDialog) : GlobalAction() object HideDialog : GlobalAction() + + object GetMoonPayUserStatus : GlobalAction() { + data class Success(val moonPayUserStatus: MoonPayUserStatus) : GlobalAction() + } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalMidlleware.kt b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalMidlleware.kt index 2f79a5bbcc..e499b320c2 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalMidlleware.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalMidlleware.kt @@ -1,14 +1,19 @@ package com.tangem.tap.common.redux.global import com.tangem.TangemSdkError +import com.tangem.commands.common.network.Result import com.tangem.common.CompletionResult +import com.tangem.tap.common.extensions.dispatchOnMain import com.tangem.tap.common.redux.AppState import com.tangem.tap.domain.configurable.warningMessage.WarningMessagesManager import com.tangem.tap.features.home.redux.HomeAction import com.tangem.tap.features.send.redux.SendAction import com.tangem.tap.features.wallet.redux.WalletAction +import com.tangem.tap.network.moonpay.MoonpayService import com.tangem.tap.preferencesStorage +import com.tangem.tap.scope import com.tangem.tap.store +import kotlinx.coroutines.launch import org.rekotlin.Middleware val globalMiddleware: Middleware = { dispatch, appState -> @@ -60,6 +65,19 @@ val globalMiddleware: Middleware = { dispatch, appState -> store.state.globalState.feedbackManager?.infoHolder ?.setWalletsInfo(action.walletManagers) } + is GlobalAction.GetMoonPayUserStatus -> { + val apiKey = appState()?.globalState?.configManager?.config?.moonPayApiKey + if (apiKey != null) { + scope.launch { + val userStatusResponse = MoonpayService().getUserStatus(apiKey) + if (userStatusResponse is Result.Success) { + store.dispatchOnMain( + GlobalAction.GetMoonPayUserStatus.Success(userStatusResponse.data) + ) + } + } + } + } } nextDispatch(action) } diff --git a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalReducer.kt b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalReducer.kt index b7208034f7..7dbbe6f40e 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalReducer.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalReducer.kt @@ -73,6 +73,9 @@ fun globalReducer(action: Action, state: AppState): GlobalState { is GlobalAction.HideDialog -> { globalState.copy(dialog = null) } + is GlobalAction.GetMoonPayUserStatus.Success -> { + globalState.copy(moonPayUserStatus = action.moonPayUserStatus) + } else -> globalState } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalState.kt b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalState.kt index c7e173310b..77b02d5e8c 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalState.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalState.kt @@ -9,6 +9,7 @@ import com.tangem.tap.domain.configurable.warningMessage.WarningMessagesManager import com.tangem.tap.domain.tasks.ScanNoteResponse import com.tangem.tap.features.feedback.FeedbackManager import com.tangem.tap.network.coinmarketcap.CoinMarketCapService +import com.tangem.tap.network.moonpay.MoonPayUserStatus import org.rekotlin.StateType data class GlobalState( @@ -22,7 +23,8 @@ data class GlobalState( val feedbackManager: FeedbackManager? = null, val appCurrency: FiatCurrencyName = DEFAULT_FIAT_CURRENCY, val scanCardFailsCounter: Int = 0, - val dialog: StateDialog? = null + val dialog: StateDialog? = null, + val moonPayUserStatus: MoonPayUserStatus? = null ) : StateType typealias CryptoCurrencyName = String diff --git a/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationAction.kt b/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationAction.kt index 5cbdb915f6..6da57dfec8 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationAction.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationAction.kt @@ -10,6 +10,8 @@ sealed class NavigationAction : Action { data class PopBackTo(val screen: AppScreen? = null) : NavigationAction() + data class OpenUrl(val url: String) : NavigationAction() + data class ActivityCreated(val activity: WeakReference) : NavigationAction() object ActivityDestroyed : NavigationAction() } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationMiddleware.kt b/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationMiddleware.kt index 8c4d5e9fea..43c8ed406e 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationMiddleware.kt @@ -1,25 +1,30 @@ package com.tangem.tap.common.redux.navigation +import com.tangem.tap.common.CustomTabsManager import com.tangem.tap.common.extensions.openFragment import com.tangem.tap.common.extensions.popBackTo import com.tangem.tap.common.redux.AppState -import com.tangem.tap.store import org.rekotlin.Middleware val navigationMiddleware: Middleware = { dispatch, state -> { next -> { action -> if (action is NavigationAction) { - val navState = store.state.navigationState + val navState = state()?.navigationState when (action) { is NavigationAction.NavigateTo -> { - navState.activity?.get()?.openFragment(action.screen, action.addToBackstack) + navState?.activity?.get()?.openFragment(action.screen, action.addToBackstack) } is NavigationAction.PopBackTo -> { if (action.screen == AppScreen.Home) { - navState.activity?.get()?.popBackTo(null, true) + navState?.activity?.get()?.popBackTo(null, true) } else { - navState.activity?.get()?.popBackTo(action.screen) + navState?.activity?.get()?.popBackTo(action.screen) + } + } + is NavigationAction.OpenUrl -> { + navState?.activity?.get()?.let { + CustomTabsManager().openUrl(action.url, it) } } } diff --git a/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationReducer.kt b/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationReducer.kt index a4ce784435..80cd5e3260 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationReducer.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/navigation/NavigationReducer.kt @@ -27,5 +27,6 @@ private fun internalReduce(action: Action, state: AppState): NavigationState { } is NavigationAction.ActivityCreated -> navState.copy(activity = navigationAction.activity) is NavigationAction.ActivityDestroyed -> navState.copy(activity = null) + else -> navState } } \ No newline at end of file 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 9ae9105a46..992ddbc606 100644 --- a/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt @@ -174,7 +174,11 @@ class TapWalletManager { } } store.dispatch(WalletAction.SetArtworkId(data.verifyResponse?.artworkInfo?.id)) - store.dispatch(WalletAction.LoadWallet(config.isTopUpEnabled)) + val moonPayUserStatus = store.state.globalState.moonPayUserStatus + store.dispatch(WalletAction.LoadWallet( + allowToBuy = config.isTopUpEnabled && moonPayUserStatus?.isBuyAllowed == true, + allowToSell = config.isTopUpEnabled && moonPayUserStatus?.isSellAllowed == true, + )) store.dispatch(WalletAction.LoadArtwork(data.card, artworkId)) store.dispatch(WalletAction.LoadFiatRate()) } @@ -245,7 +249,11 @@ class TapWalletManager { return@withContext } val config = store.state.globalState.configManager?.config ?: return@withContext - store.dispatch(WalletAction.LoadWallet(config.isTopUpEnabled)) + val moonpayUserStatus = store.state.globalState.moonPayUserStatus + store.dispatch(WalletAction.LoadWallet( + allowToBuy = config.isTopUpEnabled && moonpayUserStatus?.isBuyAllowed == true, + allowToSell = config.isTopUpEnabled && moonpayUserStatus?.isSellAllowed == true, + )) store.dispatch(WalletAction.LoadFiatRate()) } } diff --git a/app/src/main/java/com/tangem/tap/domain/TapWorkarounds.kt b/app/src/main/java/com/tangem/tap/domain/TapWorkarounds.kt index 0c1a379a3c..6a6723cd83 100644 --- a/app/src/main/java/com/tangem/tap/domain/TapWorkarounds.kt +++ b/app/src/main/java/com/tangem/tap/domain/TapWorkarounds.kt @@ -20,7 +20,7 @@ object TapWorkarounds { val cardData = this.cardData ?: return false val productMask = cardData.productMask val excludedBatch = excludedBatches.contains(cardData.batchId) - val excludedIssuerName = excludedIssuers.contains(cardData.issuerName?.toUpperCase(Locale.US)) + val excludedIssuerName = excludedIssuers.contains(cardData.issuerName?.uppercase(Locale.ROOT)) val excludedProductMask = (productMask != null && // product mask is on cards v2.30 and later !productMask.contains(Product.Note) && !productMask.contains(Product.TwinCard)) return excludedBatch || excludedIssuerName || excludedProductMask diff --git a/app/src/main/java/com/tangem/tap/domain/topup/TopUpHelper.kt b/app/src/main/java/com/tangem/tap/domain/topup/TopUpHelper.kt deleted file mode 100644 index 2ab6b843c8..0000000000 --- a/app/src/main/java/com/tangem/tap/domain/topup/TopUpHelper.kt +++ /dev/null @@ -1,55 +0,0 @@ -package com.tangem.tap.domain.topup - -import android.net.Uri -import android.util.Base64 -import com.tangem.blockchain.common.Blockchain -import com.tangem.tap.common.redux.global.CryptoCurrencyName -import javax.crypto.Mac -import javax.crypto.spec.SecretKeySpec - -class TopUpHelper { - - companion object { -// const val REDIRECT_URL = "https://success.tangem.com" - - private const val BASE_URL = "https://buy.moonpay.io" - private const val API_KEY_PATH = "?apiKey=" - private const val CURRENCY_PATH = "¤cyCode=" - private const val WALLET_ADDRESS_PATH = "&walletAddress=" - - // private const val REDIRECT_URL_PATH = "&redirectUrl=" - private const val SIGNATURE_PATH = "&signature=" - - fun getUrl( - blockchain: Blockchain?, - cryptoCurrencyName: CryptoCurrencyName, - walletAddress: String, - apiKey: String, - secretKey: String, - ): String { - if (blockchain?.isTestnet() == true) { - return blockchain.getTestnetTopUpUrl() ?: "" - } - val originalQuery = API_KEY_PATH + apiKey.urlEncode() + - CURRENCY_PATH + cryptoCurrencyName.urlEncode() + - WALLET_ADDRESS_PATH + walletAddress.urlEncode() -// REDIRECT_URL_PATH + REDIRECT_URL.urlEncode() - val signature = createSignature(originalQuery, secretKey) - - return BASE_URL + originalQuery + SIGNATURE_PATH + signature.urlEncode() - } - - private fun String.urlEncode(): String { - return Uri.encode(this) - } - - - private fun createSignature(data: String, key: String): String { - val sha256Hmac = Mac.getInstance("HmacSHA256") - val secretKey = SecretKeySpec(key.toByteArray(), "HmacSHA256") - sha256Hmac.init(secretKey) - val sha256encoded = sha256Hmac.doFinal(data.toByteArray()) - return Base64.encodeToString(sha256encoded, Base64.NO_WRAP) - } - } -} diff --git a/app/src/main/java/com/tangem/tap/domain/topup/TradeCryptoHelper.kt b/app/src/main/java/com/tangem/tap/domain/topup/TradeCryptoHelper.kt new file mode 100644 index 0000000000..f16a2591b2 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/domain/topup/TradeCryptoHelper.kt @@ -0,0 +1,102 @@ +package com.tangem.tap.domain.topup + +import android.net.Uri +import android.util.Base64 +import com.tangem.blockchain.common.Blockchain +import com.tangem.tap.common.redux.global.CryptoCurrencyName +import javax.crypto.Mac +import javax.crypto.spec.SecretKeySpec + +class TradeCryptoHelper { + + companion object { + + const val TRANSACTION_ID_PARAM = "transactionId" + const val CURRENCY_CODE_PARAM = "baseCurrencyCode" + const val CURRENCY_AMOUNT_PARAM = "baseCurrencyAmount" + const val DEPOSIT_WALLET_ADDRESS_PARAM = "depositWalletAddress" + + private const val REDIRECT_URL_BUY = "tangem://success.tangem.com" + private const val REDIRECT_URL_SELL_REQUEST = "tangem://sell-request.tangem.com" + + private const val BASE_URL_BUY = + "https://buy.moonpay.io" +// "https://buy-staging.moonpay.io" // TESTNET + private const val BASE_URL_SELL = + "https://sell.moonpay.com" +// "https://sell-staging.moonpay.com" // TESTNET + + private const val API_KEY_PATH = "?apiKey=" + private const val CURRENCY_PATH = "¤cyCode=" + private const val WALLET_ADDRESS_PATH = "&walletAddress=" + + private const val REFUND_WALLET_ADDRESS_PATH = "&refundWalletAddress=" + private const val BASE_CURRENCY_PATH = "&baseCurrencyCode=" + + private const val REDIRECT_URL_PATH = "&redirectURL=" + private const val SIGNATURE_PATH = "&signature=" + + private const val TRANSACTION_RECEIPT_PATH = "transaction_receipt?transactionId=" + + val AVAILABLE_TO_BUY: Set = setOf( + "ZRX", "AAVE", "ALGO", "AXS", "BAT", "BNB", "BUSD", "BTC", "BCH", "BTT", "ADA", "CELO", "CUSD", "LINK", "CHZ", "COMP", "ATOM", "DAI", "DASH", "MANA", "DGB", "DOGE", "EGLD", + "ENJ", "EOS", "ETC", "ETH", "KETH", "RINKETH", "FIL", "HBAR", "MIOTA", "KAVA", "KLAY", "LBC", "LTC", "LUNA", "MKR", "OM", "MATIC", "NANO", "NEAR", "XEM", "NEO", "NIM", "OKB", + "OMG", "ONG", "ONT", "DOT", "QTUM", "RVN", "RFUEL", "KEY", "SRM", "SOL", "XLM", "STMX", "SNX", "KRT", "UST", "USDT", "XTZ", "RUNE", "SAND", "TOMO", "AVA", "TRX", "TUSD", "UNI", + "USDC", "UTK", "VET", "WAXP", "WBTC", "XRP", "ZEC", "ZIL" + ) + + val AVAILABLE_TO_SELL: Set = setOf("BTC", "ETH", "BCH") + + fun getUrl( + action: Action, + blockchain: Blockchain?, + cryptoCurrencyName: CryptoCurrencyName, + walletAddress: String, + apiKey: String, + secretKey: String, + ): String { + val originalQuery: String + val baseUrl: String + when (action) { + Action.Buy -> { + if (blockchain?.isTestnet() == true) { + return blockchain.getTestnetTopUpUrl() ?: "" + } + baseUrl = BASE_URL_BUY + originalQuery = API_KEY_PATH + apiKey.urlEncode() + + CURRENCY_PATH + cryptoCurrencyName.urlEncode() + + WALLET_ADDRESS_PATH + walletAddress.urlEncode() + + REDIRECT_URL_PATH + REDIRECT_URL_BUY.urlEncode() + } + Action.Sell -> { + baseUrl = BASE_URL_SELL + originalQuery = API_KEY_PATH + apiKey.urlEncode() + + BASE_CURRENCY_PATH + cryptoCurrencyName.urlEncode() + + REFUND_WALLET_ADDRESS_PATH + walletAddress.urlEncode() + + REDIRECT_URL_PATH + REDIRECT_URL_SELL_REQUEST.urlEncode() + } + } + val signature = createSignature(originalQuery, secretKey) + return baseUrl + originalQuery + SIGNATURE_PATH + signature.urlEncode() + + } + + fun getSellCryptoReceiptUrl(transactionId: String): String { + return BASE_URL_SELL + TRANSACTION_RECEIPT_PATH + transactionId + } + + private fun String.urlEncode(): String { + return Uri.encode(this) + } + + private fun createSignature(data: String, key: String): String { + val sha256Hmac = Mac.getInstance("HmacSHA256") + val secretKey = SecretKeySpec(key.toByteArray(), "HmacSHA256") + sha256Hmac.init(secretKey) + val sha256encoded = sha256Hmac.doFinal(data.toByteArray()) + return Base64.encodeToString(sha256encoded, Base64.NO_WRAP) + } + } + + enum class Action { Buy, Sell } +} diff --git a/app/src/main/java/com/tangem/tap/features/home/HomeFragment.kt b/app/src/main/java/com/tangem/tap/features/home/HomeFragment.kt index c93543613e..29cd04dc73 100644 --- a/app/src/main/java/com/tangem/tap/features/home/HomeFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/home/HomeFragment.kt @@ -5,6 +5,7 @@ import android.os.Bundle import android.view.View import androidx.fragment.app.Fragment import androidx.transition.TransitionInflater +import com.tangem.tap.common.redux.global.GlobalAction import com.tangem.tap.common.redux.global.StateDialog import com.tangem.tap.features.home.redux.HomeAction import com.tangem.tap.features.home.redux.HomeDialog @@ -66,6 +67,8 @@ class HomeFragment : Fragment(R.layout.fragment_home), StoreSubscriber) : SendAction() } + + data class SendSpecificTransaction( + val sendAmount: String, + val destinationAddress: String, + val transactionId: String + ) : SendAction() + } \ No newline at end of file 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 9ca7c9ce1c..d24bfa3949 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 @@ -9,6 +9,7 @@ import com.tangem.blockchain.extensions.SimpleResult import com.tangem.commands.common.card.Card import com.tangem.tap.common.analytics.AnalyticsEvent import com.tangem.tap.common.analytics.FirebaseAnalyticsHandler +import com.tangem.tap.common.extensions.dispatchOnMain import com.tangem.tap.common.extensions.stripZeroPlainString import com.tangem.tap.common.redux.AppState import com.tangem.tap.common.redux.global.GlobalAction @@ -20,6 +21,8 @@ import com.tangem.tap.domain.configurable.warningMessage.WarningMessage import com.tangem.tap.domain.extensions.minimalAmount import com.tangem.tap.features.send.redux.* import com.tangem.tap.features.send.redux.FeeAction.RequestFee +import com.tangem.tap.features.send.redux.states.ExternalTransactionData +import com.tangem.tap.features.send.redux.states.MainCurrencyType import com.tangem.tap.features.send.redux.states.SendButtonState import com.tangem.tap.features.send.redux.states.TransactionExtrasState import com.tangem.tap.features.wallet.redux.WalletAction @@ -49,6 +52,18 @@ val sendMiddleware: Middleware = { dispatch, appState -> verifyAndSendTransaction(action, appState(), dispatch) is PrepareSendScreen -> setIfSendingToPayIdEnabled(appState(), dispatch) is SendAction.Warnings.Update -> updateWarnings(dispatch) + is SendActionUi.CheckIfTransactionDataWasProvided -> { + val transactionData = appState()?.sendState?.externalTransactionData + if (transactionData != null) { + store.dispatchOnMain(AddressPayIdVerifyAction.AddressVerification.SetWalletAddress( + transactionData.destinationAddress, false + )) + store.dispatchOnMain(AmountActionUi.SetMainCurrency(MainCurrencyType.CRYPTO)) + store.dispatchOnMain(AmountActionUi.HandleUserInput(transactionData.amount)) + store.dispatchOnMain(AmountAction.SetAmount(transactionData.amount.toBigDecimal(), + false)) + } + } } nextDispatch(action) } @@ -76,8 +91,11 @@ private fun verifyAndSendTransaction( dispatch(AmountAction.SetAmount(typedAmount.value!!.minus(reduceAmount), false)) dispatch(AmountActionUi.CheckAmountToSend) }, sendAllCallback = { - sendTransaction(action, walletManager, amountToSend, feeAmount, destinationAddress, - sendState.transactionExtrasState, card, dispatch) + sendTransaction( + action, walletManager, amountToSend, feeAmount, destinationAddress, + sendState.transactionExtrasState, card, sendState.externalTransactionData, + dispatch + ) }, reduceAmount)) } transactionErrors.isNotEmpty() -> { @@ -85,7 +103,7 @@ private fun verifyAndSendTransaction( } else -> { sendTransaction(action, walletManager, amountToSend, feeAmount, destinationAddress, - sendState.transactionExtrasState, card, dispatch) + sendState.transactionExtrasState, card, sendState.externalTransactionData, dispatch) } } } @@ -98,6 +116,7 @@ private fun sendTransaction( destinationAddress: String, transactionExtras: TransactionExtrasState, card: Card, + externalTransactionData: ExternalTransactionData?, dispatch: (Action) -> Unit, ) { dispatch(SendAction.ChangeSendButtonState(SendButtonState.PROGRESS)) @@ -134,7 +153,12 @@ private fun sendTransaction( blockchain = walletManager.wallet.blockchain ) dispatch(SendAction.SendSuccess) - dispatch(NavigationAction.PopBackTo()) + + if (externalTransactionData != null) { + dispatch(WalletAction.TradeCryptoAction.FinishSelling(externalTransactionData.transactionId)) + } else { + dispatch(NavigationAction.PopBackTo()) + } scope.launch(Dispatchers.IO) { withContext(Dispatchers.Main) { dispatch(WalletAction.UpdateWallet(walletManager.wallet.blockchain)) diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/reducers/SendScreenReducer.kt b/app/src/main/java/com/tangem/tap/features/send/redux/reducers/SendScreenReducer.kt index 8cad24d39c..58b36afe00 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/reducers/SendScreenReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/reducers/SendScreenReducer.kt @@ -3,6 +3,7 @@ package com.tangem.tap.features.send.redux.reducers import com.tangem.blockchain.common.AmountType import com.tangem.tap.common.CurrencyConverter import com.tangem.tap.features.send.redux.* +import com.tangem.tap.features.send.redux.states.ExternalTransactionData import com.tangem.tap.features.send.redux.states.IdStateHolder import com.tangem.tap.features.send.redux.states.SendState import org.rekotlin.Action @@ -45,11 +46,31 @@ private class SendReducer : SendInternalReducer { is SendAction.Dialog.SendTransactionFails -> sendState.copy(dialog = action) is SendAction.Dialog.Hide -> sendState.copy(dialog = null) is SendAction.Warnings.Set -> sendState.copy(sendWarningsList = action.warningList) + is SendAction.SendSpecificTransaction -> + handleSendSpecificTransactionAction(action, sendState) else -> return sendState } return updateLastState(result, result) } + + private fun handleSendSpecificTransactionAction( + action: SendAction.SendSpecificTransaction, state: SendState + ): SendState { + return state.copy( + externalTransactionData = + ExternalTransactionData(action.sendAmount, action.destinationAddress, action.transactionId), + feeState = state.feeState.copy( + includeFeeSwitcherIsEnabled = false + ), + amountState = state.amountState.copy( + inputIsEnabled = false + ), + addressPayIdState = state.addressPayIdState.copy( + inputIsEnabled = false + ), + ) + } } private class EmptyReducer : SendInternalReducer { diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/states/AddressPayIdState.kt b/app/src/main/java/com/tangem/tap/features/send/redux/states/AddressPayIdState.kt index d17531a84a..4f2e4e1a6d 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/states/AddressPayIdState.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/states/AddressPayIdState.kt @@ -12,7 +12,8 @@ data class AddressPayIdState( val error: AddressPayIdVerifyAction.Error? = null, val truncateHandler: ((String) -> String)? = null, val sendingToPayIdEnabled: Boolean = false, - val pasteIsEnabled: Boolean = false + val pasteIsEnabled: Boolean = false, + val inputIsEnabled: Boolean = true ) : SendScreenState { override val stateId: StateId = StateId.ADDRESS_PAY_ID diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/states/SendState.kt b/app/src/main/java/com/tangem/tap/features/send/redux/states/SendState.kt index 41d2fd79c5..eed6fa793c 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/states/SendState.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/states/SendState.kt @@ -40,7 +40,8 @@ data class SendState( val receiptState: ReceiptState = ReceiptState(), val sendWarningsList: List = listOf(), val sendButtonState: SendButtonState = SendButtonState.DISABLED, - val dialog: StateDialog? = null + val dialog: StateDialog? = null, + val externalTransactionData: ExternalTransactionData? = null ) : SendScreenState { override val stateId: StateId = StateId.SEND_SCREEN @@ -131,7 +132,8 @@ data class AmountState( val cursorAtTheSamePosition: Boolean = true, val maxLengthOfAmount: Int = 2, val decimalSeparator: String = ".", - val error: TapError? = null + val error: TapError? = null, + val inputIsEnabled: Boolean = true, ) : SendScreenState { override val stateId: StateId = StateId.AMOUNT @@ -166,4 +168,12 @@ data class MainCurrency( val type: MainCurrencyType, val currencySymbol: String, val isEnabled: Boolean = true +) + +data class ExternalTransactionData( + val amount: String, + val destinationAddress: String, + val transactionId: String, + val canAmountBeModified: Boolean = false, + val canDestinationAddressBeModified: Boolean = false ) \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/send/ui/SendFragment.kt b/app/src/main/java/com/tangem/tap/features/send/ui/SendFragment.kt index 2b2b682294..9736cc16e0 100644 --- a/app/src/main/java/com/tangem/tap/features/send/ui/SendFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/send/ui/SendFragment.kt @@ -71,6 +71,7 @@ class SendFragment : BaseStoreFragment(R.layout.fragment_send) { setupAmountLayout() setupFeeLayout() setupWarningMessages() + store.dispatch(SendActionUi.CheckIfTransactionDataWasProvided) } private fun initSendButtonStates() { @@ -250,7 +251,7 @@ class SendFragment : BaseStoreFragment(R.layout.fragment_send) { val sp = requireContext().getSharedPreferences("SendScreen", Context.MODE_PRIVATE) val mainCurrency = sp.getString("mainCurrency", TapCurrency.DEFAULT_FIAT_CURRENCY) val foundType = MainCurrencyType.values() - .firstOrNull { it.name.toLowerCase() == mainCurrency!!.toLowerCase() } + .firstOrNull { it.name.equals(mainCurrency!!, ignoreCase = true) } ?: MainCurrencyType.CRYPTO return foundType } diff --git a/app/src/main/java/com/tangem/tap/features/send/ui/stateSubscribers/SendStateSubscriber.kt b/app/src/main/java/com/tangem/tap/features/send/ui/stateSubscribers/SendStateSubscriber.kt index 933b1580d7..05c7af7d18 100644 --- a/app/src/main/java/com/tangem/tap/features/send/ui/stateSubscribers/SendStateSubscriber.kt +++ b/app/src/main/java/com/tangem/tap/features/send/ui/stateSubscribers/SendStateSubscriber.kt @@ -156,6 +156,12 @@ class SendStateSubscriber(fragment: BaseStoreFragment) : FragmentStateSubscriber val til = fg.tilAddressOrPayId val parsedError = parseError(til.context, state.error) + til.isEnabled = state.inputIsEnabled + fg.imvPaste.show(state.inputIsEnabled) + fg.imvQrCode.show(state.inputIsEnabled) + fg.flPaste.show(state.inputIsEnabled) + fg.flQrCode.show(state.inputIsEnabled) + val hintResId = if (state.sendingToPayIdEnabled) { R.string.send_destination_hint_address_payid } else { @@ -198,6 +204,14 @@ class SendStateSubscriber(fragment: BaseStoreFragment) : FragmentStateSubscriber state.mainCurrency.currencySymbol, state.viewBalanceValue) fg.tvBalance.update(balanceText) + + fg.tilAmountToSend.isEnabled = state.inputIsEnabled + + val imageRes = if (state.inputIsEnabled) R.drawable.ic_arrows_up_down else 0 + fg.tvAmountCurrency.setCompoundDrawablesWithIntrinsicBounds(0, 0, imageRes, 0) + val textColor = if (state.inputIsEnabled) R.color.blue else R.color.textGray + fg.tvAmountCurrency.setTextColor(fg.getColor(textColor)) + } private fun handleFeeState(fg: BaseStoreFragment, state: FeeState) { diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletAction.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletAction.kt index 43617cb446..cff630a1a8 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletAction.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletAction.kt @@ -25,7 +25,11 @@ sealed class WalletAction : Action { } - data class LoadWallet(val allowTopUp: Boolean? = null, val blockchain: Blockchain? = null) : WalletAction() { + data class LoadWallet( + val allowToSell: Boolean? = null, val allowToBuy: Boolean? = null, + val blockchain: Blockchain? = null, + ) : + WalletAction() { data class Success(val wallet: Wallet) : WalletAction() data class NoAccount(val wallet: Wallet, val amountToCreateAccount: String) : WalletAction() data class Failure(val wallet: Wallet, val errorMessage: String? = null) : WalletAction() @@ -39,13 +43,16 @@ sealed class WalletAction : Action { data class AddWalletManagers(val walletManagers: List) : MultiWallet() { constructor(walletManager: WalletManager) : this(listOf(walletManager)) } + data class AddBlockchain(val blockchain: Blockchain) : MultiWallet() data class AddBlockchains(val blockchains: List) : MultiWallet() data class AddTokens(val tokens: List) : MultiWallet() data class AddToken(val token: Token) : MultiWallet() data class SaveCurrencies(val cardCurrencies: CardCurrencies) : MultiWallet() object FindTokensInUse : MultiWallet() - data class FindBlockchainsInUse(val card: Card, val factory: WalletManagerFactory) : MultiWallet() + data class FindBlockchainsInUse(val card: Card, val factory: WalletManagerFactory) : + MultiWallet() + data class TokenLoaded(val amount: Amount, val token: Token) : MultiWallet() data class SelectWallet(val walletData: WalletData?) : MultiWallet() data class RemoveWallet(val walletData: WalletData) : MultiWallet() @@ -69,6 +76,7 @@ sealed class WalletAction : Action { object SetNeverToShow : Warnings() object RemindLater : Warnings() } + class CheckRemainingSignatures(val remainingSignatures: Int?) : Warnings() } @@ -79,7 +87,7 @@ sealed class WalletAction : Action { } data class LoadFiatRate( - val wallet: Wallet? = null, val currency: Currency? = null + val wallet: Wallet? = null, val currency: Currency? = null, ) : WalletAction() { data class Success(val fiatRate: Pair) : WalletAction() object Failure : WalletAction() @@ -114,6 +122,7 @@ sealed class WalletAction : Action { object QrCode : WalletAction() object ScanFails : WalletAction() object SignedHashesMultiWalletDialog : WalletAction() + object ChooseTradeActionDialog : WalletAction() } object HideDialog : WalletAction() @@ -123,8 +132,16 @@ sealed class WalletAction : Action { object CreateWallet : WalletAction() object EmptyWallet : WalletAction() - sealed class TopUpAction : WalletAction() { - data class TopUp(val context: Context, val toolbarColor: Int) : TopUpAction() + sealed class TradeCryptoAction : WalletAction() { + object Buy : TradeCryptoAction() + object Sell : TradeCryptoAction() + data class FinishSelling(val transactionId: String) : TradeCryptoAction() + data class SendCrypto( + val currencyId: String, + val amount: String, + val destinationAddress: String, + val transactionId: String + ) : TradeCryptoAction() } data class ChangeSelectedAddress(val type: AddressType) : WalletAction() @@ -133,8 +150,8 @@ sealed class WalletAction : Action { object ShowOnboarding : TwinsAction() object SetOnboardingShown : TwinsAction() data class SetTwinCard( - val secondCardId: String, val number: TwinCardNumber, - val isCreatingTwinCardsAllowed: Boolean, + val secondCardId: String, val number: TwinCardNumber, + val isCreatingTwinCardsAllowed: Boolean, ) : TwinsAction() } } \ No newline at end of file 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 afa3936fb4..d08f8caecf 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 @@ -9,6 +9,7 @@ import com.tangem.tap.common.redux.global.CryptoCurrencyName import com.tangem.tap.common.redux.global.StateDialog import com.tangem.tap.domain.configurable.warningMessage.WarningMessage import com.tangem.tap.domain.extensions.toSendableAmounts +import com.tangem.tap.domain.topup.TradeCryptoHelper import com.tangem.tap.domain.twins.TwinCardNumber import com.tangem.tap.features.wallet.models.PendingTransaction import com.tangem.tap.features.wallet.models.toPendingTransactions @@ -34,6 +35,7 @@ data class WalletState( val primaryBlockchain: Blockchain? = null, val primaryToken: Token? = null, val isTestnet: Boolean = false, + val tradeCryptoAllowed: TradeCryptoAvailability = TradeCryptoAvailability() ) : StateType { val primaryWallet = if (wallets.isNotEmpty()) wallets[0] else null @@ -141,6 +143,7 @@ sealed class WalletDialog: StateDialog { data class SelectAmountToSendDialog(val amounts: List?) : WalletDialog() object ScanFailsDialog : WalletDialog() object SignedHashesMultiWalletDialog : WalletDialog() + object ChooseTradeActionDialog : WalletDialog() } enum class ProgressState { Loading, Done, Error } @@ -180,10 +183,15 @@ data class Artwork( } } -data class TopUpState( - val allowed: Boolean = true, - val url: String? = null, - val redirectUrl: String? = null +data class TradeCryptoState( + val sellingAllowed: Boolean = false, + val buyingAllowed: Boolean = false, +) + +data class TradeCryptoAvailability( + val sellingAllowed: Boolean = false, + val buyingAllowed: Boolean = false, + val availableToSell: Set = TradeCryptoHelper.AVAILABLE_TO_SELL, ) data class TwinCardsState( @@ -194,17 +202,17 @@ data class TwinCardsState( ) data class WalletData( - val pendingTransactions: List = emptyList(), - val hashesCountVerified: Boolean? = null, - val walletAddresses: WalletAddresses? = null, - val currencyData: BalanceWidgetData = BalanceWidgetData(), - val updatingWallet: Boolean = false, - val topUpState: TopUpState = TopUpState(), - val allowToSend: Boolean = true, - val fiatRateString: String? = null, - val fiatRate: BigDecimal? = null, - val mainButton: WalletMainButton = WalletMainButton.SendButton(false), - val currency: Currency? = null + val pendingTransactions: List = emptyList(), + val hashesCountVerified: Boolean? = null, + val walletAddresses: WalletAddresses? = null, + val currencyData: BalanceWidgetData = BalanceWidgetData(), + val updatingWallet: Boolean = false, + val tradeCryptoState: TradeCryptoState = TradeCryptoState(), + val allowToSend: Boolean = true, + val fiatRateString: String? = null, + val fiatRate: BigDecimal? = null, + val mainButton: WalletMainButton = WalletMainButton.SendButton(false), + val currency: Currency? = null ) { fun shouldShowMultipleAddress(): Boolean { val listOfAddresses = walletAddresses?.list ?: return false diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/TopUpMiddleware.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/TopUpMiddleware.kt deleted file mode 100644 index 98b63904ba..0000000000 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/TopUpMiddleware.kt +++ /dev/null @@ -1,53 +0,0 @@ -package com.tangem.tap.features.wallet.redux.middlewares - -import android.net.Uri -import androidx.browser.customtabs.CustomTabsIntent -import com.tangem.blockchain.blockchains.ethereum.EthereumWalletManager -import com.tangem.tap.domain.topup.TopUpHelper -import com.tangem.tap.domain.topup.TopUpManager -import com.tangem.tap.features.wallet.redux.Currency -import com.tangem.tap.features.wallet.redux.WalletAction -import com.tangem.tap.scope -import com.tangem.tap.store -import kotlinx.coroutines.launch - - -class TopUpMiddleware { - fun handle(action: WalletAction.TopUpAction) { - when (action) { - is WalletAction.TopUpAction.TopUp -> { - val selectedWalletData = store.state.walletState.getSelectedWalletData() - val config = store.state.globalState.configManager?.config ?: return - val addresses = selectedWalletData?.walletAddresses ?: return - if (addresses.list.isEmpty()) return - - val defaultAddress = addresses.list[0].address - val currency = selectedWalletData.currency - - if (currency is Currency.Token && currency.blockchain.isTestnet()) { - val walletManager = store.state.walletState.getWalletManager(currency.token) - if (walletManager !is EthereumWalletManager) return - - scope.launch { - TopUpManager().topUpTestErc20Tokens( - walletManager = walletManager, token = currency.token - ) - } - } else { - val url = TopUpHelper.getUrl( - currency?.blockchain, - selectedWalletData.currencyData.currencySymbol!!, - defaultAddress, - config.moonPayApiKey, - config.moonPayApiSecretKey - ) - - val customTabsIntent = CustomTabsIntent.Builder() - .setToolbarColor(action.toolbarColor) - .build() - customTabsIntent.launchUrl(action.context, Uri.parse(url)) - } - } - } - } -} \ No newline at end of file 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 new file mode 100644 index 0000000000..2b2087dd25 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/TradeCryptoMiddleware.kt @@ -0,0 +1,94 @@ +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.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.send.redux.PrepareSendScreen +import com.tangem.tap.features.send.redux.SendAction +import com.tangem.tap.features.wallet.redux.Currency +import com.tangem.tap.features.wallet.redux.WalletAction +import com.tangem.tap.scope +import com.tangem.tap.store +import kotlinx.coroutines.launch +import timber.log.Timber + + +class TradeCryptoMiddleware { + fun handle(action: WalletAction.TradeCryptoAction) { + when (action) { + is WalletAction.TradeCryptoAction.Buy -> startExchange(action) + is WalletAction.TradeCryptoAction.Sell -> startExchange(action) + is WalletAction.TradeCryptoAction.SendCrypto -> preconfigureAndOpenSendScreen(action) + is WalletAction.TradeCryptoAction.FinishSelling -> openReceiptUrl(action.transactionId) + } + } + + private fun startExchange(action: WalletAction.TradeCryptoAction) { + val selectedWalletData = store.state.walletState.getSelectedWalletData() + val config = store.state.globalState.configManager?.config ?: return + val addresses = selectedWalletData?.walletAddresses ?: return + if (addresses.list.isEmpty()) return + + val defaultAddress = addresses.list[0].address + val currency = selectedWalletData.currency + val currencySymbol = selectedWalletData.currency?.currencySymbol ?: return + + val exchangeAction = if (action is WalletAction.TradeCryptoAction.Buy) { + TradeCryptoHelper.Action.Buy + } else { + TradeCryptoHelper.Action.Sell + } + + if (exchangeAction == TradeCryptoHelper.Action.Buy && + currency is Currency.Token && currency.blockchain.isTestnet() + ) { + val walletManager = store.state.walletState.getWalletManager(currency.token) + if (walletManager !is EthereumWalletManager) return + + scope.launch { + TopUpManager().topUpTestErc20Tokens( + walletManager = walletManager, token = currency.token + ) + } + return + } + + val url = TradeCryptoHelper.getUrl( + exchangeAction, + currency?.blockchain, + currencySymbol, + defaultAddress, + config.moonPayApiKey, + config.moonPayApiSecretKey + ) + Timber.d("Moonpay $exchangeAction URL: $url") + store.dispatchOnMain(NavigationAction.OpenUrl(url)) + } + + private fun preconfigureAndOpenSendScreen(action: WalletAction.TradeCryptoAction.SendCrypto) { + val selectedWalletData = store.state.walletState.getSelectedWalletData() ?: return + val walletManager = + store.state.walletState.getWalletManager(selectedWalletData.currency) + store.dispatchOnMain(PrepareSendScreen( + coinAmount = walletManager?.wallet?.amounts?.get(AmountType.Coin), + coinRate = selectedWalletData.fiatRate, + walletManager = walletManager + )) + store.dispatchOnMain(SendAction.SendSpecificTransaction( + sendAmount = action.amount, + destinationAddress = action.destinationAddress, + transactionId = action.transactionId + )) + store.dispatchOnMain(NavigationAction.NavigateTo(AppScreen.Send)) + } + + private fun openReceiptUrl(transactionId: String) { + store.dispatchOnMain(NavigationAction.PopBackTo()) + val url = TradeCryptoHelper.getSellCryptoReceiptUrl(transactionId) + store.dispatchOnMain(NavigationAction.OpenUrl(url)) + } +} \ No newline at end of file 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 a03fbe9c16..8dc207048b 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 @@ -28,7 +28,7 @@ import org.rekotlin.Action import org.rekotlin.Middleware class WalletMiddleware { - private val topUpMiddleware = TopUpMiddleware() + private val tradeCryptoMiddleware = TradeCryptoMiddleware() private val twinsMiddleware = TwinsMiddleware() private val warningsMiddleware = WarningsMiddleware() private val multiWalletMiddleware = MultiWalletMiddleware() @@ -39,7 +39,7 @@ class WalletMiddleware { val globalState = state()?.globalState val walletState = state()?.walletState when (action) { - is WalletAction.TopUpAction -> topUpMiddleware.handle(action) + is WalletAction.TradeCryptoAction -> tradeCryptoMiddleware.handle(action) is WalletAction.TwinsAction -> twinsMiddleware.handle(action) is WalletAction.Warnings -> warningsMiddleware.handle(action, globalState) is WalletAction.MultiWallet -> diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/MultiWalletReducer.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/MultiWalletReducer.kt index 52a557ead6..030c2e2ab6 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/MultiWalletReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/MultiWalletReducer.kt @@ -1,7 +1,6 @@ package com.tangem.tap.features.wallet.redux.reducers import com.tangem.blockchain.common.AmountType -import com.tangem.blockchain.common.Blockchain import com.tangem.blockchain.common.Token import com.tangem.common.extensions.isZero import com.tangem.tap.common.extensions.toFiatString @@ -38,8 +37,12 @@ class MultiWalletReducer { ), walletAddresses = createAddressList(wallet), mainButton = WalletMainButton.SendButton(false), - topUpState = TopUpState(allowed = false), - currency = Currency.Blockchain(blockchain) + currency = Currency.Blockchain(blockchain), + tradeCryptoState = TradeCryptoState( + sellingAllowed = state.tradeCryptoAllowed.sellingAllowed && + state.tradeCryptoAllowed.availableToSell.contains(blockchain.currency), + buyingAllowed = state.tradeCryptoAllowed.buyingAllowed + ) ) } @@ -63,8 +66,12 @@ class MultiWalletReducer { ), walletAddresses = createAddressList(wallet), mainButton = WalletMainButton.SendButton(false), - topUpState = TopUpState(allowed = false), - currency = Currency.Blockchain(action.blockchain) + currency = Currency.Blockchain(action.blockchain), + tradeCryptoState = TradeCryptoState( + sellingAllowed = state.tradeCryptoAllowed.sellingAllowed && + state.tradeCryptoAllowed.availableToSell.contains(action.blockchain.currency), + buyingAllowed = state.tradeCryptoAllowed.buyingAllowed + ) ) val newState = state.copy(wallets = state.replaceWalletInWallets(walletData)) if (wallet != null && wallet.amounts[AmountType.Coin]?.value != null) { @@ -165,7 +172,11 @@ fun Token.toWallet(state: WalletState): WalletData? { ), walletAddresses = walletAddresses, mainButton = WalletMainButton.SendButton(false), - topUpState = TopUpState(allowed = false), - currency = Currency.Token(this) + currency = Currency.Token(this), + tradeCryptoState = TradeCryptoState( + sellingAllowed = state.tradeCryptoAllowed.sellingAllowed && + state.tradeCryptoAllowed.availableToSell.contains(this.symbol), + buyingAllowed = state.tradeCryptoAllowed.buyingAllowed + ) ) } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/WalletReducer.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/WalletReducer.kt index b39e928706..3298b8a9cc 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/WalletReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/WalletReducer.kt @@ -50,7 +50,6 @@ private fun internalReduce(action: Action, state: AppState): WalletState { WalletData( currencyData = BalanceWidgetData(BalanceStatus.EmptyCard), mainButton = WalletMainButton.CreateWalletButton(creatingWalletAllowed), - topUpState = TopUpState(false) ) ) ) @@ -78,7 +77,6 @@ private fun internalReduce(action: Action, state: AppState): WalletState { wallets = listOf( WalletData( currencyData = BalanceWidgetData(BalanceStatus.UnknownBlockchain), - topUpState = TopUpState(false) ) ) ) @@ -95,6 +93,7 @@ private fun internalReduce(action: Action, state: AppState): WalletState { is WalletAction.LoadWallet -> { if (action.blockchain == null) { val wallets = newState.wallets.map { wallet -> + wallet.copy( currencyData = wallet.currencyData.copy( status = BalanceStatus.Loading, @@ -102,15 +101,22 @@ private fun internalReduce(action: Action, state: AppState): WalletState { currencySymbol = wallet.currencyData.currencySymbol, ), mainButton = WalletMainButton.SendButton(false), - topUpState = TopUpState( - allowed = action.allowTopUp - ?: wallet.topUpState.allowed + tradeCryptoState = TradeCryptoState( + sellingAllowed = action.allowToSell ?: wallet.tradeCryptoState.sellingAllowed && + state.walletState.tradeCryptoAllowed.availableToSell.contains(wallet.currencyData.currencySymbol), + buyingAllowed = action.allowToBuy + ?: wallet.tradeCryptoState.buyingAllowed ) ) } + val tradeCryptoAllowed = TradeCryptoAvailability( + sellingAllowed = action.allowToSell ?: false, + buyingAllowed = action.allowToBuy ?: false + ) newState = newState.copy( state = ProgressState.Loading, - wallets = wallets + wallets = wallets, + tradeCryptoAllowed = tradeCryptoAllowed ) } else { val walletManager = newState.getWalletManager(action.blockchain) ?: return newState @@ -126,8 +132,11 @@ private fun internalReduce(action: Action, state: AppState): WalletState { currencySymbol = wallet.currencyData.currencySymbol, ), mainButton = WalletMainButton.SendButton(false), - topUpState = TopUpState( - allowed = action.allowTopUp ?: wallet.topUpState.allowed + tradeCryptoState = TradeCryptoState( + sellingAllowed = action.allowToSell ?: wallet.tradeCryptoState.sellingAllowed && + state.walletState.tradeCryptoAllowed.availableToSell.contains(wallet.currencyData.currencySymbol), + buyingAllowed = action.allowToBuy + ?: wallet.tradeCryptoState.buyingAllowed ) ) } @@ -172,7 +181,6 @@ private fun internalReduce(action: Action, state: AppState): WalletState { status = BalanceStatus.Unreachable, errorMessage = message ), - topUpState = TopUpState(false) ) val tokenWallets = action.wallet.getTokens() .mapNotNull { newState.getWalletData(it) } @@ -242,6 +250,9 @@ private fun internalReduce(action: Action, state: AppState): WalletState { is WalletAction.ShowDialog.SignedHashesMultiWalletDialog -> { newState = newState.copy(walletDialog = WalletDialog.SignedHashesMultiWalletDialog) } + is WalletAction.ShowDialog.ChooseTradeActionDialog -> { + newState = newState.copy(walletDialog = WalletDialog.ChooseTradeActionDialog) + } is WalletAction.HideDialog -> { newState = newState.copy(walletDialog = null) } @@ -251,7 +262,7 @@ private fun internalReduce(action: Action, state: AppState): WalletState { ) } is WalletAction.Send.Cancel -> newState = newState.copy(walletDialog = null) - is WalletAction.TopUpAction -> return newState + is WalletAction.TradeCryptoAction -> return newState is WalletAction.ChangeSelectedAddress -> { val selectedWalletData = newState.getWalletData(newState.selectedWallet) 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 63a7a78812..c2ff298244 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 @@ -86,11 +86,9 @@ class WalletDetailsFragment : Fragment(R.layout.fragment_wallet_details), StoreS btn_share.setOnClickListener { store.dispatch(WalletAction.ShowDialog.QrCode) } - btn_top_up.setOnClickListener { - store.dispatch( - WalletAction.TopUpAction.TopUp(requireContext(), R.color.backgroundLightGray) - ) - } + btn_top_up.setOnClickListener { store.dispatch(WalletAction.TradeCryptoAction.Buy) } + + btn_sell.setOnClickListener { store.dispatch(WalletAction.TradeCryptoAction.Sell) } } override fun newState(state: WalletState) { @@ -131,8 +129,9 @@ class WalletDetailsFragment : Fragment(R.layout.fragment_wallet_details), StoreS srl_wallet_details.setOnRefreshListener { if (selectedWallet.currencyData.status != BalanceStatus.Loading) { store.dispatch(WalletAction.LoadWallet( - selectedWallet.topUpState.allowed, - selectedWallet.currency?.blockchain + allowToBuy = selectedWallet.tradeCryptoState.buyingAllowed, + allowToSell = selectedWallet.tradeCryptoState.sellingAllowed, + blockchain = selectedWallet.currency?.blockchain )) } } @@ -140,6 +139,9 @@ class WalletDetailsFragment : Fragment(R.layout.fragment_wallet_details), StoreS if (selectedWallet.currencyData.status != BalanceStatus.Loading) { srl_wallet_details.isRefreshing = false } + + btn_top_up.isEnabled = selectedWallet.tradeCryptoState.buyingAllowed + btn_sell.show(selectedWallet.tradeCryptoState.sellingAllowed) } private fun handleCurrencyIcon(wallet: WalletData) { diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/dialogs/ChooseTradeActionDialog.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/dialogs/ChooseTradeActionDialog.kt new file mode 100644 index 0000000000..95ff1f65ea --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/dialogs/ChooseTradeActionDialog.kt @@ -0,0 +1,32 @@ +package com.tangem.tap.features.wallet.ui.dialogs + +import android.content.Context +import com.google.android.material.bottomsheet.BottomSheetDialog +import com.tangem.tap.features.wallet.redux.WalletAction +import com.tangem.tap.store +import com.tangem.wallet.R +import kotlinx.android.synthetic.main.dialog_wallet_trade.* + +class ChooseTradeActionDialog(context: Context) : BottomSheetDialog(context) { + + init { + this.setContentView(R.layout.dialog_wallet_trade) + } + + override fun show() { + super.show() + + this.setOnDismissListener { + store.dispatch(WalletAction.HideDialog) + } + + dialog_btn_buy.setOnClickListener { + store.dispatch(WalletAction.TradeCryptoAction.Buy) + dismiss() + } + dialog_btn_sell.setOnClickListener { + store.dispatch(WalletAction.TradeCryptoAction.Sell) + dismiss() + } + } +} \ No newline at end of file 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 ce0623ac86..14883f5233 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 @@ -16,10 +16,7 @@ import com.tangem.tap.features.wallet.ui.BalanceWidget import com.tangem.tap.features.wallet.ui.MultipleAddressUiHelper import com.tangem.tap.features.wallet.ui.WalletFragment import com.tangem.tap.features.wallet.ui.adapters.PendingTransactionsAdapter -import com.tangem.tap.features.wallet.ui.dialogs.AmountToSendDialog -import com.tangem.tap.features.wallet.ui.dialogs.QrDialog -import com.tangem.tap.features.wallet.ui.dialogs.ScanFailsDialog -import com.tangem.tap.features.wallet.ui.dialogs.SignedHashesWarningDialog +import com.tangem.tap.features.wallet.ui.dialogs.* import com.tangem.tap.store import com.tangem.wallet.R import kotlinx.android.synthetic.main.card_balance.* @@ -124,12 +121,17 @@ class SingleWalletView : WalletView { setupButtonsType(state, fragment) - val btnConfirm = if (state.topUpState.allowed) { + val btnConfirm = if (state.tradeCryptoState.sellingAllowed || + state.tradeCryptoState.buyingAllowed + ) { btn_confirm_short } else { btn_confirm_long } - val btnScan = if (state.topUpState.allowed) { + + val btnScan = if (state.tradeCryptoState.sellingAllowed || + state.tradeCryptoState.buyingAllowed + ) { btn_scan_short } else { btn_scan_long @@ -149,14 +151,26 @@ class SingleWalletView : WalletView { btn_show_qr.setOnClickListener { store.dispatch(WalletAction.ShowDialog.QrCode) } btn_top_up.setOnClickListener { - store.dispatch( - WalletAction.TopUpAction.TopUp(fragment.requireContext(), R.color.backgroundLightGray) - ) + tradeCryptoAction(state.tradeCryptoState) } } + private fun tradeCryptoAction(tradeCryptoState: TradeCryptoState) { + val allowedToBuy = tradeCryptoState.buyingAllowed + val allowedToSell = tradeCryptoState.sellingAllowed + val action = when { + allowedToBuy && !allowedToSell -> WalletAction.TradeCryptoAction.Buy + !allowedToBuy && allowedToSell -> WalletAction.TradeCryptoAction.Sell + allowedToBuy && allowedToSell -> WalletAction.ShowDialog.ChooseTradeActionDialog + else -> null + } + if (action != null) store.dispatch(action) + } + private fun setupButtonsType(state: WalletData, fragment: WalletFragment) = with(fragment) { - if (state.topUpState.allowed) { + if (state.tradeCryptoState.sellingAllowed || + state.tradeCryptoState.buyingAllowed + ) { l_buttons_long.hide() l_buttons_short.show() } else { @@ -244,6 +258,9 @@ class SingleWalletView : WalletView { dialog = SignedHashesWarningDialog.create(context).apply { show() } } } + is WalletDialog.ChooseTradeActionDialog -> { + if (dialog == null) dialog = ChooseTradeActionDialog(context).apply { show() } + } null -> { dialog?.dismiss() dialog = null diff --git a/app/src/main/java/com/tangem/tap/network/moonpay/MoonpayApi.kt b/app/src/main/java/com/tangem/tap/network/moonpay/MoonpayApi.kt new file mode 100644 index 0000000000..f3e062abf3 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/network/moonpay/MoonpayApi.kt @@ -0,0 +1,16 @@ +package com.tangem.tap.network.moonpay + +import retrofit2.http.GET +import retrofit2.http.Query + +interface MoonpayApi { + @GET( MOOONPAY_IP_ADDRESS_REQUEST_URL) + suspend fun getUserStatus( + @Query("apiKey") moonpayApiKey: String + ): MoonPayUserStatus + + companion object { + const val MOOONPAY_BASE_URL = "https://api.moonpay.com/v4/" + const val MOOONPAY_IP_ADDRESS_REQUEST_URL = "ip_address/" + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/network/moonpay/MoonpayService.kt b/app/src/main/java/com/tangem/tap/network/moonpay/MoonpayService.kt new file mode 100644 index 0000000000..c20d22863e --- /dev/null +++ b/app/src/main/java/com/tangem/tap/network/moonpay/MoonpayService.kt @@ -0,0 +1,24 @@ +package com.tangem.tap.network.moonpay + +import com.squareup.moshi.JsonClass +import com.tangem.commands.common.network.Result +import com.tangem.commands.common.network.performRequest +import com.tangem.tap.network.createRetrofitInstance + +class MoonpayService { + + private val moonpayApi: MoonpayApi by lazy { + createRetrofitInstance(MoonpayApi.MOOONPAY_BASE_URL) + .create(MoonpayApi::class.java) + } + + suspend fun getUserStatus(moonpayApiKey: String): Result { + return performRequest { moonpayApi.getUserStatus(moonpayApiKey) } + } +} + +@JsonClass(generateAdapter = true) +data class MoonPayUserStatus( + val isBuyAllowed: Boolean, + val isSellAllowed: Boolean +) \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_arrows_up_down_short_btn.xml b/app/src/main/res/drawable/ic_arrows_up_down_short_btn.xml new file mode 100644 index 0000000000..51b285c947 --- /dev/null +++ b/app/src/main/res/drawable/ic_arrows_up_down_short_btn.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_wallet_trade.xml b/app/src/main/res/layout/dialog_wallet_trade.xml new file mode 100644 index 0000000000..062d0286d1 --- /dev/null +++ b/app/src/main/res/layout/dialog_wallet_trade.xml @@ -0,0 +1,47 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_topup.xml b/app/src/main/res/layout/fragment_topup.xml index 8069c307f0..01182c0c4c 100644 --- a/app/src/main/res/layout/fragment_topup.xml +++ b/app/src/main/res/layout/fragment_topup.xml @@ -11,7 +11,7 @@ android:layout_height="?attr/actionBarSize" app:layout_constraintTop_toTopOf="parent" app:navigationIcon="@drawable/ic_close" - app:title="@string/wallet_button_topup" /> + app:title="@string/wallet_button_buy" /> diff --git a/app/src/main/res/layout/fragment_wallet_details.xml b/app/src/main/res/layout/fragment_wallet_details.xml index 8270cd53ac..7b61f56d8d 100644 --- a/app/src/main/res/layout/fragment_wallet_details.xml +++ b/app/src/main/res/layout/fragment_wallet_details.xml @@ -83,12 +83,12 @@ android:id="@+id/rv_pending_transaction" android:layout_width="match_parent" android:layout_height="wrap_content" + android:layout_marginTop="16dp" android:layout_marginBottom="8dp" android:nestedScrollingEnabled="false" android:overScrollMode="never" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" - android:layout_marginTop="16dp" app:layout_constraintTop_toBottomOf="@id/barrier_currency_title" /> + android:textColor="@android:color/white" + android:textSize="14sp" /> - + + + + + app:layout_constraintHorizontal_chainStyle="packed" + app:layout_constraintStart_toEndOf="@id/btn_top_up" + app:layout_constraintEnd_toStartOf="@id/btn_confirm" + app:layout_constraintTop_toBottomOf="@id/barrier" + app:layout_constraintVertical_bias="1" /> diff --git a/app/src/main/res/layout/layout_send_address_payid.xml b/app/src/main/res/layout/layout_send_address_payid.xml index 00a5617a9d..2b0f046335 100644 --- a/app/src/main/res/layout/layout_send_address_payid.xml +++ b/app/src/main/res/layout/layout_send_address_payid.xml @@ -32,6 +32,7 @@ android:id="@+id/etAddressOrPayId" android:layout_width="match_parent" android:layout_height="wrap_content" + android:background="@color/backgroundLightGray" android:ellipsize="middle" android:inputType="textNoSuggestions" android:paddingStart="0dp" @@ -150,6 +151,7 @@ android:id="@+id/etMemo" android:layout_width="match_parent" android:layout_height="wrap_content" + android:background="@color/backgroundLightGray" android:ellipsize="middle" android:paddingStart="0dp" android:paddingEnd="0dp" @@ -180,6 +182,7 @@ android:id="@+id/etDestinationTag" android:layout_width="match_parent" android:layout_height="wrap_content" + android:background="@color/backgroundLightGray" android:ellipsize="end" android:inputType="number" android:paddingStart="0dp" diff --git a/app/src/main/res/layout/layout_send_amount.xml b/app/src/main/res/layout/layout_send_amount.xml index e2b5441fa2..b2e8700482 100644 --- a/app/src/main/res/layout/layout_send_amount.xml +++ b/app/src/main/res/layout/layout_send_amount.xml @@ -51,6 +51,7 @@ android:id="@+id/etAmountToSend" android:layout_width="match_parent" android:layout_height="82dp" + android:background="@color/backgroundLightGray" android:fontFamily="sans-serif-light" android:inputType="numberDecimal" android:paddingStart="0dp" diff --git a/app/src/main/res/layout/layout_wallet_short_buttons.xml b/app/src/main/res/layout/layout_wallet_short_buttons.xml index 072306c7b7..be494ee95b 100644 --- a/app/src/main/res/layout/layout_wallet_short_buttons.xml +++ b/app/src/main/res/layout/layout_wallet_short_buttons.xml @@ -28,10 +28,10 @@ android:layout_marginStart="8dp" android:layout_marginTop="30dp" android:layout_marginBottom="33dp" - android:drawableTop="@drawable/ic_arrow_up_short_btn" + android:drawableTop="@drawable/ic_arrows_up_down_short_btn" android:gravity="bottom|center_horizontal" android:paddingTop="1dp" - android:text="@string/wallet_button_topup" + android:text="@string/wallet_button_trade" app:drawableTint="@android:color/white" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toStartOf="@+id/btn_confirm_short" diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index de5579c26b..509d2b72be 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -5,6 +5,8 @@ #0022D4 #1ACE80 + @color/backgroundLightGray + @color/accent #661ACE80 @color/darkGray6 diff --git a/app/src/main/res/values/strings_untranslated.xml b/app/src/main/res/values/strings_untranslated.xml index a4a2aecc25..08784d1bf0 100644 --- a/app/src/main/res/values/strings_untranslated.xml +++ b/app/src/main/res/values/strings_untranslated.xml @@ -1,5 +1,7 @@ - Top Up + Buy + Sell + Trade Default Compatibility Legacy @@ -160,4 +162,7 @@ this wallet. Failed to establish WalletConnect session: timeout error. Please, try again later. This is a Testnet card. Don\'t accept it as a payment. This card must only be used for testing and development purposes. + + + Do you want to buy or sell crypto?