diff --git a/app/src/main/assets/features_dev.json b/app/src/main/assets/features_dev.json index 82e46b21bf..b24d083053 100644 --- a/app/src/main/assets/features_dev.json +++ b/app/src/main/assets/features_dev.json @@ -1,6 +1,4 @@ { - "isWalletPayIdEnabled": true, - "isSendingToPayIdEnabled": true, "isTopUpEnabled": true, "isCreatingTwinCardsAllowed": true } \ No newline at end of file diff --git a/app/src/main/assets/features_prod.json b/app/src/main/assets/features_prod.json index d3b1375fae..b24d083053 100644 --- a/app/src/main/assets/features_prod.json +++ b/app/src/main/assets/features_prod.json @@ -1,6 +1,4 @@ { - "isWalletPayIdEnabled": false, - "isSendingToPayIdEnabled": true, "isTopUpEnabled": true, "isCreatingTwinCardsAllowed": true } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/analytics/events/Basic.kt b/app/src/main/java/com/tangem/tap/common/analytics/events/Basic.kt index f4ef3e133f..e5b89b30e5 100644 --- a/app/src/main/java/com/tangem/tap/common/analytics/events/Basic.kt +++ b/app/src/main/java/com/tangem/tap/common/analytics/events/Basic.kt @@ -31,12 +31,14 @@ sealed class Basic( currency: AnalyticsParam.CardCurrency, batch: String, signInType: SignInType, + walletsCount: String, ) : Basic( event = "Signed in", params = mapOf( AnalyticsParam.CURRENCY to currency.value, AnalyticsParam.BATCH to batch, "Sign in type" to signInType.name, + "Wallets Count" to walletsCount, ), ) { enum class SignInType { diff --git a/app/src/main/java/com/tangem/tap/common/extensions/Blockchain.kt b/app/src/main/java/com/tangem/tap/common/extensions/Blockchain.kt index 5b0135f4a7..7ca3e575d4 100644 --- a/app/src/main/java/com/tangem/tap/common/extensions/Blockchain.kt +++ b/app/src/main/java/com/tangem/tap/common/extensions/Blockchain.kt @@ -10,7 +10,6 @@ import com.tangem.wallet.R fun Blockchain.getGreyedOutIconRes(): Int { return when (this) { Blockchain.Arbitrum, Blockchain.ArbitrumTestnet -> R.drawable.ic_arbitrum_no_color -// Blockchain.Ducatus -> R.drawable.ic_ducatus Blockchain.Bitcoin, Blockchain.BitcoinTestnet -> R.drawable.ic_bitcoin_no_color Blockchain.BitcoinCash -> R.drawable.ic_bitcoin_cash_no_color Blockchain.Litecoin -> R.drawable.ic_litecoin_no_color 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 c4becfb45b..91c1ae14b7 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 @@ -6,7 +6,6 @@ import com.tangem.tap.common.analytics.topup.TopUpController import com.tangem.tap.common.entities.FiatCurrency import com.tangem.tap.common.feedback.FeedbackManager import com.tangem.tap.common.redux.StateDialog -import com.tangem.tap.domain.PayIdManager import com.tangem.tap.domain.TapWalletManager import com.tangem.tap.domain.configurable.warningMessage.WarningMessagesManager import com.tangem.tap.domain.userWalletList.UserWalletsListManager @@ -20,7 +19,6 @@ data class GlobalState( val onboardingState: OnboardingState = OnboardingState(), val cardVerifiedOnline: Boolean = false, val tapWalletManager: TapWalletManager = TapWalletManager(), - val payIdManager: PayIdManager = PayIdManager(), val configManager: ConfigManager? = null, val warningManager: WarningMessagesManager? = null, val feedbackManager: FeedbackManager? = null, diff --git a/app/src/main/java/com/tangem/tap/domain/PayIdManager.kt b/app/src/main/java/com/tangem/tap/domain/PayIdManager.kt deleted file mode 100644 index 16565ff5a6..0000000000 --- a/app/src/main/java/com/tangem/tap/domain/PayIdManager.kt +++ /dev/null @@ -1,57 +0,0 @@ -package com.tangem.tap.domain - -import com.tangem.blockchain.common.Blockchain -import com.tangem.common.services.Result -import com.tangem.tap.network.payid.PayIdVerifyService -import com.tangem.tap.network.payid.VerifyPayIdResponse -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.withContext -import java.util.* - -class PayIdManager { - - @Suppress("MagicNumber") - suspend fun verifyPayId(payId: String, blockchain: Blockchain): Result = - withContext(Dispatchers.IO) { - val splitPayId = payId.split("\$") - val user = splitPayId[0] - val baseUrl = "https://${splitPayId[1]}/" - return@withContext PayIdVerifyService(baseUrl).verifyAddress(user, blockchain.getPayIdNetwork()) - } - - private fun Blockchain.getPayIdNetwork(): String { - return when (this) { - Blockchain.XRP -> "XRPL" - Blockchain.RSK -> "RSK" - else -> this.currency - }.lowercase(Locale.getDefault()) - } - - companion object { - private val payIdRegExp = ( - "^[a-z0-9!#@%&*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#@%&*+/=?^_`{|}~-]+)*\\\$(?:(?:[a-z0-9]" + - "(?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z-]*[a-z0-9])?|(?:[0-9]{1,3}\\.){3}[0-9]{1,3})\$" - ).toRegex() - - val payIdSupported: EnumSet = EnumSet.of( - Blockchain.XRP, - Blockchain.Ethereum, - Blockchain.Bitcoin, - Blockchain.Litecoin, - Blockchain.Stellar, - Blockchain.Cardano, - Blockchain.CardanoShelley, - Blockchain.Ducatus, - Blockchain.BitcoinCash, - Blockchain.Binance, - Blockchain.RSK, - Blockchain.Tezos, - ) - - fun isPayId(value: String?): Boolean = value?.contains(payIdRegExp) ?: false - } -} - -fun Blockchain.isPayIdSupported(): Boolean { - return PayIdManager.payIdSupported.contains(this) -} \ 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 f9be91dcce..4e39b73bbe 100644 --- a/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt @@ -1,6 +1,9 @@ package com.tangem.tap.domain -import com.tangem.blockchain.common.* +import com.tangem.blockchain.common.BlockchainSdkConfig +import com.tangem.blockchain.common.Token +import com.tangem.blockchain.common.Wallet +import com.tangem.blockchain.common.WalletManagerFactory import com.tangem.common.doOnFailure import com.tangem.common.doOnSuccess import com.tangem.core.analytics.Analytics @@ -134,22 +137,13 @@ class TapWalletManager( fun updateConfigManager(data: ScanResponse) { val configManager = store.state.globalState.configManager - val blockchain = data.cardTypesResolver.getBlockchain() + if (data.cardTypesResolver.isStart2Coin()) { - configManager?.turnOff(ConfigManager.IS_SENDING_TO_PAY_ID_ENABLED) configManager?.turnOff(ConfigManager.IS_TOP_UP_ENABLED) - } else if (blockchain == Blockchain.Bitcoin || - data.walletData?.blockchain == Blockchain.Bitcoin.id - ) { - configManager?.resetToDefault(ConfigManager.IS_SENDING_TO_PAY_ID_ENABLED) - configManager?.resetToDefault(ConfigManager.IS_TOP_UP_ENABLED) } else { - configManager?.resetToDefault(ConfigManager.IS_SENDING_TO_PAY_ID_ENABLED) configManager?.resetToDefault(ConfigManager.IS_TOP_UP_ENABLED) } } } -fun Wallet.getFirstToken(): Token? { - return getTokens().toList().getOrNull(0) -} \ No newline at end of file +fun Wallet.getFirstToken(): Token? = getTokens().toList().getOrNull(index = 0) \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/userWalletList/UserWalletsListManager.kt b/app/src/main/java/com/tangem/tap/domain/userWalletList/UserWalletsListManager.kt index b1c97b594b..249ac0d0f4 100644 --- a/app/src/main/java/com/tangem/tap/domain/userWalletList/UserWalletsListManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/userWalletList/UserWalletsListManager.kt @@ -26,6 +26,11 @@ interface UserWalletsListManager { * */ val hasUserWallets: Boolean + /** + * Count of saved user wallets + */ + val walletsCount: Int + /** * Set [UserWallet] with provided [UserWalletId] as selected * diff --git a/app/src/main/java/com/tangem/tap/domain/userWalletList/implementation/BiometricUserWalletsListManager.kt b/app/src/main/java/com/tangem/tap/domain/userWalletList/implementation/BiometricUserWalletsListManager.kt index 4ea180e270..568d16fc28 100644 --- a/app/src/main/java/com/tangem/tap/domain/userWalletList/implementation/BiometricUserWalletsListManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/userWalletList/implementation/BiometricUserWalletsListManager.kt @@ -53,6 +53,9 @@ internal class BiometricUserWalletsListManager( override val hasUserWallets: Boolean get() = keysRepository.hasSavedEncryptionKeys() + override val walletsCount: Int + get() = state.value.userWallets.size + override suspend fun unlock(): CompletionResult { return unlockWithBiometryInternal() .mapFailure { error -> diff --git a/app/src/main/java/com/tangem/tap/domain/userWalletList/implementation/RuntimeUserWalletsListManager.kt b/app/src/main/java/com/tangem/tap/domain/userWalletList/implementation/RuntimeUserWalletsListManager.kt index c94958d46f..cfcd68bc57 100644 --- a/app/src/main/java/com/tangem/tap/domain/userWalletList/implementation/RuntimeUserWalletsListManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/userWalletList/implementation/RuntimeUserWalletsListManager.kt @@ -7,13 +7,7 @@ import com.tangem.tap.domain.model.UserWallet import com.tangem.tap.domain.userWalletList.UserWalletsListError import com.tangem.tap.domain.userWalletList.UserWalletsListManager import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.distinctUntilChanged -import kotlinx.coroutines.flow.filterNotNull -import kotlinx.coroutines.flow.mapLatest -import kotlinx.coroutines.flow.update -import kotlinx.coroutines.flow.updateAndGet +import kotlinx.coroutines.flow.* @OptIn(ExperimentalCoroutinesApi::class) internal class RuntimeUserWalletsListManager : UserWalletsListManager { @@ -36,6 +30,12 @@ internal class RuntimeUserWalletsListManager : UserWalletsListManager { override val hasUserWallets: Boolean get() = state.value.userWallet != null + /** + * only 1 wallet stored in runtime implementation + */ + override val walletsCount: Int + get() = 1 + override suspend fun select(userWalletId: UserWalletId): CompletionResult = catching { state.value.userWallet ?.takeIf { it.walletId == userWalletId } diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/SendScreenAction.kt b/app/src/main/java/com/tangem/tap/features/send/redux/SendScreenAction.kt index 20c206b37d..5f92c3c10b 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/SendScreenAction.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/SendScreenAction.kt @@ -6,7 +6,6 @@ import com.tangem.blockchain.common.Blockchain import com.tangem.blockchain.common.WalletManager import com.tangem.common.core.TangemSdkError import com.tangem.tap.common.analytics.events.Token.Send.AddressEntered -import com.tangem.tap.common.redux.ErrorAction import com.tangem.tap.common.redux.StateDialog import com.tangem.tap.common.redux.ToastNotificationAction import com.tangem.tap.domain.TapError @@ -34,15 +33,14 @@ data class PrepareSendScreen( val tokenRate: BigDecimal? = null, ) : SendScreenAction -// Address or PayId -sealed class AddressPayIdActionUi : SendScreenActionUi { - data class HandleUserInput(val data: String) : AddressPayIdActionUi() - data class PasteAddressPayId(val data: String, val sourceType: AddressEntered.SourceType) : AddressPayIdActionUi() - data class CheckClipboard(val data: String?) : AddressPayIdActionUi() - data class CheckAddressPayId(val sourceType: AddressEntered.SourceType?) : AddressPayIdActionUi() - data class SetTruncateHandler(val handler: (String) -> String) : AddressPayIdActionUi() - data class TruncateOrRestore(val truncate: Boolean) : AddressPayIdActionUi() - data class ChangePayIdState(val sendingToPayIdEnabled: Boolean) : AddressPayIdActionUi() +// Address +sealed class AddressActionUi : SendScreenActionUi { + data class HandleUserInput(val data: String) : AddressActionUi() + data class PasteAddress(val data: String, val sourceType: AddressEntered.SourceType) : AddressActionUi() + data class CheckClipboard(val data: String?) : AddressActionUi() + data class CheckAddress(val sourceType: AddressEntered.SourceType?) : AddressActionUi() + data class SetTruncateHandler(val handler: (String) -> String) : AddressActionUi() + data class TruncateOrRestore(val truncate: Boolean) : AddressActionUi() } sealed class TransactionExtrasAction : SendScreenActionUi { @@ -76,27 +74,15 @@ sealed class TransactionExtrasAction : SendScreenActionUi { } } -sealed class AddressPayIdVerifyAction : SendScreenAction { +sealed class AddressVerifyAction : SendScreenAction { enum class Error { - PAY_ID_UNSUPPORTED_BY_BLOCKCHAIN, - PAY_ID_NOT_REGISTERED, - PAY_ID_REQUEST_FAILED, ADDRESS_INVALID_OR_UNSUPPORTED_BY_BLOCKCHAIN, ADDRESS_SAME_AS_WALLET, } - data class ChangePasteBtnEnableState(val isEnabled: Boolean) : AddressPayIdVerifyAction() + data class ChangePasteBtnEnableState(val isEnabled: Boolean) : AddressVerifyAction() - sealed class PayIdVerification : AddressPayIdVerifyAction() { - data class SetPayIdError(val error: Error?) : PayIdVerification() - data class SetPayIdWalletAddress( - val payId: String, - val payIdWalletAddress: String, - val isUserInput: Boolean, - ) : PayIdVerification() - } - - sealed class AddressVerification : AddressPayIdVerifyAction() { + sealed class AddressVerification : AddressVerifyAction() { data class SetAddressError(val error: Error?) : AddressVerification() data class SetWalletAddress(val address: String, val isUserInput: Boolean) : AddressVerification() } @@ -155,8 +141,6 @@ sealed class SendAction : SendScreenAction { override val messageResource: Int = R.string.send_transaction_success } - data class SendError(override val error: TapError) : SendAction(), ErrorAction - sealed class Dialog : SendAction(), StateDialog { data class TezosWarningDialog( val reduceCallback: () -> Unit, diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/AddressPayIdMiddleware.kt b/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/AddressMiddleware.kt similarity index 50% rename from app/src/main/java/com/tangem/tap/features/send/redux/middlewares/AddressPayIdMiddleware.kt rename to app/src/main/java/com/tangem/tap/features/send/redux/middlewares/AddressMiddleware.kt index 406eacd087..576497222e 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/AddressPayIdMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/AddressMiddleware.kt @@ -2,49 +2,39 @@ package com.tangem.tap.features.send.redux.middlewares import com.tangem.blockchain.common.Blockchain import com.tangem.blockchain.common.Wallet -import com.tangem.common.services.Result import com.tangem.core.analytics.Analytics import com.tangem.tap.common.analytics.events.Token.Send.AddressEntered import com.tangem.tap.common.redux.AppState -import com.tangem.tap.domain.PayIdManager -import com.tangem.tap.domain.isPayIdSupported import com.tangem.tap.features.send.redux.* -import com.tangem.tap.features.send.redux.AddressPayIdVerifyAction.AddressVerification.SetAddressError -import com.tangem.tap.features.send.redux.AddressPayIdVerifyAction.AddressVerification.SetWalletAddress -import com.tangem.tap.features.send.redux.AddressPayIdVerifyAction.Error -import com.tangem.tap.features.send.redux.AddressPayIdVerifyAction.PayIdVerification.SetPayIdError -import com.tangem.tap.features.send.redux.AddressPayIdVerifyAction.PayIdVerification.SetPayIdWalletAddress -import com.tangem.tap.scope -import com.tangem.tap.store -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext +import com.tangem.tap.features.send.redux.AddressVerifyAction.AddressVerification.SetAddressError +import com.tangem.tap.features.send.redux.AddressVerifyAction.AddressVerification.SetWalletAddress +import com.tangem.tap.features.send.redux.AddressVerifyAction.Error import org.rekotlin.Action import org.rekotlin.DispatchFunction /** [REDACTED_AUTHOR] */ -internal class AddressPayIdMiddleware { +internal class AddressMiddleware { - fun handle(action: AddressPayIdActionUi, appState: AppState?, dispatch: (Action) -> Unit) { + fun handle(action: AddressActionUi, appState: AppState?, dispatch: (Action) -> Unit) { when (action) { - is AddressPayIdActionUi.HandleUserInput -> handleUserInput(action.data, appState, dispatch) - is AddressPayIdActionUi.PasteAddressPayId -> pasteAddressPayId(action.data, action.sourceType, dispatch) - is AddressPayIdActionUi.CheckClipboard -> verifyClipboard(action.data, appState, dispatch) - is AddressPayIdActionUi.CheckAddressPayId -> verifyAddressPayId(action.sourceType, appState, dispatch) + is AddressActionUi.HandleUserInput -> handleUserInput(action.data, appState, dispatch) + is AddressActionUi.PasteAddress -> pasteAddress(action.data, action.sourceType, dispatch) + is AddressActionUi.CheckClipboard -> verifyClipboard(action.data, appState, dispatch) + is AddressActionUi.CheckAddress -> verifyAddress(action.sourceType, appState, dispatch) else -> return } } private fun handleUserInput(input: String, appState: AppState?, dispatch: DispatchFunction) { val sendState = appState?.sendState ?: return - if (input == sendState.addressPayIdState.viewFieldValue.value) return + if (input == sendState.addressState.viewFieldValue.value) return setAddressAndCheck(data = input, sourceType = null, isUserInput = true, dispatch = dispatch) } - private fun pasteAddressPayId(data: String, sourceType: AddressEntered.SourceType, dispatch: (Action) -> Unit) { + private fun pasteAddress(data: String, sourceType: AddressEntered.SourceType, dispatch: (Action) -> Unit) { setAddressAndCheck(data = data, sourceType = sourceType, isUserInput = false, dispatch = dispatch) } @@ -54,74 +44,27 @@ internal class AddressPayIdMiddleware { isUserInput: Boolean, dispatch: (Action) -> Unit, ) { - val potentialPayId = data.lowercase() - if (isPayIdEnabled() && PayIdManager.isPayId(potentialPayId)) { - dispatch(SetPayIdWalletAddress(potentialPayId, "", isUserInput)) - } else { - dispatch(SetWalletAddress(data, isUserInput)) - } - dispatch(AddressPayIdActionUi.CheckAddressPayId(sourceType)) + dispatch(SetWalletAddress(data, isUserInput)) + dispatch(AddressActionUi.CheckAddress(sourceType)) } - private fun verifyAddressPayId( + private fun verifyAddress( sourceType: AddressEntered.SourceType?, appState: AppState?, dispatch: (Action) -> Unit, ) { val sendState = appState?.sendState ?: return val wallet = sendState.walletManager?.wallet ?: return - val addressPayId = sendState.addressPayIdState.normalFieldValue ?: return - val isUserInput = sendState.addressPayIdState.viewFieldValue.isFromUserInput + val address = sendState.addressState.normalFieldValue ?: return + val isUserInput = sendState.addressState.viewFieldValue.isFromUserInput - if (isPayIdEnabled() && PayIdManager.isPayId(addressPayId)) { - verifyPayId(addressPayId, wallet, isUserInput, dispatch) - } else { - verifyAddress( - address = addressPayId, - wallet = wallet, - isUserInput = isUserInput, - dispatch = dispatch, - sourceType = sourceType, - ) - } - } - - private fun verifyPayId(payId: String, wallet: Wallet, isUserInput: Boolean, dispatch: DispatchFunction) { - val blockchain = wallet.blockchain - if (!blockchain.isPayIdSupported()) { - dispatch(SetPayIdError(Error.PAY_ID_UNSUPPORTED_BY_BLOCKCHAIN)) - return - } - - scope.launch { - val result = PayIdManager().verifyPayId(payId, blockchain) - withContext(Dispatchers.Main) { - when (result) { - is Result.Success -> { - val addressDetails = result.data.getAddressDetails() - if (addressDetails == null) { - dispatch(SetPayIdError(Error.PAY_ID_NOT_REGISTERED)) - return@withContext - } - - val address = addressDetails.address - val failReason = isValidBlockchainAddressAndNotTheSameAsWallet(wallet, address) - if (failReason == null) { - dispatch(SetPayIdWalletAddress(payId, address, isUserInput)) - dispatch(TransactionExtrasAction.Prepare(wallet.blockchain, address, addressDetails.tag)) - dispatch(FeeAction.RequestFee) - } else { - dispatch(SetAddressError(failReason)) - dispatch(TransactionExtrasAction.Release) - } - } - is Result.Failure -> { - dispatch(SetPayIdError(Error.PAY_ID_REQUEST_FAILED)) - dispatch(TransactionExtrasAction.Release) - } - } - } - } + verifyAddress( + address = address, + wallet = wallet, + isUserInput = isUserInput, + dispatch = dispatch, + sourceType = sourceType, + ) } private fun verifyAddress( @@ -219,41 +162,33 @@ internal class AddressPayIdMiddleware { } private fun verifyClipboard(input: String?, appState: AppState?, dispatch: DispatchFunction) { - val addressPayId = input ?: return + val address = input ?: return val wallet = appState?.sendState?.walletManager?.wallet ?: return val internalDispatcher: (Action) -> Unit = { when (it) { - is SetWalletAddress, is SetPayIdWalletAddress -> { - dispatch(AddressPayIdVerifyAction.ChangePasteBtnEnableState(true)) + is SetWalletAddress -> { + dispatch(AddressVerifyAction.ChangePasteBtnEnableState(true)) } - is SetAddressError, is SetPayIdError -> { - dispatch(AddressPayIdVerifyAction.ChangePasteBtnEnableState(false)) + is SetAddressError -> { + dispatch(AddressVerifyAction.ChangePasteBtnEnableState(false)) } } } - if (PayIdManager.isPayId(addressPayId) && isPayIdEnabled()) { - verifyPayId(addressPayId, wallet, false, internalDispatcher) - } else { - verifyAddress( - address = addressPayId, - wallet = wallet, - sourceType = null, - isUserInput = false, - dispatch = internalDispatcher, - ) - } - } - - private fun isPayIdEnabled(): Boolean { - return store.state.globalState.configManager?.config?.isSendingToPayIdEnabled ?: false + verifyAddress( + address = address, + wallet = wallet, + sourceType = null, + isUserInput = false, + dispatch = internalDispatcher, + ) } } fun String.splitToMap(firstDelimiter: String, secondDelimiter: String): Map { - return this.split(firstDelimiter) + return this + .split(firstDelimiter) .map { it.split(secondDelimiter) } - .map { it.first() to it.last().toString() } - .toMap() + .associate { it.first() to it.last() } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/RequestFeeMiddleware.kt b/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/RequestFeeMiddleware.kt index 8a0b096c4c..3cbd54e044 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/RequestFeeMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/RequestFeeMiddleware.kt @@ -38,7 +38,7 @@ class RequestFeeMiddleware { } val typedAmount = sendState.amountState.amountToExtract ?: return - val destinationAddress = sendState.addressPayIdState.destinationWalletAddress!! + val destinationAddress = sendState.addressState.destinationWalletAddress!! val destinationAmount = Amount(typedAmount, sendState.amountState.amountToSendCrypto) val txSender = if (scanResponse.isDemoCard()) { DemoTransactionSender(walletManager) 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 d54adda3d5..5f8d039210 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 @@ -55,18 +55,17 @@ class SendMiddleware { { nextDispatch -> { action -> when (action) { - is AddressPayIdActionUi -> AddressPayIdMiddleware().handle(action, appState(), dispatch) + is AddressActionUi -> AddressMiddleware().handle(action, appState(), dispatch) is AmountActionUi -> AmountMiddleware().handle(action, appState(), dispatch) is RequestFee -> RequestFeeMiddleware().handle(appState(), dispatch) is SendActionUi.SendAmountToRecipient -> 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( + AddressVerifyAction.AddressVerification.SetWalletAddress( address = transactionData.destinationAddress, isUserInput = false, ), @@ -96,7 +95,7 @@ private fun verifyAndSendTransaction( val sendState = appState?.sendState ?: return val walletManager = sendState.walletManager ?: return val card = appState.globalState.scanResponse?.card ?: return - val destinationAddress = sendState.addressPayIdState.destinationWalletAddress ?: return + val destinationAddress = sendState.addressState.destinationWalletAddress ?: return val typedAmount = sendState.amountState.amountToExtract ?: return val feeAmount = sendState.feeState.currentFee ?: return @@ -391,12 +390,6 @@ fun createValidateTransactionError( return TapError.ValidateTransactionErrors(tapErrors) { it.joinToString("\r\n") } } -private fun setIfSendingToPayIdEnabled(appState: AppState?, dispatch: (Action) -> Unit) { - val isSendingToPayIdEnabled = - appState?.globalState?.configManager?.config?.isSendingToPayIdEnabled ?: false - dispatch(AddressPayIdActionUi.ChangePayIdState(isSendingToPayIdEnabled)) -} - private fun updateWarnings(dispatch: (Action) -> Unit) { val warningsManager = store.state.globalState.warningManager ?: return val blockchain = store.state.sendState.walletManager?.wallet?.blockchain ?: return diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/reducers/AddressPayIdReducer.kt b/app/src/main/java/com/tangem/tap/features/send/redux/reducers/AddressPayIdReducer.kt deleted file mode 100644 index ddacac0df8..0000000000 --- a/app/src/main/java/com/tangem/tap/features/send/redux/reducers/AddressPayIdReducer.kt +++ /dev/null @@ -1,72 +0,0 @@ -package com.tangem.tap.features.send.redux.reducers - -import com.tangem.tap.features.send.redux.AddressPayIdActionUi -import com.tangem.tap.features.send.redux.AddressPayIdVerifyAction -import com.tangem.tap.features.send.redux.AddressPayIdVerifyAction.AddressVerification -import com.tangem.tap.features.send.redux.AddressPayIdVerifyAction.PayIdVerification -import com.tangem.tap.features.send.redux.SendScreenAction -import com.tangem.tap.features.send.redux.states.AddressPayIdState -import com.tangem.tap.features.send.redux.states.InputViewValue -import com.tangem.tap.features.send.redux.states.SendState - -/** -[REDACTED_AUTHOR] - */ -class AddressPayIdReducer : SendInternalReducer { - override fun handle(action: SendScreenAction, sendState: SendState): SendState = when (action) { - is AddressPayIdActionUi -> handleUiAction(action, sendState, sendState.addressPayIdState) - is AddressPayIdVerifyAction -> handleAction(action, sendState, sendState.addressPayIdState) - else -> sendState - } - - private fun handleUiAction( - action: AddressPayIdActionUi, - sendState: SendState, - state: AddressPayIdState, - ): SendState { - val result = when (action) { - is AddressPayIdActionUi.HandleUserInput -> state - is AddressPayIdActionUi.SetTruncateHandler -> state.copy(truncateHandler = action.handler) - is AddressPayIdActionUi.TruncateOrRestore -> { - val value = if (action.truncate) state.truncatedFieldValue ?: "" else state.normalFieldValue ?: "" - state.copy(viewFieldValue = state.viewFieldValue.copy(value = value)) - } - is AddressPayIdActionUi.PasteAddressPayId -> return sendState - is AddressPayIdActionUi.CheckClipboard -> return sendState - is AddressPayIdActionUi.CheckAddressPayId -> return sendState - is AddressPayIdActionUi.ChangePayIdState -> state.copy(sendingToPayIdEnabled = action.sendingToPayIdEnabled) - } - return updateLastState(sendState.copy(addressPayIdState = result), result) - } - - private fun handleAction( - action: AddressPayIdVerifyAction, - sendState: SendState, - state: AddressPayIdState, - ): SendState { - val result = when (action) { - is PayIdVerification.SetPayIdWalletAddress -> { - state.copy( - viewFieldValue = InputViewValue(action.payId, action.isUserInput), - normalFieldValue = action.payId, - truncatedFieldValue = state.truncate(action.payId), - destinationWalletAddress = action.payIdWalletAddress, - error = null, - ) - } - is AddressVerification.SetWalletAddress -> { - state.copy( - viewFieldValue = InputViewValue(action.address, action.isUserInput), - normalFieldValue = action.address, - truncatedFieldValue = state.truncate(action.address), - destinationWalletAddress = action.address, - error = null, - ) - } - is AddressPayIdVerifyAction.ChangePasteBtnEnableState -> state.copy(pasteIsEnabled = action.isEnabled) - is AddressVerification.SetAddressError -> state.copy(error = action.error, destinationWalletAddress = null) - is PayIdVerification.SetPayIdError -> state.copy(error = action.error, destinationWalletAddress = null) - } - return updateLastState(sendState.copy(addressPayIdState = result), result) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/reducers/AddressReducer.kt b/app/src/main/java/com/tangem/tap/features/send/redux/reducers/AddressReducer.kt new file mode 100644 index 0000000000..a5b44bff0f --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/send/redux/reducers/AddressReducer.kt @@ -0,0 +1,52 @@ +package com.tangem.tap.features.send.redux.reducers + +import com.tangem.tap.features.send.redux.AddressActionUi +import com.tangem.tap.features.send.redux.AddressVerifyAction +import com.tangem.tap.features.send.redux.AddressVerifyAction.AddressVerification +import com.tangem.tap.features.send.redux.SendScreenAction +import com.tangem.tap.features.send.redux.states.AddressState +import com.tangem.tap.features.send.redux.states.InputViewValue +import com.tangem.tap.features.send.redux.states.SendState + +/** +[REDACTED_AUTHOR] + */ +class AddressReducer : SendInternalReducer { + override fun handle(action: SendScreenAction, sendState: SendState): SendState = when (action) { + is AddressActionUi -> handleUiAction(action, sendState, sendState.addressState) + is AddressVerifyAction -> handleAction(action, sendState, sendState.addressState) + else -> sendState + } + + private fun handleUiAction(action: AddressActionUi, sendState: SendState, state: AddressState): SendState { + val result = when (action) { + is AddressActionUi.HandleUserInput -> state + is AddressActionUi.SetTruncateHandler -> state.copy(truncateHandler = action.handler) + is AddressActionUi.TruncateOrRestore -> { + val value = if (action.truncate) state.truncatedFieldValue ?: "" else state.normalFieldValue ?: "" + state.copy(viewFieldValue = state.viewFieldValue.copy(value = value)) + } + is AddressActionUi.PasteAddress -> return sendState + is AddressActionUi.CheckClipboard -> return sendState + is AddressActionUi.CheckAddress -> return sendState + } + return updateLastState(sendState.copy(addressState = result), result) + } + + private fun handleAction(action: AddressVerifyAction, sendState: SendState, state: AddressState): SendState { + val result = when (action) { + is AddressVerification.SetWalletAddress -> { + state.copy( + viewFieldValue = InputViewValue(action.address, action.isUserInput), + normalFieldValue = action.address, + truncatedFieldValue = state.truncate(action.address), + destinationWalletAddress = action.address, + error = null, + ) + } + is AddressVerifyAction.ChangePasteBtnEnableState -> state.copy(pasteIsEnabled = action.isEnabled) + is AddressVerification.SetAddressError -> state.copy(error = action.error, destinationWalletAddress = null) + } + return updateLastState(sendState.copy(addressState = result), result) + } +} \ No newline at end of file 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 4134e8c148..463b0dcaea 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 @@ -25,7 +25,7 @@ object SendScreenReducer { val reducer: SendInternalReducer = when (action) { is PrepareSendScreen -> PrepareSendScreenStatesReducer() - is AddressPayIdActionUi, is AddressPayIdVerifyAction -> AddressPayIdReducer() + is AddressActionUi, is AddressVerifyAction -> AddressReducer() is TransactionExtrasAction -> TransactionExtrasReducer() is AmountActionUi, is AmountAction -> AmountReducer() is FeeActionUi, is FeeAction -> FeeReducer() @@ -71,7 +71,7 @@ private class SendReducer : SendInternalReducer { amountState = state.amountState.copy( inputIsEnabled = false, ), - addressPayIdState = state.addressPayIdState.copy( + addressState = state.addressState.copy( inputIsEnabled = false, ), ) 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/AddressState.kt similarity index 90% rename from app/src/main/java/com/tangem/tap/features/send/redux/states/AddressPayIdState.kt rename to app/src/main/java/com/tangem/tap/features/send/redux/states/AddressState.kt index 9f0851e06d..b6b65a595f 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/AddressState.kt @@ -2,17 +2,16 @@ package com.tangem.tap.features.send.redux.states import androidx.core.text.isDigitsOnly import com.tangem.blockchain.blockchains.stellar.StellarMemo -import com.tangem.tap.features.send.redux.AddressPayIdVerifyAction +import com.tangem.tap.features.send.redux.AddressVerifyAction import java.math.BigInteger -data class AddressPayIdState( +data class AddressState( val viewFieldValue: InputViewValue = InputViewValue(""), val normalFieldValue: String? = null, val truncatedFieldValue: String? = null, val destinationWalletAddress: String? = null, - val error: AddressPayIdVerifyAction.Error? = null, + val error: AddressVerifyAction.Error? = null, val truncateHandler: ((String) -> String)? = null, - val sendingToPayIdEnabled: Boolean = false, val pasteIsEnabled: Boolean = false, val inputIsEnabled: Boolean = true, ) : SendScreenState { @@ -22,8 +21,6 @@ data class AddressPayIdState( fun truncate(value: String): String = truncateHandler?.invoke(value) ?: value fun isReady(): Boolean = error == null && destinationWalletAddress?.isNotEmpty() ?: false - - fun isPayIdState(): Boolean = destinationWalletAddress != null && destinationWalletAddress != normalFieldValue } data class TransactionExtrasState( @@ -98,11 +95,7 @@ data class BinanceMemoState( val viewFieldValue: InputViewValue = InputViewValue(""), val memo: BigInteger? = null, val error: TransactionExtraError? = null, -) { - companion object { - val MAX_NUMBER: BigInteger = BigInteger("FFFFFFFFFFFFFFFF", 16) - } -} +) // tag must contains only digits data class XrpDestinationTagState( 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 6a47e2d342..8400f07c87 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 @@ -34,7 +34,7 @@ data class SendState( val coinConverter: CurrencyConverter? = null, val tokenConverter: CurrencyConverter? = null, val lastChangedStates: LinkedHashSet = linkedSetOf(), - val addressPayIdState: AddressPayIdState = AddressPayIdState(), + val addressState: AddressState = AddressState(), val transactionExtrasState: TransactionExtrasState = TransactionExtrasState(), val amountState: AmountState = AmountState(), val feeState: FeeState = FeeState(), @@ -52,7 +52,7 @@ data class SendState( MainCurrencyType.CRYPTO -> amountState.amountToExtract?.decimals ?: 0 } - fun convertFiatToCoin(value: BigDecimal): BigDecimal { + private fun convertFiatToCoin(value: BigDecimal): BigDecimal { return if (!this.coinIsConvertible()) value else coinConverter!!.toCrypto(value) } @@ -60,14 +60,14 @@ data class SendState( return if (!this.tokenIsConvertible()) value else tokenConverter!!.toCrypto(value) } - fun convertCoinToFiat(value: BigDecimal, scaleWithPrecision: Boolean = false): BigDecimal { + private fun convertCoinToFiat(value: BigDecimal, scaleWithPrecision: Boolean = false): BigDecimal { if (!this.coinIsConvertible()) return value val converter = coinConverter!! return if (!scaleWithPrecision) converter.toFiat(value) else converter.toFiatWithPrecision(value) } - fun convertTokenToFiat(value: BigDecimal, scaleWithPrecision: Boolean = false): BigDecimal { + private fun convertTokenToFiat(value: BigDecimal, scaleWithPrecision: Boolean = false): BigDecimal { if (!this.tokenIsConvertible()) return value val converter = tokenConverter!! @@ -107,13 +107,13 @@ data class SendState( } companion object { - fun addressPayIdIsReady(): Boolean = store.state.sendState.addressPayIdState.isReady() + private fun addressIsReady(): Boolean = store.state.sendState.addressState.isReady() - fun amountIsReady(): Boolean = store.state.sendState.amountState.isReady() + private fun amountIsReady(): Boolean = store.state.sendState.amountState.isReady() - fun isReadyToRequestFee(): Boolean = addressPayIdIsReady() && amountIsReady() + fun isReadyToRequestFee(): Boolean = addressIsReady() && amountIsReady() - fun isReadyToSend(): Boolean = addressPayIdIsReady() && amountIsReady() && + fun isReadyToSend(): Boolean = addressIsReady() && amountIsReady() && store.state.sendState.feeState.isReady() } } 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 64c4fc53a7..fec34d9996 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 @@ -33,7 +33,7 @@ import com.tangem.tap.common.toggleWidget.ViewStateWidget import com.tangem.tap.features.BaseStoreFragment import com.tangem.tap.features.addBackPressHandler import com.tangem.tap.features.send.redux.* -import com.tangem.tap.features.send.redux.AddressPayIdActionUi.* +import com.tangem.tap.features.send.redux.AddressActionUi.* import com.tangem.tap.features.send.redux.AmountActionUi.* import com.tangem.tap.features.send.redux.FeeActionUi.* import com.tangem.tap.features.send.redux.states.FeeType @@ -80,7 +80,7 @@ class SendFragment : BaseStoreFragment(R.layout.fragment_send) { etAmountToSend = view.findViewById(R.id.etAmountToSend) initSendButtonStates() - setupAddressOrPayIdLayout() + setupAddressLayout() setupTransactionExtrasLayout() setupAmountLayout() setupFeeLayout() @@ -99,14 +99,14 @@ class SendFragment : BaseStoreFragment(R.layout.fragment_send) { sendBtn = IndeterminateProgressButtonWidget(btnSend, progress) } - private fun setupAddressOrPayIdLayout() = with(binding.lSendAddressPayid) { - store.dispatch(SetTruncateHandler { etAddressOrPayId.truncateMiddleWith(it, "...") }) + private fun setupAddressLayout() = with(binding.lSendAddress) { + store.dispatch(SetTruncateHandler { etAddress.truncateMiddleWith(it, "...") }) store.dispatch(CheckClipboard(requireContext().getFromClipboard()?.toString())) - etAddressOrPayId.apply { + etAddress.apply { setOnSystemPasteButtonClickListener { store.dispatch( - PasteAddressPayId( + PasteAddress( data = requireContext().getFromClipboard()?.toString() ?: "", sourceType = Token.Send.AddressEntered.SourceType.PastePopup, ), @@ -119,9 +119,9 @@ class SendFragment : BaseStoreFragment(R.layout.fragment_send) { inputtedTextAsFlow() .debounce(EDIT_TEXT_INPUT_DEBOUNCE) - .filter { store.state.sendState.addressPayIdState.viewFieldValue.value != it } + .filter { store.state.sendState.addressState.viewFieldValue.value != it } .onEach { - store.dispatch(AddressPayIdActionUi.HandleUserInput(it)) + store.dispatch(AddressActionUi.HandleUserInput(it)) } .launchIn(mainScope) } @@ -129,12 +129,12 @@ class SendFragment : BaseStoreFragment(R.layout.fragment_send) { imvPaste.setOnClickListener { Analytics.send(Token.Send.ButtonPaste()) store.dispatch( - PasteAddressPayId( + PasteAddress( data = requireContext().getFromClipboard()?.toString() ?: "", sourceType = Token.Send.AddressEntered.SourceType.PasteButton, ), ) - store.dispatch(TruncateOrRestore(!etAddressOrPayId.isFocused)) + store.dispatch(TruncateOrRestore(!etAddress.isFocused)) } imvQrCode.setOnClickListener { Analytics.send(Token.Send.ButtonQRCode()) @@ -145,7 +145,7 @@ class SendFragment : BaseStoreFragment(R.layout.fragment_send) { } } - private fun setupTransactionExtrasLayout() = with(binding.lSendAddressPayid) { + private fun setupTransactionExtrasLayout() = with(binding.lSendAddress) { // TODO: [REDACTED_TASK_KEY] etXlmMemo.inputtedTextAsFlow() .debounce(EDIT_TEXT_INPUT_DEBOUNCE) @@ -202,15 +202,15 @@ class SendFragment : BaseStoreFragment(R.layout.fragment_send) { // Delayed launch is needed in order for the UI to be drawn and to process the sent events. // If do not use the delay, then etAmount error field is not displayed when // inserting an incorrect amount by shareUri - binding.lSendAddressPayid.imvQrCode.postDelayed( + binding.lSendAddress.imvQrCode.postDelayed( { store.dispatch( - PasteAddressPayId( + PasteAddress( data = scannedCode, sourceType = Token.Send.AddressEntered.SourceType.QRCode, ), ) - store.dispatch(TruncateOrRestore(!binding.lSendAddressPayid.etAddressOrPayId.isFocused)) + store.dispatch(TruncateOrRestore(!binding.lSendAddress.etAddress.isFocused)) }, 200, ) 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 12f16a0274..7c12e82496 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 @@ -7,30 +7,15 @@ import android.view.View import android.view.ViewGroup import androidx.core.text.bold import com.tangem.common.extensions.remove -import com.tangem.tap.common.extensions.beginDelayedTransition -import com.tangem.tap.common.extensions.enableError -import com.tangem.tap.common.extensions.getColor -import com.tangem.tap.common.extensions.getString -import com.tangem.tap.common.extensions.hide -import com.tangem.tap.common.extensions.show -import com.tangem.tap.common.extensions.update +import com.tangem.tap.common.extensions.* import com.tangem.tap.common.redux.getMessageString import com.tangem.tap.common.text.DecimalDigitsInputFilter import com.tangem.tap.domain.MultiMessageError import com.tangem.tap.domain.assembleErrors import com.tangem.tap.features.BaseStoreFragment -import com.tangem.tap.features.send.redux.AddressPayIdVerifyAction.Error +import com.tangem.tap.features.send.redux.AddressVerifyAction.Error import com.tangem.tap.features.send.redux.SendAction -import com.tangem.tap.features.send.redux.states.AddressPayIdState -import com.tangem.tap.features.send.redux.states.AmountState -import com.tangem.tap.features.send.redux.states.FeeState -import com.tangem.tap.features.send.redux.states.MainCurrencyType -import com.tangem.tap.features.send.redux.states.ReceiptLayoutType -import com.tangem.tap.features.send.redux.states.ReceiptState -import com.tangem.tap.features.send.redux.states.SendState -import com.tangem.tap.features.send.redux.states.StateId -import com.tangem.tap.features.send.redux.states.TransactionExtraError -import com.tangem.tap.features.send.redux.states.TransactionExtrasState +import com.tangem.tap.features.send.redux.states.* import com.tangem.tap.features.send.ui.FeeUiHelper import com.tangem.tap.features.send.ui.SendFragment import com.tangem.tap.features.send.ui.dialogs.KaspaWarningDialog @@ -61,7 +46,7 @@ class SendStateSubscriber(fragment: BaseStoreFragment) : FragmentStateSubscriber lastChangedStates.forEach { when (it) { StateId.SEND_SCREEN -> handleSendScreen(fg, state) - StateId.ADDRESS_PAY_ID -> handleAddressPayIdState(fg, state.addressPayIdState) + StateId.ADDRESS_PAY_ID -> handleAddressState(fg, state.addressState) StateId.TRANSACTION_EXTRAS -> handleTransactionExtrasState(fg, state.transactionExtrasState) StateId.AMOUNT -> handleAmountState(fg, state.amountState) StateId.FEE -> handleFeeState(fg, state.feeState) @@ -72,7 +57,7 @@ class SendStateSubscriber(fragment: BaseStoreFragment) : FragmentStateSubscriber @Suppress("ComplexMethod") private fun handleTransactionExtrasState(fg: SendFragment, infoState: TransactionExtrasState) = - with(fg.binding.lSendAddressPayid) { + with(fg.binding.lSendAddress) { fun showView(view: View, info: Any?) { view.show(info != null) } @@ -192,13 +177,10 @@ class SendStateSubscriber(fragment: BaseStoreFragment) : FragmentStateSubscriber ) } - private fun handleAddressPayIdState(fg: SendFragment, state: AddressPayIdState) = - with(fg.binding.lSendAddressPayid) { + private fun handleAddressState(fg: SendFragment, state: AddressState) { + with(fg.binding.lSendAddress) { fun parseError(context: Context, error: Error?): String? { val resId = when (error) { - Error.PAY_ID_UNSUPPORTED_BY_BLOCKCHAIN -> R.string.send_error_payid_unsupported_by_blockchain - Error.PAY_ID_NOT_REGISTERED -> R.string.send_error_payid_not_registered - Error.PAY_ID_REQUEST_FAILED -> R.string.send_error_payid_request_failed Error.ADDRESS_INVALID_OR_UNSUPPORTED_BY_BLOCKCHAIN -> R.string.send_validation_invalid_address Error.ADDRESS_SAME_AS_WALLET -> R.string.send_error_address_same_as_wallet else -> null @@ -208,8 +190,8 @@ class SendStateSubscriber(fragment: BaseStoreFragment) : FragmentStateSubscriber imvPaste.isEnabled = state.pasteIsEnabled - val et = etAddressOrPayId - val til = tilAddressOrPayId + val et = etAddress + val til = tilAddress val parsedError = parseError(til.context, state.error) til.isEnabled = state.inputIsEnabled @@ -218,19 +200,15 @@ class SendStateSubscriber(fragment: BaseStoreFragment) : FragmentStateSubscriber flPaste.show(state.inputIsEnabled) flQrCode.show(state.inputIsEnabled) - val hintResId = if (state.sendingToPayIdEnabled) { - R.string.send_destination_hint_address_payid - } else { - R.string.send_destination_hint_address - } - til.hint = til.getString(hintResId) + til.hint = til.getString(R.string.send_destination_hint_address) til.error = parsedError til.isErrorEnabled = parsedError != null til.helperText = state.destinationWalletAddress - til.isHelperTextEnabled = state.isPayIdState() && parsedError == null + til.isHelperTextEnabled = parsedError == null if (!state.viewFieldValue.isFromUserInput) et.update(state.viewFieldValue.value) } + } private fun handleAmountState(fg: SendFragment, state: AmountState) = with(fg.binding.lSendAmount) { if (state.error != null) { diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletViewModel.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletViewModel.kt index ded1be23f3..95f80ebca8 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletViewModel.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletViewModel.kt @@ -17,16 +17,8 @@ import com.tangem.tap.features.wallet.ui.analytics.WalletAnalyticsEventsMapper import com.tangem.tap.store import com.tangem.tap.walletStoresManager import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.FlowPreview -import kotlinx.coroutines.Job -import kotlinx.coroutines.delay -import kotlinx.coroutines.flow.debounce -import kotlinx.coroutines.flow.flatMapLatest -import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.onEach -import kotlinx.coroutines.launch +import kotlinx.coroutines.* +import kotlinx.coroutines.flow.* import org.rekotlin.StoreSubscriber import javax.inject.Inject @@ -71,6 +63,7 @@ internal class WalletViewModel @Inject constructor( currency = currency, batch = scanResponse.card.batchId, signInType = signInType, + walletsCount = store.state.globalState.userWalletsListManager?.walletsCount.toString(), ), ) } diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/utils/WalletDataOperations.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/utils/WalletDataOperations.kt index f2d65a4fcf..3f72a34acb 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/utils/WalletDataOperations.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/utils/WalletDataOperations.kt @@ -98,7 +98,7 @@ internal fun WalletDataModel.getAvailableActions( internal fun WalletDataModel.shouldShowMultipleAddress(): Boolean { val listOfAddresses = walletAddresses?.list.orEmpty() - return listOfAddresses.size > 1 && currency.blockchain != Blockchain.BitcoinCash + return listOfAddresses.size > 1 } internal fun WalletDataModel.assembleWarnings( diff --git a/app/src/main/java/com/tangem/tap/network/payid/PayIdVerifyApi.kt b/app/src/main/java/com/tangem/tap/network/payid/PayIdVerifyApi.kt deleted file mode 100644 index d9d00a663b..0000000000 --- a/app/src/main/java/com/tangem/tap/network/payid/PayIdVerifyApi.kt +++ /dev/null @@ -1,40 +0,0 @@ -package com.tangem.tap.network.payid - -import com.squareup.moshi.JsonClass -import retrofit2.http.GET -import retrofit2.http.Header -import retrofit2.http.Path - -/** -[REDACTED_AUTHOR] - */ -interface PayIdVerifyApi { - @GET("{user}") - suspend fun verifyAddress( - @Path("user") user: String, - @Header("Accept") acceptNetworkHeader: String, - @Header("PayID-Version") payIdVersion: String = "1.0", - ): VerifyPayIdResponse -} - -@JsonClass(generateAdapter = true) -data class VerifyPayIdResponse( - val addresses: List = mutableListOf(), - val payId: String? = null, -) { - fun getAddressDetails(): PayIdAddressDetails? = if (addresses.isNotEmpty()) addresses[0].addressDetails else null -} - -@JsonClass(generateAdapter = true) -data class PayIdAddress( - var paymentNetwork: String, - var environment: String, - var addressDetailsType: String, - var addressDetails: PayIdAddressDetails, -) - -@JsonClass(generateAdapter = true) -data class PayIdAddressDetails( - var address: String, - var tag: String? = null, -) \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/network/payid/PayIdVerifyService.kt b/app/src/main/java/com/tangem/tap/network/payid/PayIdVerifyService.kt deleted file mode 100644 index 05d16f8724..0000000000 --- a/app/src/main/java/com/tangem/tap/network/payid/PayIdVerifyService.kt +++ /dev/null @@ -1,24 +0,0 @@ -package com.tangem.tap.network.payid - -import com.tangem.common.services.Result -import com.tangem.common.services.performRequest -import com.tangem.datasource.api.common.createRetrofitInstance - -/** -[REDACTED_AUTHOR] - */ -class PayIdVerifyService( - private val baseUrl: String, -) { - - private val api = createRetrofitInstance( - baseUrl = baseUrl, - logEnabled = false, - ).create(PayIdVerifyApi::class.java) - - suspend fun verifyAddress(user: String, network: String): Result { - return performRequest { api.verifyAddress(user, createNetworkHeader(network)) } - } - - private fun createNetworkHeader(network: String): String = "application/$network-mainnet+json" -} \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_send.xml b/app/src/main/res/layout/fragment_send.xml index 8ebe4b5488..4035f923e2 100644 --- a/app/src/main/res/layout/fragment_send.xml +++ b/app/src/main/res/layout/fragment_send.xml @@ -39,8 +39,8 @@ android:orientation="vertical"> diff --git a/app/src/main/res/layout/layout_address.xml b/app/src/main/res/layout/layout_address.xml index 22853700ed..713c384939 100644 --- a/app/src/main/res/layout/layout_address.xml +++ b/app/src/main/res/layout/layout_address.xml @@ -150,63 +150,6 @@ app:layout_constraintTop_toBottomOf="@id/tv_explore" tools:text="Send only Ethereum (ETH) from Ethereum network to this address. Using other tokens and networks may result in loss of funds." /> - - - - - - - - - - diff --git a/app/src/main/res/layout/layout_send_address_payid.xml b/app/src/main/res/layout/layout_send_address.xml similarity index 84% rename from app/src/main/res/layout/layout_send_address_payid.xml rename to app/src/main/res/layout/layout_send_address.xml index 1bf69a649f..c6dd2873c8 100644 --- a/app/src/main/res/layout/layout_send_address_payid.xml +++ b/app/src/main/res/layout/layout_send_address.xml @@ -18,10 +18,9 @@ app:layout_constraintTop_toTopOf="parent"> + app:layout_constraintTop_toTopOf="@+id/tilAddress"> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + app:layout_constraintTop_toBottomOf="@+id/tilAddress"> = Build.VERSION_CODES.N) - Html.fromHtml(engine!!.balanceHTML, Html.FROM_HTML_MODE_LEGACY) - else - Html.fromHtml(engine!!.balanceHTML) - tvBalance.text = html - - isIncludeFee = arguments?.getBoolean(Constant.EXTRA_FEE_INCLUDED, true) ?: true - - if (isIncludeFee) - tvIncFee.setText(R.string.confirm_transaction_including_fee) - else - tvIncFee.setText(R.string.confirm_transaction_not_including_fee) - - amount = CoinEngine.Amount(arguments?.getString(Constant.EXTRA_AMOUNT) ?: "0", - arguments?.getString(Constant.EXTRA_AMOUNT_CURRENCY) ?: "") - - if (engine.allowSelectFeeInclusion()) - tvIncFee.visibility = View.VISIBLE - else - tvIncFee.visibility = View.INVISIBLE - - if (ctx.card.blockchainID == Blockchain.Token.id) { - // for Blockchain.Token limit decimals - etAmount.setText(amount.toValueString(ctx.card.tokensDecimal)) - } else { - // for others - etAmount.setText(amount.toValueString()) - } - - tvCurrency.text = engine.balanceCurrency - tvCurrency2.text = engine.feeCurrency - tvCardID.text = ctx.card.cidDescription - etWallet.setText(arguments?.getString(Constant.EXTRA_TARGET_ADDRESS)) - - btnSend.visibility = View.INVISIBLE - - if (!engine.allowSelectFeeLevel()) { - rgFee.visibility = View.INVISIBLE - } - - etFee.isEnabled = sp.getBoolean(getString(R.string.pref_manual_editing_fee), false) - - // set listeners - rgFee.setOnCheckedChangeListener { _, checkedId -> doSetFee(checkedId) } - etFee.addTextChangedListener(object : TextWatcher { - override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) { - - } - - override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) { - try { - val eqFee = engine.evaluateFeeEquivalent(etFee!!.text.toString()) - tvFeeEquivalent.text = eqFee - - if (!ctx.coinData!!.amountEquivalentDescriptionAvailable) { - tvFeeEquivalent.error = getString(R.string.confirm_transaction_error_service_unavailable) - tvCurrency2.visibility = View.GONE - tvFeeEquivalent.visibility = View.GONE - } else - tvFeeEquivalent.error = null - - if (sp.getBoolean(getString(R.string.pref_manual_editing_fee), false)) - (activity as MainActivity).toastHelper - .showSingleToast(context, getString(R.string.confirm_transaction_warning_risk_delaying)) - - } catch (e: Exception) { - e.printStackTrace() - tvFeeEquivalent.text = "" - } - } - - override fun afterTextChanged(s: Editable) { - - } - }) - btnSend.setOnClickListener { - if (UtilHelper.isOnline(requireContext())) { - val calendar = Calendar.getInstance() - calendar.add(Calendar.MINUTE, -1) - - if (dtVerified == null || dtVerified!!.before(calendar.time)) { - finishWithError(Activity.RESULT_CANCELED, getString(R.string.confirm_transaction_error_data_is_outdated)) - return@setOnClickListener - } - - val engineCoin = CoinEngineFactory.create(ctx) - - if (engineCoin!!.isNeedCheckNode && !nodeCheck) { - Toast.makeText(context, getString(R.string.confirm_transaction_error_cannot_reach_node), Toast.LENGTH_LONG).show() - return@setOnClickListener - } - - val txFee = engineCoin.convertToAmount(etFee.text.toString(), tvCurrency2.text.toString()) - val txAmount = engineCoin.convertToAmount(etAmount.text.toString(), tvCurrency.text.toString()) - - if (!engineCoin.hasBalanceInfo()) { - finishWithError(Activity.RESULT_CANCELED, getString(R.string.confirm_transaction_error_cannot_check_balance)) - return@setOnClickListener - - } else if (!engineCoin.isBalanceNotZero) { - finishWithError(Activity.RESULT_CANCELED, getString(R.string.general_wallet_empty)) - return@setOnClickListener - - } else if (!engineCoin.isExtractPossible) { - finishWithError(Activity.RESULT_CANCELED, getString(R.string.confirm_transaction_error_incoming_transaction_unconfirmed)) - return@setOnClickListener - } - - if (!engineCoin.checkNewTransactionAmountAndFee(txAmount, txFee, isIncludeFee)) { - finishWithError(Activity.RESULT_CANCELED, getString(R.string.prepare_transaction_error_not_enough_funds)) - return@setOnClickListener - } - - requestPIN2Count = 0 - val data = Bundle() - data.putString(Constant.EXTRA_MODE, PinRequestFragment.Mode.RequestPIN2.toString()) - ctx.saveToBundle(data) - data.putBoolean(Constant.EXTRA_FEE_INCLUDED, isIncludeFee) - navigateForResult(Constant.REQUEST_CODE_REQUEST_PIN2_, R.id.action_confirmTransactionFragment_to_pinRequestFragment, data) - } else - Toast.makeText(context, getString(R.string.general_error_no_connection), Toast.LENGTH_SHORT).show() - } - - progressBar.visibility = View.VISIBLE - - if (!navigatedBack) requestFee() - } - - private fun requestFee() { - val coinEngine = CoinEngineFactory.create(ctx) - coinEngine!!.requestFee( - object : CoinEngine.BlockchainRequestsCallbacks { - override fun onComplete(success: Boolean) { - if (success) { - progressBar?.visibility = View.INVISIBLE - dtVerified = Date() - doSetFee(rgFee?.checkedRadioButtonId ?: R.id.rbNormalFee) - } else { - finishWithError(Activity.RESULT_CANCELED, ctx.error) - } - } - - override fun onProgress() { - } - - override fun allowAdvance(): Boolean { - return UtilHelper.isOnline(requireContext()) - } - }, - etWallet.text.toString(), - amount) - } - - override fun onNavigationResult(requestCode: String, resultCode: Int, data: Bundle?) { - Log.d("LIFECYCLE", "NavigationResult assessed ${this::class.java.simpleName}") - if (requestCode == Constant.REQUEST_CODE_SIGN_TRANSACTION) { - if (data != null) { - if (data.containsKey(EXTRA_TANGEM_CARD_UID) && data.containsKey(EXTRA_TANGEM_CARD)) { - val updatedCard = TangemCard(data.getString(EXTRA_TANGEM_CARD_UID)) - updatedCard.loadFromBundle(data.getBundle(EXTRA_TANGEM_CARD)) - ctx.card = updatedCard - } - } - if (resultCode == Constant.RESULT_INVALID_PIN_ && requestPIN2Count < 2) { - requestPIN2Count++ - val bundle = Bundle() - bundle.putString(Constant.EXTRA_MODE, PinRequestFragment.Mode.RequestPIN2.toString()) - ctx.saveToBundle(bundle) - bundle.putBoolean(Constant.EXTRA_FEE_INCLUDED, isIncludeFee) - navigateForResult(Constant.REQUEST_CODE_REQUEST_PIN2_, - R.id.action_confirmTransactionFragment_to_pinRequestFragment, - bundle) - return - } - navigateBackWithResult(resultCode, data) - } else if (requestCode == Constant.REQUEST_CODE_REQUEST_PIN2_) { - if (resultCode == Activity.RESULT_OK) { - val bundle = Bundle() - ctx.saveToBundle(bundle) - bundle.putString(Constant.EXTRA_TARGET_ADDRESS, etWallet!!.text.toString()) - bundle.putString(Constant.EXTRA_AMOUNT, etAmount.text.toString()) - bundle.putString(Constant.EXTRA_AMOUNT_CURRENCY, tvCurrency.text.toString()) - bundle.putString(Constant.EXTRA_FEE, etFee.text.toString()) - bundle.putString(Constant.EXTRA_FEE_CURRENCY, tvCurrency2.text.toString()) - bundle.putBoolean(Constant.EXTRA_FEE_INCLUDED, isIncludeFee) - navigateForResult(Constant.REQUEST_CODE_SIGN_TRANSACTION, - R.id.action_confirmTransactionFragment_to_signTransactionFragment, - bundle) - } else - Toast.makeText(context, R.string.confirm_transaction_error_pin_2_is_required, Toast.LENGTH_LONG).show() - } - } - - override fun onTagDiscovered(tag: Tag) { - try { - (activity as MainActivity).nfcManager.ignoreTag(tag) - } catch (e: IOException) { - e.printStackTrace() - } - } - - private fun doSetFee(checkedRadioButtonId: Int) { - var txtFee = "" - when (checkedRadioButtonId) { - R.id.rbMinimalFee -> - if (ctx.coinData.minFee != null) { - txtFee = ctx.coinData.minFee!!.toValueString() - btnSend?.visibility = View.VISIBLE - } else - btnSend?.visibility = View.INVISIBLE - - R.id.rbNormalFee -> - if (ctx.coinData.normalFee != null) { - txtFee = ctx.coinData.normalFee!!.toValueString() - btnSend?.visibility = View.VISIBLE - } else - btnSend?.visibility = View.INVISIBLE - - R.id.rbMaximumFee -> - if (ctx.coinData.maxFee != null) { - txtFee = ctx.coinData.maxFee!!.toValueString() - btnSend?.visibility = View.VISIBLE - } else - btnSend?.visibility = View.INVISIBLE - } - etFee?.setText(txtFee.replace(',', '.')) - } - - private fun finishWithError(errorCode: Int, message: String) { - navigateBackWithResult( - errorCode, - bundleOf(Constant.EXTRA_MESSAGE to message), - R.id.loadedWalletFragment) - } -} \ No newline at end of file diff --git a/app/src/tangemAccess/java/com/tangem/ui/PrepareTransactionFragment.kt b/app/src/tangemAccess/java/com/tangem/ui/PrepareTransactionFragment.kt deleted file mode 100644 index 387f992bb6..0000000000 --- a/app/src/tangemAccess/java/com/tangem/ui/PrepareTransactionFragment.kt +++ /dev/null @@ -1,182 +0,0 @@ -package com.tangem.ui - -import android.app.Activity -import android.content.Context -import android.nfc.NfcAdapter -import android.nfc.Tag -import android.os.Build -import android.os.Bundle -import android.text.Html -import android.view.View -import android.view.inputmethod.EditorInfo -import android.view.inputmethod.InputMethodManager -import android.widget.Toast -import com.tangem.Constant -import com.tangem.data.isPayIdSupported -import com.tangem.ui.activity.MainActivity -import com.tangem.ui.fragment.BaseFragment -import com.tangem.ui.fragment.qr.CameraPermissionManager -import com.tangem.ui.navigation.NavigationResultListener -import com.tangem.util.UtilHelper -import com.tangem.util.extensions.isStart2CoinCard -import com.tangem.wallet.CoinEngineFactory -import com.tangem.wallet.R -import com.tangem.wallet.TangemContext -import kotlinx.android.synthetic.tangemAccess.fragment_prepare_transaction.* -import java.io.IOException - -class PrepareTransactionFragment : BaseFragment(), NavigationResultListener, NfcAdapter.ReaderCallback { - companion object { - val TAG: String = PrepareTransactionFragment::class.java.simpleName - } - - override val layoutId = R.layout.fragment_prepare_transaction - - private val ctx: TangemContext by lazy { TangemContext.loadFromBundle(context, arguments) } - private val cameraPermissionManager: CameraPermissionManager by lazy { CameraPermissionManager(this) } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - - tvCardID.text = ctx.card?.cidDescription - val engine = CoinEngineFactory.create(ctx) - - @Suppress("DEPRECATION") val html = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) - Html.fromHtml(engine!!.balanceHTML, Html.FROM_HTML_MODE_LEGACY) - else - Html.fromHtml(engine!!.balanceHTML) - tvBalance.text = html - - if (ctx.blockchain.isPayIdSupported() && !ctx.card.isStart2CoinCard()) { - etWallet.hint = getString(R.string.prepare_transaction_hint_address_or_pay_id) - } - - if (!engine.allowSelectFeeInclusion()) { - rgIncFee.visibility = View.INVISIBLE - } else { - rgIncFee.visibility = View.VISIBLE - } - - if (ctx.card!!.remainingSignatures < 2) { - etAmount.isEnabled = false - } - - if (ctx.card.remainingSignatures == 1) { - androidx.appcompat.app.AlertDialog.Builder(requireContext()) - .setTitle(R.string.prepare_transaction_warning_last_signature) - .setMessage(R.string.prepare_transaction_warning_send_full_amount) - .setPositiveButton(R.string.general_ok) { _, _ -> } - .create() - .show() - } - - tvCurrency.text = engine.balance.currency - etAmount.setText(engine.balance.toValueString()) - - // limit number of symbols after comma - etAmount.filters = engine.amountInputFilters - - // set listeners - etAmount.setOnEditorActionListener { lv, actionId, _ -> - if (actionId == EditorInfo.IME_ACTION_DONE) { - val imm = lv.context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager - imm.hideSoftInputFromWindow(lv.windowToken, 0) - lv.clearFocus() - true - } else { - false - } - } - - btnVerify.setOnClickListener { - if (!UtilHelper.isOnline(requireContext())) { - Toast.makeText(context, R.string.general_error_no_connection, Toast.LENGTH_LONG).show() - return@setOnClickListener - } - - val engine1 = CoinEngineFactory.create(ctx) - val strAmount: String = etAmount.text.toString().replace(",", ".") - val amount = engine1!!.convertToAmount(etAmount.text.toString(), tvCurrency.text.toString()) - - try { - if (!engine.checkNewTransactionAmount(amount)) - etAmount.error = getString(R.string.prepare_transaction_error_not_enough_funds) - else - etAmount.error = null - } catch (e: Exception) { - etAmount.error = getString(R.string.prepare_transaction_error_unknown_amount_format) - } - - // check wallet address - if (!engine1.validateAddress(etWallet.text.toString())) { - etWallet.error = getString(R.string.prepare_transaction_error_incorrect_destination) - return@setOnClickListener - } else - etWallet.error = null - - if (etWallet.text.toString() == ctx.coinData!!.wallet) { - etWallet.error = getString(R.string.prepare_transaction_error_same_address) - return@setOnClickListener - } - - if (!etAmount.error.isNullOrEmpty() || !etWallet.error.isNullOrEmpty()) { - return@setOnClickListener - } - - val data = Bundle() - ctx.saveToBundle(data) - data.putString(Constant.EXTRA_TARGET_ADDRESS, etWallet!!.text.toString()) - data.putBoolean(Constant.EXTRA_FEE_INCLUDED, (rgIncFee!!.checkedRadioButtonId == R.id.rbFeeIn)) - data.putString(Constant.EXTRA_AMOUNT, strAmount) - data.putString(Constant.EXTRA_AMOUNT_CURRENCY, tvCurrency.text.toString()) - navigateForResult( - Constant.REQUEST_CODE_SEND_TRANSACTION__, - R.id.action_prepareTransactionFragment_to_confirmTransactionFragment, - data) - } - - ivCamera.setOnClickListener { - if (cameraPermissionManager.isPermissionGranted()) { - navigateForResult(Constant.REQUEST_CODE_SCAN_QR, R.id.action_prepareTransactionFragment_to_qrScanFragment) - } else { - cameraPermissionManager.requirePermission() - } - } - } - - override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) { - super.onRequestPermissionsResult(requestCode, permissions, grantResults) - cameraPermissionManager.handleRequestPermissionResult(requestCode, grantResults) { - navigateForResult(Constant.REQUEST_CODE_SCAN_QR, R.id.action_prepareTransactionFragment_to_qrScanFragment) - } - } - - override fun onNavigationResult(requestCode: String, resultCode: Int, data: Bundle?) { - if (requestCode == Constant.REQUEST_CODE_SCAN_QR && resultCode == Activity.RESULT_OK && data != null && data.containsKey("QRCode")) { - val code = data.getString("QRCode") - val schemeSplit = code!!.split(":") - when (schemeSplit.size) { - 2 -> { - if (schemeSplit[0] == ctx.blockchain.uriScheme) { - etWallet?.setText(schemeSplit[1]) - } else { - etWallet?.setText(code) - } - } - else -> { - etWallet?.setText(code) - } - } - } else if (requestCode == Constant.REQUEST_CODE_SEND_TRANSACTION__) { - navigateBackWithResult(resultCode, data) - } - } - - override fun onTagDiscovered(tag: Tag) { - try { - (activity as MainActivity).nfcManager.ignoreTag(tag) - } catch (e: IOException) { - e.printStackTrace() - } - } -} \ No newline at end of file diff --git a/app/src/tangemAccess/java/com/tangem/ui/SignTransactionFragment.kt b/app/src/tangemAccess/java/com/tangem/ui/SignTransactionFragment.kt deleted file mode 100644 index fdcdc64a72..0000000000 --- a/app/src/tangemAccess/java/com/tangem/ui/SignTransactionFragment.kt +++ /dev/null @@ -1,312 +0,0 @@ -package com.tangem.ui - -import android.app.Activity -import android.content.res.ColorStateList -import android.graphics.Color -import android.media.MediaPlayer -import android.nfc.NfcAdapter -import android.nfc.Tag -import android.nfc.tech.IsoDep -import android.os.Bundle -import android.view.View -import androidx.activity.OnBackPressedCallback -import com.google.firebase.analytics.FirebaseAnalytics -import com.google.firebase.crashlytics.FirebaseCrashlytics -import com.tangem.App -import com.tangem.Constant -import com.tangem.tangem_card.reader.CardProtocol -import com.tangem.tangem_card.tasks.SignTask -import com.tangem.tangem_card.util.Util -import com.tangem.tangem_sdk.android.nfc.NfcDeviceAntennaLocation -import com.tangem.tangem_sdk.android.reader.NfcReader -import com.tangem.tangem_sdk.data.EXTRA_TANGEM_CARD -import com.tangem.tangem_sdk.data.EXTRA_TANGEM_CARD_UID -import com.tangem.tangem_sdk.data.asBundle -import com.tangem.ui.activity.MainActivity -import com.tangem.ui.dialog.NoExtendedLengthSupportDialog -import com.tangem.ui.dialog.WaitSecurityDelayDialog -import com.tangem.ui.fragment.BaseFragment -import com.tangem.ui.navigation.NavigationResultListener -import com.tangem.util.Analytics -import com.tangem.util.AnalyticsEvent -import com.tangem.util.LOG -import com.tangem.wallet.CoinEngine -import com.tangem.wallet.CoinEngineFactory -import com.tangem.wallet.R -import com.tangem.wallet.TangemContext -import kotlinx.android.synthetic.main.layout_progress_horizontal.* -import kotlinx.android.synthetic.main.layout_touch_card.* -import kotlinx.android.synthetic.tangemAccess.fragment_sign_transaction.* - - -class SignTransactionFragment : BaseFragment(), NavigationResultListener, - NfcAdapter.ReaderCallback, CardProtocol.Notifications { - - companion object { - val TAG: String = SignTransactionFragment::class.java.simpleName - } - - override val layoutId = R.layout.fragment_sign_transaction - - private lateinit var ctx: TangemContext - private lateinit var mpFinishSignSound: MediaPlayer - - private lateinit var nfcDeviceAntenna: NfcDeviceAntennaLocation - - private var signTransactionTask: SignTask? = null - - private lateinit var amount: CoinEngine.Amount - private lateinit var fee: CoinEngine.Amount - private var isIncludeFee = true - private var outAddressStr: String? = null - private var lastReadSuccess = true - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - ctx = TangemContext.loadFromBundle(context, arguments) - - val callback = object : OnBackPressedCallback(true) { - override fun handleOnBackPressed() { - navigateBackWithResult(Activity.RESULT_CANCELED) - } - } - requireActivity().onBackPressedDispatcher.addCallback(this, callback) - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - - mpFinishSignSound = MediaPlayer.create(context, R.raw.scan_card_sound) - - // init NFC Antenna - nfcDeviceAntenna = NfcDeviceAntennaLocation(requireContext(), ivHandCardHorizontal, ivHandCardVertical, llHand, llNfc) - nfcDeviceAntenna.init() - - amount = CoinEngine.Amount(arguments?.getString(Constant.EXTRA_AMOUNT), arguments?.getString(Constant.EXTRA_AMOUNT_CURRENCY)) - fee = CoinEngine.Amount(arguments?.getString(Constant.EXTRA_FEE), arguments?.getString(Constant.EXTRA_FEE_CURRENCY)) - isIncludeFee = arguments?.getBoolean(Constant.EXTRA_FEE_INCLUDED, true) ?: true - outAddressStr = arguments?.getString(Constant.EXTRA_TARGET_ADDRESS) - - tvCardID.text = ctx.card!!.cidDescription - progressBar.progressTintList = ColorStateList.valueOf(Color.DKGRAY) - progressBar.visibility = View.INVISIBLE - - FirebaseAnalytics.getInstance(requireActivity()) - .logEvent(AnalyticsEvent.READY_TO_SIGN.event, Analytics.setCardData(ctx)) - } - - override fun onPause() { - signTransactionTask?.cancel(true) - super.onPause() - } - - override fun onStop() { - signTransactionTask?.cancel(true) - super.onStop() - } - - override fun onNavigationResult(requestCode: String, resultCode: Int, data: Bundle?) { - if (requestCode == Constant.REQUEST_CODE_SEND_TRANSACTION_) { - navigateBackWithResult(resultCode, data) - } - } - - override fun onTagDiscovered(tag: Tag) { - try { - // get IsoDep handle and run cardReader thread - val isoDep = IsoDep.get(tag) - val uid = tag.id - val sUID = Util.byteArrayToHexString(uid) - - if (sUID == ctx.card.uid) { - if (lastReadSuccess) - isoDep.timeout = ctx.card.pauseBeforePIN2 + 5000 - else - isoDep.timeout = ctx.card.pauseBeforePIN2 + 65000 - - val coinEngine = CoinEngineFactory.create(ctx) - coinEngine?.setOnNeedSendTransaction { tx -> - if (tx != null) { - val data = Bundle() - ctx.saveToBundle(data) - data.putByteArray(Constant.EXTRA_TX, tx) - navigateForResult( - Constant.REQUEST_CODE_SEND_TRANSACTION_, - R.id.action_signTransactionFragment_to_sendTransactionFragment, - data) - } - } - val transactionToSign = coinEngine?.constructTransaction(amount, fee, isIncludeFee, outAddressStr) - - signTransactionTask = SignTask(ctx.card, NfcReader((activity as MainActivity).nfcManager, isoDep), - App.localStorage, App.pinStorage, this, transactionToSign) - signTransactionTask?.start() - } else - (activity as MainActivity).nfcManager.ignoreTag(isoDep.tag) - - } catch (e: CardProtocol.TangemException_WrongAmount) { - try { - val data = Bundle() - data.putString(Constant.EXTRA_MESSAGE, getString(R.string.send_transaction_error_wrong_amount)) - data.putString(EXTRA_TANGEM_CARD_UID, ctx.card.uid) - data.putBundle(EXTRA_TANGEM_CARD, ctx.card.asBundle) - navigateBackWithResult(Activity.RESULT_CANCELED, data) - } catch (e: Exception) { - e.printStackTrace() - } - } catch(e: IllegalArgumentException) { - val data = Bundle() - data.putString(Constant.EXTRA_MESSAGE, e.message) - navigateBackWithResult(Activity.RESULT_CANCELED, data, R.id.loadedWalletFragment) - } catch (e: Exception) { - e.printStackTrace() - } - } - - override fun onReadStart(cardProtocol: CardProtocol) { - rlProgressBar?.post { rlProgressBar.visibility = View.VISIBLE } - - progressBar?.post { - progressBar?.visibility = View.VISIBLE - progressBar?.progress = 5 - } - } - - override fun onReadProgress(protocol: CardProtocol, progress: Int) { - progressBar?.post { progressBar?.progress = progress } - } - - override fun onReadFinish(cardProtocol: CardProtocol?) { - signTransactionTask = null - if (cardProtocol != null) { - if (cardProtocol.error == null) { - - FirebaseAnalytics.getInstance(requireActivity()) - .logEvent(AnalyticsEvent.SIGNED.event, Analytics.setCardData(ctx)) - - rlProgressBar?.post { rlProgressBar?.visibility = View.GONE } - - progressBar?.post { - progressBar?.progress = 100 - progressBar?.progressTintList = ColorStateList.valueOf(Color.GREEN) - } - - mpFinishSignSound.start() - } else { - lastReadSuccess = false - FirebaseCrashlytics.getInstance().recordException(cardProtocol.error) - if (cardProtocol.error.javaClass == CardProtocol.TangemException_InvalidPIN::class.java) { - progressBar?.post { - progressBar?.progress = 100 - progressBar?.progressTintList = ColorStateList.valueOf(Color.RED) - } - progressBar?.postDelayed({ - try { - progressBar?.progress = 0 - progressBar?.progressTintList = ColorStateList.valueOf(Color.DKGRAY) - progressBar?.visibility = View.INVISIBLE - val data = Bundle() - data.putString(Constant.EXTRA_MESSAGE, getString(R.string.send_transaction_error_cannot_sign)) - data.putString(EXTRA_TANGEM_CARD_UID, cardProtocol.card.uid) - data.putBundle(EXTRA_TANGEM_CARD, cardProtocol.card.asBundle) - navigateBackWithResult(Constant.RESULT_INVALID_PIN_, data) - } catch (e: Exception) { - e.printStackTrace() - } - }, 500) - } else { - if (cardProtocol.error is CardProtocol.TangemException_WrongAmount) { - try { - val data = Bundle() - data.putString(Constant.EXTRA_MESSAGE, getString(R.string.send_transaction_error_wrong_amount)) - data.putString(EXTRA_TANGEM_CARD_UID, cardProtocol.card.uid) - data.putBundle(EXTRA_TANGEM_CARD, cardProtocol.card.asBundle) - navigateBackWithResult(Activity.RESULT_CANCELED, data) - } catch (e: Exception) { - e.printStackTrace() - } - } - progressBar?.post { - if (cardProtocol.error is CardProtocol.TangemException_ExtendedLengthNotSupported) { - if (!NoExtendedLengthSupportDialog.allReadyShowed) { - NoExtendedLengthSupportDialog.message = getText(R.string.dialog_the_nfc_adapter_length_apdu).toString() + "\n" + getText(R.string.dialog_the_nfc_adapter_length_apdu_advice).toString() - NoExtendedLengthSupportDialog().show(requireFragmentManager(), NoExtendedLengthSupportDialog.TAG) - } - } else { - (activity as? MainActivity)?.toastHelper?.showSingleToast( - context, getString(R.string.general_notification_scan_again) - ) - } - progressBar?.progress = 100 - progressBar?.progressTintList = ColorStateList.valueOf(Color.RED) - } - } - } - } - - rlProgressBar?.postDelayed({ - try { - rlProgressBar?.visibility = View.GONE - } catch (e: Exception) { - e.printStackTrace() - } - }, 500) - - progressBar?.postDelayed({ - try { - progressBar?.progress = 0 - progressBar?.progressTintList = ColorStateList.valueOf(Color.DKGRAY) - progressBar?.visibility = View.INVISIBLE - } catch (e: Exception) { - e.printStackTrace() - } - }, 500) - } - - override fun onReadCancel() { - signTransactionTask = null - - progressBar?.postDelayed({ - try { - progressBar?.progress = 0 - progressBar?.progressTintList = ColorStateList.valueOf(Color.DKGRAY) - progressBar?.visibility = View.INVISIBLE - } catch (e: Exception) { - e.printStackTrace() - } - }, 500) - } - -// private val waitSecurityDelayDialogNew = WaitSecurityDelayDialogNew() - - override fun onReadBeforeRequest(timeout: Int) { - LOG.i(TAG, "onReadBeforeRequest timeout $timeout") - activity?.let { WaitSecurityDelayDialog.onReadBeforeRequest(it, timeout) } - -// if (!waitSecurityDelayDialogNew.isAdded) -// waitSecurityDelayDialogNew.show(supportFragmentManager, WaitSecurityDelayDialogNew.TAG) - - -// val readBeforeRequest = ReadBeforeRequest() -// readBeforeRequest.timeout = timeout -// EventBus.getDefault().post(readBeforeRequest) - } - - override fun onReadAfterRequest() { - LOG.i(TAG, "onReadAfterRequest") - activity?.let { WaitSecurityDelayDialog.onReadAfterRequest(it) } - -// val readAfterRequest = ReadAfterRequest() -// EventBus.getDefault().post(readAfterRequest) - } - - override fun onReadWait(msec: Int) { - LOG.i(TAG, "onReadWait msec $msec") - activity?.let { WaitSecurityDelayDialog.onReadWait(it, msec) } - -// val readWait = ReadWait() -// readWait.msec = msec -// EventBus.getDefault().post(readWait) - } - -} \ No newline at end of file diff --git a/app/src/tangemAccess/res/layout/fragment_confirm_transaction.xml b/app/src/tangemAccess/res/layout/fragment_confirm_transaction.xml deleted file mode 100644 index 6bb5f43dda..0000000000 --- a/app/src/tangemAccess/res/layout/fragment_confirm_transaction.xml +++ /dev/null @@ -1,343 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -