From bc587895f084a7d0706f86d94ec8fbe62e9f0066 Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 4 Sep 2020 16:13:35 +0300 Subject: [PATCH 1/6] Updated on 2026-08-14 --- .../java/com/tangem/tap/features/wallet/ui/WalletFragment.kt | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletFragment.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletFragment.kt index 1c4c43a098..da574ab6e5 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletFragment.kt @@ -56,9 +56,6 @@ class WalletFragment : Fragment(R.layout.fragment_wallet), StoreSubscriber TODO() + is WalletMainButton.SendButton -> store.dispatch(NavigationAction.NavigateTo(AppScreen.Send)) is WalletMainButton.CreateWalletButton -> store.dispatch(WalletAction.CreateWallet) } } From 90bdddb5e8792f7dcb4694557e1c2c43983b1b41 Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 4 Sep 2020 20:14:19 +0300 Subject: [PATCH 2/6] Updated on 2026-08-14 --- .../main/java/com/tangem/tap/MainActivity.kt | 3 + .../com/tangem/tap/domain/PayIdManager.kt | 10 +- .../tap/features/send/redux/SendMiddleware.kt | 109 +++++++++++------- .../tap/features/send/redux/SendReducer.kt | 48 +++++--- .../features/send/redux/SendScreenAction.kt | 29 ++++- .../tap/features/send/redux/SendState.kt | 26 ++++- .../tap/features/send/ui/SendFragment.kt | 23 +++- .../stateSubscribers/SendStateSubscriber.kt | 27 ++++- .../tap/network/payid/PayIdVerifyApi.kt | 44 +++++++ .../tap/network/payid/PayIdVerifyService.kt | 21 ++++ .../res/layout/layout_send_address_payid.xml | 2 +- app/src/main/res/values/strings.xml | 7 ++ 12 files changed, 278 insertions(+), 71 deletions(-) create mode 100644 app/src/main/java/com/tangem/tap/network/payid/PayIdVerifyApi.kt create mode 100644 app/src/main/java/com/tangem/tap/network/payid/PayIdVerifyService.kt diff --git a/app/src/main/java/com/tangem/tap/MainActivity.kt b/app/src/main/java/com/tangem/tap/MainActivity.kt index b2947e0c13..9b24821ade 100644 --- a/app/src/main/java/com/tangem/tap/MainActivity.kt +++ b/app/src/main/java/com/tangem/tap/MainActivity.kt @@ -36,6 +36,9 @@ private val coroutineContext: CoroutineContext get() = Job() + Dispatchers.IO + initCoroutineExceptionHandler() val scope = CoroutineScope(coroutineContext) +private val mainCoroutineContext: CoroutineContext + get() = Job() + Dispatchers.Main +val mainScope = CoroutineScope(mainCoroutineContext) private fun initCoroutineExceptionHandler(): CoroutineExceptionHandler { return CoroutineExceptionHandler { _, throwable -> diff --git a/app/src/main/java/com/tangem/tap/domain/PayIdManager.kt b/app/src/main/java/com/tangem/tap/domain/PayIdManager.kt index 235f0ea37a..ddf6c99669 100644 --- a/app/src/main/java/com/tangem/tap/domain/PayIdManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/PayIdManager.kt @@ -3,7 +3,9 @@ package com.tangem.tap.domain import com.tangem.blockchain.common.Blockchain import com.tangem.commands.common.network.Result import com.tangem.tap.network.payid.PayIdService +import com.tangem.tap.network.payid.PayIdVerifyService import com.tangem.tap.network.payid.SetPayIdResponse +import com.tangem.tap.network.payid.VerifyPayIdResponse import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import retrofit2.HttpException @@ -41,13 +43,19 @@ class PayIdManager { } } + 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 - } + }.toLowerCase() } companion object { diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/SendMiddleware.kt b/app/src/main/java/com/tangem/tap/features/send/redux/SendMiddleware.kt index f734d0816c..604d7c7a22 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/SendMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/SendMiddleware.kt @@ -1,13 +1,19 @@ package com.tangem.tap.features.send.redux +import com.tangem.blockchain.common.Wallet import com.tangem.blockchain.common.WalletManager +import com.tangem.commands.common.network.Result import com.tangem.tap.common.redux.AppState import com.tangem.tap.domain.PayIdManager +import com.tangem.tap.domain.PayIdManager.Companion.isPayId import com.tangem.tap.domain.isPayIdSupported import com.tangem.tap.features.send.redux.AddressPayIdActionUI.SetAddressOrPayId +import com.tangem.tap.features.send.redux.AddressPayIdVerifyAction.* import com.tangem.tap.scope import com.tangem.tap.store +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import org.rekotlin.Action import org.rekotlin.Middleware @@ -29,59 +35,84 @@ private fun handleSendAction(action: Action) { when (sendAction) { is AddressPayIdActionUI -> { when (sendAction) { - is SetAddressOrPayId -> AddressPayIdHandler().handle(sendAction.data?.toString()) + is SetAddressOrPayId -> AddressPayIdHandler().handle(sendAction.data) } } } } internal class AddressPayIdHandler { - fun handle(data: String?) { + fun handle(data: String) { val walletManager = store.state.globalState.walletManager ?: return - val clipboardData = data ?: return - if (PayIdManager.isPayId(clipboardData)) { - if (walletManager.wallet.blockchain.isPayIdSupported()) { - store.dispatch(AddressPayIdAction.Verification.PayIdNotSupportedByBlockchain) - } else { - scope.launch { - val response = verifyPayID(walletManager, clipboardData) - if (response == null) { - store.dispatch(AddressPayIdAction.Verification.Failed) - } else { - val address = "some address D:" // extract from response - store.dispatch(AddressPayIdAction.Verification.Success(address)) - } - } - } + if (isPayId(data)) { + verifyPayId(data, walletManager) + store.dispatch(ProcessedButNotVerifiedAddressPayId(data)) } else { - + val supposedAddress = extractAddressFromShareUri(data) + store.dispatch(PayIdVerification.Failed(supposedAddress, FailReason.IS_NOT_PAY_ID)) + store.dispatch(ProcessedButNotVerifiedAddressPayId(supposedAddress)) + verifyAddress(walletManager, supposedAddress) } } - private suspend fun verifyPayID(walletManager: WalletManager, payID: String?): String? { - val cardId = walletManager.cardId - val publicKey = store.state.globalState.card?.cardPublicKey + private fun verifyPayId(payId: String, walletManager: WalletManager) { + val blockchain = walletManager.wallet.blockchain + if (!blockchain.isPayIdSupported()) { + store.dispatch(PayIdVerification.Failed(payId, FailReason.PAY_ID_UNSUPPORTED_BY_BLOCKCHAIN)) + return + } -// val result = PayIdManager().getPayId(cardId, publicKey.toHexString()) -// withContext(Dispatchers.Main) { -// when (result) { -// is Result.Success -> { -// val payId = result.data -// if (payId == null) { -// store.dispatch(WalletAction.LoadPayId.NotCreated) -// } else { -// store.dispatch(WalletAction.LoadPayId.Success(payId)) -// } -// } -// is Result.Failure -> store.dispatch(WalletAction.LoadPayId.Failure) -// } -// } - return null + scope.launch { + val result = PayIdManager().verifyPayId(payId, blockchain) + withContext(Dispatchers.Main) { + when (result) { + is Result.Success -> { + val address = result.data.getAddress() + if (address == null) { + store.dispatch(PayIdVerification.Failed(payId, FailReason.PAY_ID_NOT_REGISTERED)) + return@withContext + } + val failReason = isValidBlockchainAddressAndNotTheSameAsWallet(walletManager.wallet, address) + + val actionToSend = if (failReason == FailReason.NONE) PayIdVerification.Success(payId, address) + else AddressVerification.Failed(payId, failReason) + store.dispatch(actionToSend) + } + is Result.Failure -> { + store.dispatch(PayIdVerification.Failed(payId, FailReason.PAY_ID_REQUEST_FAILED)) + } + } + + } + } } - private suspend fun verifyWalletAddress(payID: String?): Boolean { - val isRealAddress = true - return isRealAddress + private fun verifyAddress(walletManager: WalletManager, supposedAddress: String) { + val failReason = isValidBlockchainAddressAndNotTheSameAsWallet(walletManager.wallet, supposedAddress) + val actionToSend = if (failReason == FailReason.NONE) AddressVerification.Success(supposedAddress) + else AddressVerification.Failed(supposedAddress, failReason) + + store.dispatch(actionToSend) + } + + private fun isValidBlockchainAddressAndNotTheSameAsWallet(wallet: Wallet, address: String): FailReason { + return if (wallet.blockchain.validateAddress(address)) { + if (wallet.address != address) { + FailReason.NONE + } else { + FailReason.ADDRESS_SAME_AS_WALLET + } + } else { + FailReason.ADDRESS_INVALID_OR_UNSUPPORTED_BY_BLOCKCHAIN + } + } + + //TODO: move to the blockchainSDK + private fun extractAddressFromShareUri(shareUri: String): String { + val sharePrefix = listOf("bitcoin:", "ethereum:", "ripple:") + val prefixes = sharePrefix.filter { shareUri.contains(it) } + return if (prefixes.isEmpty()) shareUri + else shareUri.replace(prefixes[0], "") } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/SendReducer.kt b/app/src/main/java/com/tangem/tap/features/send/redux/SendReducer.kt index 67a2f55308..27f36ed995 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/SendReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/SendReducer.kt @@ -1,7 +1,6 @@ package com.tangem.tap.features.send.redux import android.view.View -import com.tangem.tap.features.send.redux.AddressPayIdActionUI.SetAddressOrPayId import com.tangem.tap.features.send.redux.FeeActionUI.* import org.rekotlin.Action import org.rekotlin.StateType @@ -16,32 +15,47 @@ class SendReducer { } } -private fun internalReduce(action: Action, sendState: SendState): SendState { - if (action is ReleaseSendState) return SendState() - val sendAction = action as? SendScreenAction ?: return sendState +private fun internalReduce(incomingAction: Action, sendState: SendState): SendState { + if (incomingAction is ReleaseSendState) return SendState() + val action = incomingAction as? SendScreenAction ?: return sendState - return when (sendAction) { - is AddressPayIdActionUI -> handleAddressPayIdAction(sendAction, sendState, sendState.addressPayIDState) - is FeeActionUI -> handleFeeLayoutAction(sendAction, sendState, sendState.feeLayoutState) + return when (action) { + is FeeActionUI -> handleFeeLayoutAction(action, sendState, sendState.feeLayoutState) +// is SetAddressOrPayId -> handleAddressPayIdUIAction(action, sendState, sendState.addressPayIDState) + is AddressPayIdVerifyAction -> handleAddressPayIdAction(action, sendState, sendState.addressPayIDState) else -> sendState } } private fun handleAddressPayIdAction( - action: AddressPayIdActionUI, + action: AddressPayIdVerifyAction, sendState: SendState, state: AddressPayIDState ): SendState { - var state = state - - when (action) { - is SetAddressOrPayId -> { - state = state.copy(value = action.data?.toString()) - return updateLastState(sendState.copy(addressPayIDState = state), state) + val result: AddressPayIDState? = when (action) { + is AddressPayIdVerifyAction.PayIdVerification.Success -> { + state.copyPayIdWalletAddress(action.payId, action.payIdWalletAddress) + } + is AddressPayIdVerifyAction.PayIdVerification.Failed -> { + state.copyPaiIdError(action.payId, action.reason) + } + is AddressPayIdVerifyAction.AddressVerification.Success -> { + state.copyWalletAddress(action.address) + } + is AddressPayIdVerifyAction.AddressVerification.Failed -> { + state.copyError(action.address, action.reason) + } + is AddressPayIdVerifyAction.ProcessedButNotVerifiedAddressPayId -> { + state.copy(etFieldValue = action.data) } } + return if (result == null) { + Timber.e("AddressPayIDState didn't modified.") + sendState + } else { + updateLastState(sendState.copy(addressPayIDState = result), result) + } - return sendState } private fun handleFeeLayoutAction(action: FeeActionUI, sendState: SendState, state: FeeLayoutState): SendState { @@ -64,8 +78,8 @@ private fun handleFeeLayoutAction(action: FeeActionUI, sendState: SendState, sta } } -private fun updateLastState(sendState: SendState, state: StateType): SendState { - val sendState = sendState.copy(lastChangedStateType = state) +private fun updateLastState(sendState: SendState, lastChangedState: StateType): SendState { + val sendState = sendState.copy(lastChangedStateType = lastChangedState) Timber.d("$sendState") return sendState } 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 6e0075913b..e37b243aa2 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 @@ -16,14 +16,31 @@ sealed class FeeActionUI : SendScreenActionUI { class ChangeIncludeFee(val isChecked: Boolean) : FeeActionUI() } +// shortness AddressOrPayId = APid sealed class AddressPayIdActionUI : SendScreenActionUI { - data class SetAddressOrPayId(val data: CharSequence?) : AddressPayIdActionUI() + data class SetAddressOrPayId(val data: String) : AddressPayIdActionUI() } -sealed class AddressPayIdAction : SendScreenAction { - object Verification : AddressPayIdAction() { - object PayIdNotSupportedByBlockchain : AddressPayIdAction() - object Failed : AddressPayIdAction() - data class Success(val payIdWalletAddress: String) : AddressPayIdAction() +sealed class AddressPayIdVerifyAction : SendScreenAction { + enum class FailReason { + NONE, + IS_NOT_PAY_ID, + 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 ProcessedButNotVerifiedAddressPayId(val data: String) : AddressPayIdVerifyAction() + + sealed class PayIdVerification : AddressPayIdVerifyAction() { + data class Failed(val payId: String, val reason: FailReason) : PayIdVerification() + data class Success(val payId: String, val payIdWalletAddress: String) : PayIdVerification() + } + + sealed class AddressVerification : AddressPayIdVerifyAction() { + data class Failed(val address: String, val reason: FailReason) : AddressVerification() + data class Success(val address: String) : AddressVerification() } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/SendState.kt b/app/src/main/java/com/tangem/tap/features/send/redux/SendState.kt index 70036db005..221290b937 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/SendState.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/SendState.kt @@ -18,10 +18,28 @@ data class SendState( class NoneState : StateType data class AddressPayIDState( - val value: String? = null, - val payIDWalletAddress: String? = null, - val error: String? = null, -) : StateType + val etFieldValue: String? = null, + val walletAddress: String? = null, + val error: AddressPayIdVerifyAction.FailReason? = null, +) : StateType { + + fun isPayIdState():Boolean = walletAddress != null && walletAddress != etFieldValue + + fun copyWalletAddress(address: String): AddressPayIDState { + return this.copy(etFieldValue = address, walletAddress = address, error = null) + } + fun copyError(address: String, error: AddressPayIdVerifyAction.FailReason): AddressPayIDState { + return this.copy(etFieldValue = address, error = error, walletAddress = null) + } + + fun copyPayIdWalletAddress(payId: String, address: String): AddressPayIDState { + return this.copy(etFieldValue = payId, walletAddress = address, error = null) + } + + fun copyPaiIdError(payId: String, error: AddressPayIdVerifyAction.FailReason): AddressPayIDState { + return this.copy(etFieldValue = payId, error = error, walletAddress = null) + } +} data class FeeLayoutState( val visibility: Int = View.GONE, 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 1879d895a8..94e0c1bc34 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 @@ -3,6 +3,8 @@ package com.tangem.tap.features.send.ui import android.content.Intent import android.os.Bundle import android.view.View +import android.widget.EditText +import androidx.core.widget.addTextChangedListener import com.tangem.tap.common.extensions.getFromClipboard import com.tangem.tap.common.qrCodeScan.ScanQrCodeActivity import com.tangem.tap.features.send.BaseStoreFragment @@ -11,11 +13,17 @@ import com.tangem.tap.features.send.redux.FeeActionUI.* import com.tangem.tap.features.send.redux.ReleaseSendState import com.tangem.tap.features.send.ui.stateSubscribers.SendStateSubscriber import com.tangem.tap.features.send.ui.stateSubscribers.WalletStateSubscriber +import com.tangem.tap.mainScope import com.tangem.tap.store import com.tangem.wallet.R import kotlinx.android.synthetic.main.btn_paste.* import kotlinx.android.synthetic.main.btn_qr_code.* +import kotlinx.android.synthetic.main.layout_send_address_payid.* import kotlinx.android.synthetic.main.layout_send_network_fee.* +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.flow.* + /** [REDACTED_AUTHOR] @@ -27,7 +35,6 @@ class SendFragment : BaseStoreFragment(R.layout.fragment_send) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - flExpandCollapse.setOnClickListener { store.dispatch(ToggleFeeLayoutVisibility) } @@ -38,8 +45,14 @@ class SendFragment : BaseStoreFragment(R.layout.fragment_send) { store.dispatch(ChangeIncludeFee(isChecked)) } + etAddressOrPayId.inputedTextAsFlow() + .debounce(400) + .filter { store.state.sendState.addressPayIDState.etFieldValue != it } + .onEach { store.dispatch(SetAddressOrPayId(it)) } + .launchIn(mainScope) + imvPaste.setOnClickListener { - store.dispatch(SetAddressOrPayId(requireContext().getFromClipboard())) + store.dispatch(SetAddressOrPayId(requireContext().getFromClipboard()?.toString() ?: "")) } imvQrCode.setOnClickListener { requireActivity().startActivity(Intent(requireContext(), ScanQrCodeActivity::class.java)) @@ -64,5 +77,11 @@ class SendFragment : BaseStoreFragment(R.layout.fragment_send) { } } +@ExperimentalCoroutinesApi +fun EditText.inputedTextAsFlow(): Flow = callbackFlow { + val watcher = addTextChangedListener { editable -> offer(editable?.toString() ?: "") } + awaitClose { removeTextChangedListener(watcher) } +} + 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 d86fe32991..12e4deee26 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 @@ -1,11 +1,14 @@ package com.tangem.tap.features.send.ui.stateSubscribers +import android.content.Context import android.view.ViewGroup import androidx.fragment.app.Fragment import androidx.transition.TransitionManager import com.tangem.tap.features.send.redux.AddressPayIDState +import com.tangem.tap.features.send.redux.AddressPayIdVerifyAction.FailReason import com.tangem.tap.features.send.redux.FeeLayoutState import com.tangem.tap.features.send.redux.SendState +import com.tangem.wallet.R import kotlinx.android.synthetic.main.btn_expand_collapse.* import kotlinx.android.synthetic.main.layout_send_address_payid.* import kotlinx.android.synthetic.main.layout_send_network_fee.* @@ -23,7 +26,29 @@ class SendStateSubscriber(fragment: Fragment) : FragmentStateSubscriber R.string.error_payid_verification_failed + FailReason.PAY_ID_UNSUPPORTED_BY_BLOCKCHAIN -> R.string.error_payid_unsupported_by_blockchain + FailReason.PAY_ID_NOT_REGISTERED -> R.string.error_payid_not_registere + FailReason.PAY_ID_REQUEST_FAILED -> R.string.error_payid_request_failed + FailReason.ADDRESS_INVALID_OR_UNSUPPORTED_BY_BLOCKCHAIN -> R.string.error_address_invalid_or_unsupported + FailReason.ADDRESS_SAME_AS_WALLET -> R.string.error_address_same_as_wallet + else -> null + } + return if (resId == null) null + else context.getString(resId) + } + + val parsedError = parseError(fg.tilAddressOrPayId.context, state.error) + fg.tilAddressOrPayId.error = parsedError + fg.tilAddressOrPayId.isErrorEnabled = parsedError != null + fg.tilAddressOrPayId.helperText = state.walletAddress + fg.tilAddressOrPayId.isHelperTextEnabled = state.isPayIdState() && parsedError == null + if (fg.etAddressOrPayId.text?.toString() != state.etFieldValue) { + fg.etAddressOrPayId.setText(state.etFieldValue) + if (fg.etAddressOrPayId.isFocused) fg.etAddressOrPayId.setSelection(state.etFieldValue?.length ?: 0) + } } private fun handleFeeLayoutState(fg: Fragment, layoutState: FeeLayoutState) { 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 new file mode 100644 index 0000000000..e026dd8b11 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/network/payid/PayIdVerifyApi.kt @@ -0,0 +1,44 @@ +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 getAddress(): String? { + return if (addresses.isEmpty()) null + else addresses[0].addressDetails.address + } +} + +@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 new file mode 100644 index 0000000000..109dc5e97f --- /dev/null +++ b/app/src/main/java/com/tangem/tap/network/payid/PayIdVerifyService.kt @@ -0,0 +1,21 @@ +package com.tangem.tap.network.payid + +import com.tangem.commands.common.network.Result +import com.tangem.commands.common.network.performRequest +import com.tangem.tap.network.createRetrofitInstance + +/** +[REDACTED_AUTHOR] + */ +class PayIdVerifyService( + private val baseUrl: String +) { + + private val api = createRetrofitInstance(baseUrl).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/layout_send_address_payid.xml b/app/src/main/res/layout/layout_send_address_payid.xml index 3f48a6131d..a399a74119 100644 --- a/app/src/main/res/layout/layout_send_address_payid.xml +++ b/app/src/main/res/layout/layout_send_address_payid.xml @@ -45,7 +45,7 @@ layout="@layout/btn_qr_code" android:layout_width="wrap_content" android:layout_height="wrap_content" - app:layout_constraintBottom_toBottomOf="@+id/tilAddressOrPayId" + android:layout_marginTop="16dp" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toTopOf="@+id/tilAddressOrPayId" /> diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 9f30e32ccb..283438efc0 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -33,6 +33,13 @@ This PayID already exists. Try a different one. Error response while creating PayID. + PayID verification failed + PayID unsupported by blockchain + PayID not registered + PayID request failed + Address is invalid or unsupported by blockchain + Address is invalid or unsupported by blockchain + Send Address or PayID From a19f530fd8edc1e8dae57e6c6b4b97d688243ea5 Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 4 Sep 2020 20:25:31 +0300 Subject: [PATCH 3/6] Updated on 2026-08-14 --- app/src/main/res/layout/fragment_send.xml | 2 +- app/src/main/res/layout/layout_send_currency.xml | 1 + app/src/main/res/values/strings.xml | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/app/src/main/res/layout/fragment_send.xml b/app/src/main/res/layout/fragment_send.xml index 0768fc3cf5..6abb666dc5 100644 --- a/app/src/main/res/layout/fragment_send.xml +++ b/app/src/main/res/layout/fragment_send.xml @@ -34,7 +34,7 @@ layout="@layout/layout_send_currency" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginTop="8dp" /> + android:layout_marginTop="4dp" /> diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 283438efc0..e333a580a8 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -38,7 +38,7 @@ PayID not registered PayID request failed Address is invalid or unsupported by blockchain - Address is invalid or unsupported by blockchain + Address is the same as wallet address Send From 1d1c641e6ba658e185a09e4df46d9dd8bacd0f54 Mon Sep 17 00:00:00 2001 From: Tangem Date: Sun, 6 Sep 2020 16:50:14 +0300 Subject: [PATCH 4/6] Updated on 2026-08-14 --- app/build.gradle | 2 +- .../com/tangem/tap/common/extensions/Int.kt | 3 + .../common/qrCodeScan/ScanQrCodeActivity.kt | 10 +- .../tangem/tap/common/redux/LogMiddleware.kt | 2 +- .../com/tangem/tap/common/text/Truncate.kt | 136 ++++++++++++++++++ .../tap/features/send/redux/SendMiddleware.kt | 3 +- .../tap/features/send/redux/SendReducer.kt | 87 ++++++----- .../features/send/redux/SendScreenAction.kt | 4 +- .../tap/features/send/redux/SendState.kt | 42 +++++- .../tap/features/send/ui/SendFragment.kt | 63 +++++--- .../stateSubscribers/SendStateSubscriber.kt | 27 ++-- .../stateSubscribers/WalletStateSubscriber.kt | 13 -- .../res/layout/layout_send_address_payid.xml | 11 +- 13 files changed, 295 insertions(+), 108 deletions(-) create mode 100644 app/src/main/java/com/tangem/tap/common/extensions/Int.kt create mode 100644 app/src/main/java/com/tangem/tap/common/text/Truncate.kt delete mode 100644 app/src/main/java/com/tangem/tap/features/send/ui/stateSubscribers/WalletStateSubscriber.kt diff --git a/app/build.gradle b/app/build.gradle index 6dba2b1d60..89fc092c4a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -61,7 +61,7 @@ dependencies { implementation 'androidx.core:core-ktx:1.3.1' implementation 'androidx.appcompat:appcompat:1.2.0' implementation 'androidx.constraintlayout:constraintlayout:2.0.1' - implementation 'com.google.android.material:material:1.2.0' + implementation 'com.google.android.material:material:1.2.1' coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.0.10' implementation 'com.tangem:blockchain:1.27.0' diff --git a/app/src/main/java/com/tangem/tap/common/extensions/Int.kt b/app/src/main/java/com/tangem/tap/common/extensions/Int.kt new file mode 100644 index 0000000000..555dc252bd --- /dev/null +++ b/app/src/main/java/com/tangem/tap/common/extensions/Int.kt @@ -0,0 +1,3 @@ +package com.tangem.tap.common.extensions + +fun Int.isEven() = this and 1 == 0 \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/qrCodeScan/ScanQrCodeActivity.kt b/app/src/main/java/com/tangem/tap/common/qrCodeScan/ScanQrCodeActivity.kt index b350c9ebe4..da69eed9ae 100644 --- a/app/src/main/java/com/tangem/tap/common/qrCodeScan/ScanQrCodeActivity.kt +++ b/app/src/main/java/com/tangem/tap/common/qrCodeScan/ScanQrCodeActivity.kt @@ -1,6 +1,7 @@ package com.tangem.tap.common.qrCodeScan import android.Manifest +import android.content.Intent import android.content.pm.PackageManager import android.os.Build import android.os.Bundle @@ -9,14 +10,17 @@ import androidx.core.app.ActivityCompat import androidx.core.content.ContextCompat import com.google.zxing.Result import com.otaliastudios.cameraview.CameraView.PERMISSION_REQUEST_CODE -import com.tangem.tap.features.send.redux.AddressPayIdActionUI -import com.tangem.tap.store import me.dm7.barcodescanner.zxing.ZXingScannerView /** [REDACTED_AUTHOR] */ class ScanQrCodeActivity : AppCompatActivity(), ZXingScannerView.ResultHandler { + companion object { + val SCAN_QR_REQUEST_CODE = 1001 + val SCAN_RESULT = "scanResult" + } + private lateinit var mScannerView: ZXingScannerView override fun onCreate(state: Bundle?) { @@ -40,7 +44,7 @@ class ScanQrCodeActivity : AppCompatActivity(), ZXingScannerView.ResultHandler { } override fun handleResult(result: Result) { - store.dispatch(AddressPayIdActionUI.SetAddressOrPayId(result.text)) + setResult(SCAN_QR_REQUEST_CODE, Intent().apply { putExtra(SCAN_RESULT, result.text) }) finish() } diff --git a/app/src/main/java/com/tangem/tap/common/redux/LogMiddleware.kt b/app/src/main/java/com/tangem/tap/common/redux/LogMiddleware.kt index 9e7276f9bd..15ea778fed 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/LogMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/LogMiddleware.kt @@ -9,7 +9,7 @@ import timber.log.Timber val logMiddleware: Middleware = { dispatch, appState -> { nextDispatch -> { action -> - Timber.d("$action") + Timber.d("Dispatch action: $action") nextDispatch(action) } } diff --git a/app/src/main/java/com/tangem/tap/common/text/Truncate.kt b/app/src/main/java/com/tangem/tap/common/text/Truncate.kt new file mode 100644 index 0000000000..3ed50ca1d2 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/common/text/Truncate.kt @@ -0,0 +1,136 @@ +package com.tangem.tap.common.text + +import android.widget.TextView +import com.tangem.tap.common.extensions.isEven + +/** +[REDACTED_AUTHOR] + */ +enum class TruncateType { + START, MIDDLE, END +} + +interface Truncate { + fun apply(tv: TextView, text: String, with: String): String + + companion object { + fun create(type: TruncateType): Truncate { + return when (type) { + TruncateType.START -> TruncateStart() + TruncateType.MIDDLE -> TruncateMiddle() + TruncateType.END -> TruncateEnd() + } + } + } +} + +abstract class BaseTruncate : Truncate { + protected var hasBeenTruncated = false + + override fun apply(tv: TextView, text: String, with: String): String { + val roughLength = getRoughFitLength(tv, text, with) + val fittedText = preciseFitting(tv, roughTruncate(text, roughLength), with) + return if (hasBeenTruncated) attachWith(fittedText, with) else fittedText + } + + protected fun getRoughFitLength(tv: TextView, text: String, with: String): Int { + val existingSpace = tv.measuredWidth - (tv.paddingStart + tv.paddingEnd) + val textWillTakeSpace = tv.paint.measureText(text) + val overSizeRatio: Float = textWillTakeSpace / existingSpace + + val maxLengthOfText = text.length / overSizeRatio + if (text.length <= maxLengthOfText) return text.length + + return maxLengthOfText.toInt() + } + + protected fun preciseFitting(tv: TextView, text: String, with: String): String { + if (!hasBeenTruncated) return text + + val spaceForText = tv.measuredWidth - (tv.paddingStart + tv.paddingEnd) + + var fittedText = text + while (tv.paint.measureText(fittedText + with) > spaceForText) { + fittedText = preciseTruncate(fittedText) + } + return fittedText + } + + protected abstract fun roughTruncate(text: String, residualLength: Int): String + protected abstract fun preciseTruncate(text: String): String + protected abstract fun attachWith(text: String, with: String): String +} + +class TruncateStart : BaseTruncate() { + override fun roughTruncate(text: String, residualLength: Int): String { + if (text.length <= residualLength) return text + + hasBeenTruncated = true + return text.substring(residualLength, text.length) + } + + override fun preciseTruncate(text: String): String = text.substring(1, text.length) + + override fun attachWith(text: String, with: String): String = with + text +} + +class TruncateMiddle : BaseTruncate() { + + override fun roughTruncate(text: String, residualLength: Int): String { + if (text.length <= residualLength) return text + + hasBeenTruncated = true + val halfOfResidualLength = residualLength / 2 + val leftSide = text.substring(0, halfOfResidualLength) + val rightSide = text.substring(text.length - halfOfResidualLength, text.length) + + return leftSide + rightSide + } + + override fun preciseTruncate(text: String): String { + val middlePosition = text.length / 2 + return if (text.length.isEven()) { + val leftSide = text.substring(0, middlePosition - 1) + val rightSide = text.substring(middlePosition, text.length) + leftSide + rightSide + } else { + val leftSide = text.substring(0, middlePosition) + val rightSide = text.substring(middlePosition + 1, text.length) + leftSide + rightSide + } + } + + override fun attachWith(text: String, with: String): String { + val cuttingPosition = text.length / 2 + val leftSide = text.substring(0, cuttingPosition) + val rightSide = text.substring(cuttingPosition, text.length) + return leftSide + with + rightSide + } +} + +class TruncateEnd : BaseTruncate() { + override fun roughTruncate(text: String, residualLength: Int): String { + if (text.length <= residualLength) return text + + hasBeenTruncated = true + return text.substring(0, residualLength) + } + + override fun preciseTruncate(text: String): String = text.substring(0, text.length - 1) + + override fun attachWith(text: String, with: String): String = text + with +} + +fun TextView.truncateWith(text: String, type: TruncateType, with: String = "..."): String { + val truncate = Truncate.create(type) + return truncate.apply(this, text, with) +} + +fun TextView.truncateStartWith(text: String, with: String = "..."): String = + this.truncateWith(text, TruncateType.START, with) + +fun TextView.truncateMiddleWith(text: String, with: String = "..."): String = + this.truncateWith(text, TruncateType.MIDDLE, with) + +fun TextView.truncateEndWith(text: String, with: String = "..."): String = + this.truncateWith(text, TruncateType.END, with) \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/SendMiddleware.kt b/app/src/main/java/com/tangem/tap/features/send/redux/SendMiddleware.kt index 604d7c7a22..f2f52398e3 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/SendMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/SendMiddleware.kt @@ -44,14 +44,13 @@ private fun handleSendAction(action: Action) { internal class AddressPayIdHandler { fun handle(data: String) { val walletManager = store.state.globalState.walletManager ?: return + if (data == store.state.sendState.addressPayIDState.etFieldValue) return if (isPayId(data)) { verifyPayId(data, walletManager) - store.dispatch(ProcessedButNotVerifiedAddressPayId(data)) } else { val supposedAddress = extractAddressFromShareUri(data) store.dispatch(PayIdVerification.Failed(supposedAddress, FailReason.IS_NOT_PAY_ID)) - store.dispatch(ProcessedButNotVerifiedAddressPayId(supposedAddress)) verifyAddress(walletManager, supposedAddress) } } diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/SendReducer.kt b/app/src/main/java/com/tangem/tap/features/send/redux/SendReducer.kt index 27f36ed995..dc9e216301 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/SendReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/SendReducer.kt @@ -1,6 +1,9 @@ package com.tangem.tap.features.send.redux import android.view.View +import com.tangem.tap.features.send.redux.AddressPayIdActionUI.* +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.FeeActionUI.* import org.rekotlin.Action import org.rekotlin.StateType @@ -11,7 +14,12 @@ import timber.log.Timber */ class SendReducer { companion object { - fun reduce(action: Action, sendState: SendState): SendState = internalReduce(action, sendState) + fun reduce(action: Action, sendState: SendState): SendState { + val newState = internalReduce(action, sendState) + if (newState == sendState) Timber.i("state didn't modified.") + else Timber.i("state was updated to: $newState.") + return newState + } } } @@ -20,66 +28,53 @@ private fun internalReduce(incomingAction: Action, sendState: SendState): SendSt val action = incomingAction as? SendScreenAction ?: return sendState return when (action) { - is FeeActionUI -> handleFeeLayoutAction(action, sendState, sendState.feeLayoutState) -// is SetAddressOrPayId -> handleAddressPayIdUIAction(action, sendState, sendState.addressPayIDState) + is AddressPayIdActionUI -> handleAddressPayIdActionUI(action, sendState, sendState.addressPayIDState) is AddressPayIdVerifyAction -> handleAddressPayIdAction(action, sendState, sendState.addressPayIDState) + is FeeActionUI -> handleFeeActionUI(action, sendState, sendState.feeLayoutState) else -> sendState } } +fun handleAddressPayIdActionUI( + action: AddressPayIdActionUI, + sendState: SendState, + state: AddressPayIDState +): SendState { + val result = when (action) { + is SetAddressOrPayId -> state + is SetTruncateHandler -> state.copy(truncateHandler = action.handler) + is TruncateOrRestore -> { + if(action.truncate) state.copy(etFieldValue = state.truncatedFieldValue) + else state.copy(etFieldValue = state.normalFieldValue) + } + } + return updateLastState(sendState.copy(addressPayIDState = result), result) +} + private fun handleAddressPayIdAction( action: AddressPayIdVerifyAction, sendState: SendState, state: AddressPayIDState ): SendState { - val result: AddressPayIDState? = when (action) { - is AddressPayIdVerifyAction.PayIdVerification.Success -> { - state.copyPayIdWalletAddress(action.payId, action.payIdWalletAddress) - } - is AddressPayIdVerifyAction.PayIdVerification.Failed -> { - state.copyPaiIdError(action.payId, action.reason) - } - is AddressPayIdVerifyAction.AddressVerification.Success -> { - state.copyWalletAddress(action.address) - } - is AddressPayIdVerifyAction.AddressVerification.Failed -> { - state.copyError(action.address, action.reason) - } - is AddressPayIdVerifyAction.ProcessedButNotVerifiedAddressPayId -> { - state.copy(etFieldValue = action.data) - } + val result = when (action) { + is PayIdVerification.Success -> state.copyPayIdWalletAddress(action.payId, action.payIdWalletAddress) + is PayIdVerification.Failed -> state.copyPaiIdError(action.payId, action.reason) + is AddressVerification.Success -> state.copyWalletAddress(action.address) + is AddressVerification.Failed -> state.copyError(action.address, action.reason) } - return if (result == null) { - Timber.e("AddressPayIDState didn't modified.") - sendState - } else { - updateLastState(sendState.copy(addressPayIDState = result), result) - } - + return updateLastState(sendState.copy(addressPayIDState = result), result) } -private fun handleFeeLayoutAction(action: FeeActionUI, sendState: SendState, state: FeeLayoutState): SendState { - return when (action) { +private fun handleFeeActionUI(action: FeeActionUI, sendState: SendState, state: FeeLayoutState): SendState { + val result = when (action) { is ToggleFeeLayoutVisibility -> { - val visibility = if (state.visibility == View.VISIBLE) View.GONE - else View.VISIBLE - - val result = state.copy(visibility = visibility) - updateLastState(sendState.copy(feeLayoutState = result), result) - } - is ChangeSelectedFee -> { - val result = state.copy(selectedFeeId = action.id) - updateLastState(sendState.copy(feeLayoutState = result), result) - } - is ChangeIncludeFee -> { - val result = state.copy(includeFeeIsChecked = action.isChecked) - updateLastState(sendState.copy(feeLayoutState = result), result) + state.copy(visibility = if (state.visibility == View.VISIBLE) View.GONE else View.VISIBLE) } + is ChangeSelectedFee -> state.copy(selectedFeeId = action.id) + is ChangeIncludeFee -> state.copy(includeFeeIsChecked = action.isChecked) } + return updateLastState(sendState.copy(feeLayoutState = result), result) } -private fun updateLastState(sendState: SendState, lastChangedState: StateType): SendState { - val sendState = sendState.copy(lastChangedStateType = lastChangedState) - Timber.d("$sendState") - return sendState -} +private fun updateLastState(sendState: SendState, lastChangedState: StateType): SendState = + sendState.copy(lastChangedStateType = lastChangedState) 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 e37b243aa2..3ea66b87d5 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 @@ -19,6 +19,8 @@ sealed class FeeActionUI : SendScreenActionUI { // shortness AddressOrPayId = APid sealed class AddressPayIdActionUI : SendScreenActionUI { data class SetAddressOrPayId(val data: String) : AddressPayIdActionUI() + data class SetTruncateHandler(val handler: (String) -> String) : AddressPayIdActionUI() + data class TruncateOrRestore(val truncate: Boolean) : AddressPayIdActionUI() } sealed class AddressPayIdVerifyAction : SendScreenAction { @@ -32,8 +34,6 @@ sealed class AddressPayIdVerifyAction : SendScreenAction { ADDRESS_SAME_AS_WALLET } - data class ProcessedButNotVerifiedAddressPayId(val data: String) : AddressPayIdVerifyAction() - sealed class PayIdVerification : AddressPayIdVerifyAction() { data class Failed(val payId: String, val reason: FailReason) : PayIdVerification() data class Success(val payId: String, val payIdWalletAddress: String) : PayIdVerification() diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/SendState.kt b/app/src/main/java/com/tangem/tap/features/send/redux/SendState.kt index 221290b937..aefebf34ba 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/SendState.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/SendState.kt @@ -19,25 +19,57 @@ class NoneState : StateType data class AddressPayIDState( val etFieldValue: String? = null, + val normalFieldValue: String? = null, + val truncatedFieldValue: String? = null, val walletAddress: String? = null, val error: AddressPayIdVerifyAction.FailReason? = null, + val truncateHandler: ((String) -> String)? = null ) : StateType { - fun isPayIdState():Boolean = walletAddress != null && walletAddress != etFieldValue + fun isPayIdState(): Boolean = walletAddress != null && walletAddress != normalFieldValue fun copyWalletAddress(address: String): AddressPayIDState { - return this.copy(etFieldValue = address, walletAddress = address, error = null) + val truncated = truncateHandler?.invoke(address) ?: address + return this.copy( + etFieldValue = address, + normalFieldValue = address, + truncatedFieldValue = truncated, + walletAddress = address, + error = null + ) } + fun copyError(address: String, error: AddressPayIdVerifyAction.FailReason): AddressPayIDState { - return this.copy(etFieldValue = address, error = error, walletAddress = null) + val truncated = truncateHandler?.invoke(address) ?: address + return this.copy( + etFieldValue = address, + normalFieldValue = address, + truncatedFieldValue = truncated, + error = error, + walletAddress = null + ) } fun copyPayIdWalletAddress(payId: String, address: String): AddressPayIDState { - return this.copy(etFieldValue = payId, walletAddress = address, error = null) + val truncated = truncateHandler?.invoke(address) ?: address + return this.copy( + etFieldValue = payId, + normalFieldValue = payId, + truncatedFieldValue = truncated, + walletAddress = address, + error = null + ) } fun copyPaiIdError(payId: String, error: AddressPayIdVerifyAction.FailReason): AddressPayIDState { - return this.copy(etFieldValue = payId, error = error, walletAddress = null) + val truncated = truncateHandler?.invoke(payId) ?: payId + return this.copy( + etFieldValue = payId, + normalFieldValue = payId, + truncatedFieldValue = truncated, + error = error, + walletAddress = null + ) } } 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 94e0c1bc34..81d48ba2fc 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 @@ -7,12 +7,12 @@ import android.widget.EditText import androidx.core.widget.addTextChangedListener import com.tangem.tap.common.extensions.getFromClipboard import com.tangem.tap.common.qrCodeScan.ScanQrCodeActivity +import com.tangem.tap.common.text.truncateMiddleWith import com.tangem.tap.features.send.BaseStoreFragment -import com.tangem.tap.features.send.redux.AddressPayIdActionUI.SetAddressOrPayId +import com.tangem.tap.features.send.redux.AddressPayIdActionUI.* import com.tangem.tap.features.send.redux.FeeActionUI.* import com.tangem.tap.features.send.redux.ReleaseSendState import com.tangem.tap.features.send.ui.stateSubscribers.SendStateSubscriber -import com.tangem.tap.features.send.ui.stateSubscribers.WalletStateSubscriber import com.tangem.tap.mainScope import com.tangem.tap.store import com.tangem.wallet.R @@ -24,17 +24,53 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.* - /** [REDACTED_AUTHOR] */ class SendFragment : BaseStoreFragment(R.layout.fragment_send) { private val sendSubscriber = SendStateSubscriber(this) - private val walletSubscriber = WalletStateSubscriber(this) override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + + setupAddressOrPayIdLayout() + setupFeeLayout() + } + + private fun setupAddressOrPayIdLayout() { + store.dispatch(SetTruncateHandler { etAddressOrPayId.truncateMiddleWith(it, " *** ") }) + + etAddressOrPayId.setOnFocusChangeListener { v, hasFocus -> + store.dispatch(TruncateOrRestore(!hasFocus)) + } + etAddressOrPayId.inputedTextAsFlow() + .debounce(400) + .filter { store.state.sendState.addressPayIDState.etFieldValue != it } + .onEach { store.dispatch(SetAddressOrPayId(it)) } + .launchIn(mainScope) + + imvPaste.setOnClickListener { + store.dispatch(SetAddressOrPayId(requireContext().getFromClipboard()?.toString() ?: "")) + store.dispatch(TruncateOrRestore(!etAddressOrPayId.isFocused)) + } + imvQrCode.setOnClickListener { + startActivityForResult( + Intent(requireContext(), ScanQrCodeActivity::class.java), + ScanQrCodeActivity.SCAN_QR_REQUEST_CODE + ) + } + } + + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + if (requestCode != ScanQrCodeActivity.SCAN_QR_REQUEST_CODE) return + + val scannedCode = data?.getStringExtra(ScanQrCodeActivity.SCAN_RESULT) ?: "" + store.dispatch(SetAddressOrPayId(scannedCode)) + store.dispatch(TruncateOrRestore(!etAddressOrPayId.isFocused)) + } + + private fun setupFeeLayout() { flExpandCollapse.setOnClickListener { store.dispatch(ToggleFeeLayoutVisibility) } @@ -44,30 +80,13 @@ class SendFragment : BaseStoreFragment(R.layout.fragment_send) { swIncludeFee.setOnCheckedChangeListener { btn, isChecked -> store.dispatch(ChangeIncludeFee(isChecked)) } - - etAddressOrPayId.inputedTextAsFlow() - .debounce(400) - .filter { store.state.sendState.addressPayIDState.etFieldValue != it } - .onEach { store.dispatch(SetAddressOrPayId(it)) } - .launchIn(mainScope) - - imvPaste.setOnClickListener { - store.dispatch(SetAddressOrPayId(requireContext().getFromClipboard()?.toString() ?: "")) - } - imvQrCode.setOnClickListener { - requireActivity().startActivity(Intent(requireContext(), ScanQrCodeActivity::class.java)) - } } override fun subscribeToStore() { - store.subscribe(walletSubscriber) { appState -> - appState.skipRepeats { oldState, newState -> false }.select { it.walletState } - } store.subscribe(sendSubscriber) { appState -> - appState.skipRepeats { oldState, newState -> false }.select { it.sendState } + appState.skipRepeats { oldState, newState -> oldState == newState }.select { it.sendState } } - storeSubscribersList.add(walletSubscriber) storeSubscribersList.add(sendSubscriber) } 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 12e4deee26..780a2a23aa 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 @@ -40,14 +40,25 @@ class SendStateSubscriber(fragment: Fragment) : FragmentStateSubscriber(fragment) { - override fun updateWithNewState(fg: Fragment, state: WalletState) { - - } -} \ No newline at end of file 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 a399a74119..5cb09462f0 100644 --- a/app/src/main/res/layout/layout_send_address_payid.xml +++ b/app/src/main/res/layout/layout_send_address_payid.xml @@ -2,17 +2,18 @@ + android:layout_height="wrap_content" + android:paddingStart="16dp" + android:paddingEnd="16dp"> @@ -23,7 +24,7 @@ android:layout_height="wrap_content" android:ellipsize="middle" android:paddingStart="0dp" - android:paddingEnd="96dp" + android:paddingEnd="98dp" android:singleLine="true" android:textSize="16sp" tools:text="139mrsJgyWnJ **** y9BV" /> From 6f341880381d9c5bb6ebcb2b50797da6bf17c1a7 Mon Sep 17 00:00:00 2001 From: Tangem Date: Sun, 6 Sep 2020 18:27:34 +0300 Subject: [PATCH 5/6] Updated on 2026-08-14 --- .../tap/features/send/redux/SendMiddleware.kt | 16 ++++++++-------- .../tap/features/send/redux/SendReducer.kt | 8 ++++---- .../tap/features/send/redux/SendScreenAction.kt | 8 ++++---- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/SendMiddleware.kt b/app/src/main/java/com/tangem/tap/features/send/redux/SendMiddleware.kt index f2f52398e3..d18531b7b2 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/SendMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/SendMiddleware.kt @@ -50,7 +50,7 @@ internal class AddressPayIdHandler { verifyPayId(data, walletManager) } else { val supposedAddress = extractAddressFromShareUri(data) - store.dispatch(PayIdVerification.Failed(supposedAddress, FailReason.IS_NOT_PAY_ID)) + store.dispatch(PayIdVerification.SetError(supposedAddress, FailReason.IS_NOT_PAY_ID)) verifyAddress(walletManager, supposedAddress) } } @@ -58,7 +58,7 @@ internal class AddressPayIdHandler { private fun verifyPayId(payId: String, walletManager: WalletManager) { val blockchain = walletManager.wallet.blockchain if (!blockchain.isPayIdSupported()) { - store.dispatch(PayIdVerification.Failed(payId, FailReason.PAY_ID_UNSUPPORTED_BY_BLOCKCHAIN)) + store.dispatch(PayIdVerification.SetError(payId, FailReason.PAY_ID_UNSUPPORTED_BY_BLOCKCHAIN)) return } @@ -69,17 +69,17 @@ internal class AddressPayIdHandler { is Result.Success -> { val address = result.data.getAddress() if (address == null) { - store.dispatch(PayIdVerification.Failed(payId, FailReason.PAY_ID_NOT_REGISTERED)) + store.dispatch(PayIdVerification.SetError(payId, FailReason.PAY_ID_NOT_REGISTERED)) return@withContext } val failReason = isValidBlockchainAddressAndNotTheSameAsWallet(walletManager.wallet, address) - val actionToSend = if (failReason == FailReason.NONE) PayIdVerification.Success(payId, address) - else AddressVerification.Failed(payId, failReason) + val actionToSend = if (failReason == FailReason.NONE) PayIdVerification.SetPayIdWalletAddress(payId, address) + else AddressVerification.SetError(payId, failReason) store.dispatch(actionToSend) } is Result.Failure -> { - store.dispatch(PayIdVerification.Failed(payId, FailReason.PAY_ID_REQUEST_FAILED)) + store.dispatch(PayIdVerification.SetError(payId, FailReason.PAY_ID_REQUEST_FAILED)) } } @@ -89,8 +89,8 @@ internal class AddressPayIdHandler { private fun verifyAddress(walletManager: WalletManager, supposedAddress: String) { val failReason = isValidBlockchainAddressAndNotTheSameAsWallet(walletManager.wallet, supposedAddress) - val actionToSend = if (failReason == FailReason.NONE) AddressVerification.Success(supposedAddress) - else AddressVerification.Failed(supposedAddress, failReason) + val actionToSend = if (failReason == FailReason.NONE) AddressVerification.SetWalletAddress(supposedAddress) + else AddressVerification.SetError(supposedAddress, failReason) store.dispatch(actionToSend) } diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/SendReducer.kt b/app/src/main/java/com/tangem/tap/features/send/redux/SendReducer.kt index dc9e216301..1d8b36f02a 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/SendReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/SendReducer.kt @@ -57,10 +57,10 @@ private fun handleAddressPayIdAction( state: AddressPayIDState ): SendState { val result = when (action) { - is PayIdVerification.Success -> state.copyPayIdWalletAddress(action.payId, action.payIdWalletAddress) - is PayIdVerification.Failed -> state.copyPaiIdError(action.payId, action.reason) - is AddressVerification.Success -> state.copyWalletAddress(action.address) - is AddressVerification.Failed -> state.copyError(action.address, action.reason) + is PayIdVerification.SetPayIdWalletAddress -> state.copyPayIdWalletAddress(action.payId, action.payIdWalletAddress) + is PayIdVerification.SetError -> state.copyPaiIdError(action.payId, action.reason) + is AddressVerification.SetWalletAddress -> state.copyWalletAddress(action.address) + is AddressVerification.SetError -> state.copyError(action.address, action.reason) } return updateLastState(sendState.copy(addressPayIDState = result), result) } 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 3ea66b87d5..300dc34a78 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 @@ -35,12 +35,12 @@ sealed class AddressPayIdVerifyAction : SendScreenAction { } sealed class PayIdVerification : AddressPayIdVerifyAction() { - data class Failed(val payId: String, val reason: FailReason) : PayIdVerification() - data class Success(val payId: String, val payIdWalletAddress: String) : PayIdVerification() + data class SetError(val payId: String, val reason: FailReason) : PayIdVerification() + data class SetPayIdWalletAddress(val payId: String, val payIdWalletAddress: String) : PayIdVerification() } sealed class AddressVerification : AddressPayIdVerifyAction() { - data class Failed(val address: String, val reason: FailReason) : AddressVerification() - data class Success(val address: String) : AddressVerification() + data class SetError(val address: String, val reason: FailReason) : AddressVerification() + data class SetWalletAddress(val address: String) : AddressVerification() } } \ No newline at end of file From b72d38021f17e19ebbeffe2de144484a2f43c261 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 7 Sep 2020 11:22:02 +0300 Subject: [PATCH 6/6] Updated on 2026-08-14 --- .../tap/features/send/redux/SendMiddleware.kt | 6 +-- .../tap/features/send/redux/SendReducer.kt | 24 ++++----- .../features/send/redux/SendScreenAction.kt | 16 +++--- .../tap/features/send/redux/SendState.kt | 12 ++--- .../tap/features/send/ui/SendFragment.kt | 6 +-- .../stateSubscribers/SendStateSubscriber.kt | 6 +-- app/src/main/res/anim/slide_in_up.xml | 7 +++ app/src/main/res/anim/slide_out_down.xml | 7 +++ app/src/main/res/layout/btn_arrow_up_down.xml | 1 - app/src/main/res/layout/fragment_send.xml | 2 +- ...nd_currency.xml => layout_send_amount.xml} | 49 ++++++++++++------- 11 files changed, 81 insertions(+), 55 deletions(-) create mode 100644 app/src/main/res/anim/slide_in_up.xml create mode 100644 app/src/main/res/anim/slide_out_down.xml rename app/src/main/res/layout/{layout_send_currency.xml => layout_send_amount.xml} (71%) diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/SendMiddleware.kt b/app/src/main/java/com/tangem/tap/features/send/redux/SendMiddleware.kt index d18531b7b2..9f07a09224 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/SendMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/SendMiddleware.kt @@ -7,7 +7,7 @@ import com.tangem.tap.common.redux.AppState import com.tangem.tap.domain.PayIdManager import com.tangem.tap.domain.PayIdManager.Companion.isPayId import com.tangem.tap.domain.isPayIdSupported -import com.tangem.tap.features.send.redux.AddressPayIdActionUI.SetAddressOrPayId +import com.tangem.tap.features.send.redux.AddressPayIdActionUi.SetAddressOrPayId import com.tangem.tap.features.send.redux.AddressPayIdVerifyAction.* import com.tangem.tap.scope import com.tangem.tap.store @@ -33,7 +33,7 @@ private fun handleSendAction(action: Action) { val sendAction = action as? SendScreenActionUI ?: return when (sendAction) { - is AddressPayIdActionUI -> { + is AddressPayIdActionUi -> { when (sendAction) { is SetAddressOrPayId -> AddressPayIdHandler().handle(sendAction.data) } @@ -44,7 +44,7 @@ private fun handleSendAction(action: Action) { internal class AddressPayIdHandler { fun handle(data: String) { val walletManager = store.state.globalState.walletManager ?: return - if (data == store.state.sendState.addressPayIDState.etFieldValue) return + if (data == store.state.sendState.addressPayIdState.etFieldValue) return if (isPayId(data)) { verifyPayId(data, walletManager) diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/SendReducer.kt b/app/src/main/java/com/tangem/tap/features/send/redux/SendReducer.kt index 1d8b36f02a..0ec3afea23 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/SendReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/SendReducer.kt @@ -1,10 +1,10 @@ package com.tangem.tap.features.send.redux import android.view.View -import com.tangem.tap.features.send.redux.AddressPayIdActionUI.* +import com.tangem.tap.features.send.redux.AddressPayIdActionUi.* 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.FeeActionUI.* +import com.tangem.tap.features.send.redux.FeeActionUi.* import org.rekotlin.Action import org.rekotlin.StateType import timber.log.Timber @@ -28,17 +28,17 @@ private fun internalReduce(incomingAction: Action, sendState: SendState): SendSt val action = incomingAction as? SendScreenAction ?: return sendState return when (action) { - is AddressPayIdActionUI -> handleAddressPayIdActionUI(action, sendState, sendState.addressPayIDState) - is AddressPayIdVerifyAction -> handleAddressPayIdAction(action, sendState, sendState.addressPayIDState) - is FeeActionUI -> handleFeeActionUI(action, sendState, sendState.feeLayoutState) + is AddressPayIdActionUi -> handleAddressPayIdActionUi(action, sendState, sendState.addressPayIdState) + is AddressPayIdVerifyAction -> handleAddressPayIdAction(action, sendState, sendState.addressPayIdState) + is FeeActionUi -> handleFeeActionUi(action, sendState, sendState.feeLayoutState) else -> sendState } } -fun handleAddressPayIdActionUI( - action: AddressPayIdActionUI, +fun handleAddressPayIdActionUi( + action: AddressPayIdActionUi, sendState: SendState, - state: AddressPayIDState + state: AddressPayIdState ): SendState { val result = when (action) { is SetAddressOrPayId -> state @@ -48,13 +48,13 @@ fun handleAddressPayIdActionUI( else state.copy(etFieldValue = state.normalFieldValue) } } - return updateLastState(sendState.copy(addressPayIDState = result), result) + return updateLastState(sendState.copy(addressPayIdState = result), result) } private fun handleAddressPayIdAction( action: AddressPayIdVerifyAction, sendState: SendState, - state: AddressPayIDState + state: AddressPayIdState ): SendState { val result = when (action) { is PayIdVerification.SetPayIdWalletAddress -> state.copyPayIdWalletAddress(action.payId, action.payIdWalletAddress) @@ -62,10 +62,10 @@ private fun handleAddressPayIdAction( is AddressVerification.SetWalletAddress -> state.copyWalletAddress(action.address) is AddressVerification.SetError -> state.copyError(action.address, action.reason) } - return updateLastState(sendState.copy(addressPayIDState = result), result) + return updateLastState(sendState.copy(addressPayIdState = result), result) } -private fun handleFeeActionUI(action: FeeActionUI, sendState: SendState, state: FeeLayoutState): SendState { +private fun handleFeeActionUi(action: FeeActionUi, sendState: SendState, state: FeeLayoutState): SendState { val result = when (action) { is ToggleFeeLayoutVisibility -> { state.copy(visibility = if (state.visibility == View.VISIBLE) View.GONE else View.VISIBLE) 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 300dc34a78..d6f5950ea7 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 @@ -10,17 +10,17 @@ interface SendScreenActionUI : SendScreenAction object ReleaseSendState : Action -sealed class FeeActionUI : SendScreenActionUI { - object ToggleFeeLayoutVisibility : FeeActionUI() - data class ChangeSelectedFee(val id: Int) : FeeActionUI() - class ChangeIncludeFee(val isChecked: Boolean) : FeeActionUI() +sealed class FeeActionUi : SendScreenActionUI { + object ToggleFeeLayoutVisibility : FeeActionUi() + data class ChangeSelectedFee(val id: Int) : FeeActionUi() + class ChangeIncludeFee(val isChecked: Boolean) : FeeActionUi() } // shortness AddressOrPayId = APid -sealed class AddressPayIdActionUI : SendScreenActionUI { - data class SetAddressOrPayId(val data: String) : AddressPayIdActionUI() - data class SetTruncateHandler(val handler: (String) -> String) : AddressPayIdActionUI() - data class TruncateOrRestore(val truncate: Boolean) : AddressPayIdActionUI() +sealed class AddressPayIdActionUi : SendScreenActionUI { + data class SetAddressOrPayId(val data: String) : AddressPayIdActionUi() + data class SetTruncateHandler(val handler: (String) -> String) : AddressPayIdActionUi() + data class TruncateOrRestore(val truncate: Boolean) : AddressPayIdActionUi() } sealed class AddressPayIdVerifyAction : SendScreenAction { diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/SendState.kt b/app/src/main/java/com/tangem/tap/features/send/redux/SendState.kt index aefebf34ba..4fde717e85 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/SendState.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/SendState.kt @@ -11,13 +11,13 @@ import org.rekotlin.StateType data class SendState( val walletManager: WalletManager? = null, val lastChangedStateType: StateType = NoneState(), - val addressPayIDState: AddressPayIDState = AddressPayIDState(), + val addressPayIdState: AddressPayIdState = AddressPayIdState(), val feeLayoutState: FeeLayoutState = FeeLayoutState() ) : StateType class NoneState : StateType -data class AddressPayIDState( +data class AddressPayIdState( val etFieldValue: String? = null, val normalFieldValue: String? = null, val truncatedFieldValue: String? = null, @@ -28,7 +28,7 @@ data class AddressPayIDState( fun isPayIdState(): Boolean = walletAddress != null && walletAddress != normalFieldValue - fun copyWalletAddress(address: String): AddressPayIDState { + fun copyWalletAddress(address: String): AddressPayIdState { val truncated = truncateHandler?.invoke(address) ?: address return this.copy( etFieldValue = address, @@ -39,7 +39,7 @@ data class AddressPayIDState( ) } - fun copyError(address: String, error: AddressPayIdVerifyAction.FailReason): AddressPayIDState { + fun copyError(address: String, error: AddressPayIdVerifyAction.FailReason): AddressPayIdState { val truncated = truncateHandler?.invoke(address) ?: address return this.copy( etFieldValue = address, @@ -50,7 +50,7 @@ data class AddressPayIDState( ) } - fun copyPayIdWalletAddress(payId: String, address: String): AddressPayIDState { + fun copyPayIdWalletAddress(payId: String, address: String): AddressPayIdState { val truncated = truncateHandler?.invoke(address) ?: address return this.copy( etFieldValue = payId, @@ -61,7 +61,7 @@ data class AddressPayIDState( ) } - fun copyPaiIdError(payId: String, error: AddressPayIdVerifyAction.FailReason): AddressPayIDState { + fun copyPaiIdError(payId: String, error: AddressPayIdVerifyAction.FailReason): AddressPayIdState { val truncated = truncateHandler?.invoke(payId) ?: payId return this.copy( etFieldValue = payId, 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 81d48ba2fc..ec3e9b7b42 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 @@ -9,8 +9,8 @@ import com.tangem.tap.common.extensions.getFromClipboard import com.tangem.tap.common.qrCodeScan.ScanQrCodeActivity import com.tangem.tap.common.text.truncateMiddleWith import com.tangem.tap.features.send.BaseStoreFragment -import com.tangem.tap.features.send.redux.AddressPayIdActionUI.* -import com.tangem.tap.features.send.redux.FeeActionUI.* +import com.tangem.tap.features.send.redux.AddressPayIdActionUi.* +import com.tangem.tap.features.send.redux.FeeActionUi.* import com.tangem.tap.features.send.redux.ReleaseSendState import com.tangem.tap.features.send.ui.stateSubscribers.SendStateSubscriber import com.tangem.tap.mainScope @@ -46,7 +46,7 @@ class SendFragment : BaseStoreFragment(R.layout.fragment_send) { } etAddressOrPayId.inputedTextAsFlow() .debounce(400) - .filter { store.state.sendState.addressPayIDState.etFieldValue != it } + .filter { store.state.sendState.addressPayIdState.etFieldValue != it } .onEach { store.dispatch(SetAddressOrPayId(it)) } .launchIn(mainScope) 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 780a2a23aa..1027616f81 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 @@ -4,7 +4,7 @@ import android.content.Context import android.view.ViewGroup import androidx.fragment.app.Fragment import androidx.transition.TransitionManager -import com.tangem.tap.features.send.redux.AddressPayIDState +import com.tangem.tap.features.send.redux.AddressPayIdState import com.tangem.tap.features.send.redux.AddressPayIdVerifyAction.FailReason import com.tangem.tap.features.send.redux.FeeLayoutState import com.tangem.tap.features.send.redux.SendState @@ -21,11 +21,11 @@ class SendStateSubscriber(fragment: Fragment) : FragmentStateSubscriber handleFeeLayoutState(fg, state.feeLayoutState) - is AddressPayIDState -> handleAddressPayIdState(fg, state.addressPayIDState) + is AddressPayIdState -> handleAddressPayIdState(fg, state.addressPayIdState) } } - private fun handleAddressPayIdState(fg: Fragment, state: AddressPayIDState) { + private fun handleAddressPayIdState(fg: Fragment, state: AddressPayIdState) { fun parseError(context: Context, error: FailReason?): String? { val resId = when (error) { FailReason.IS_NOT_PAY_ID -> R.string.error_payid_verification_failed diff --git a/app/src/main/res/anim/slide_in_up.xml b/app/src/main/res/anim/slide_in_up.xml new file mode 100644 index 0000000000..a821c2d9c6 --- /dev/null +++ b/app/src/main/res/anim/slide_in_up.xml @@ -0,0 +1,7 @@ + + diff --git a/app/src/main/res/anim/slide_out_down.xml b/app/src/main/res/anim/slide_out_down.xml new file mode 100644 index 0000000000..f6095c0a17 --- /dev/null +++ b/app/src/main/res/anim/slide_out_down.xml @@ -0,0 +1,7 @@ + + diff --git a/app/src/main/res/layout/btn_arrow_up_down.xml b/app/src/main/res/layout/btn_arrow_up_down.xml index 1aedcead47..6f9c5f9bc0 100644 --- a/app/src/main/res/layout/btn_arrow_up_down.xml +++ b/app/src/main/res/layout/btn_arrow_up_down.xml @@ -4,7 +4,6 @@ android:id="@+id/flArrowUpDown" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_marginBottom="8dp" android:background="@drawable/shape_ellipse" android:backgroundTint="@android:color/transparent" android:clickable="true" diff --git a/app/src/main/res/layout/fragment_send.xml b/app/src/main/res/layout/fragment_send.xml index 6abb666dc5..fa601d85e6 100644 --- a/app/src/main/res/layout/fragment_send.xml +++ b/app/src/main/res/layout/fragment_send.xml @@ -31,7 +31,7 @@ android:layout_marginTop="14dp" /> diff --git a/app/src/main/res/layout/layout_send_currency.xml b/app/src/main/res/layout/layout_send_amount.xml similarity index 71% rename from app/src/main/res/layout/layout_send_currency.xml rename to app/src/main/res/layout/layout_send_amount.xml index f53f5c0c3d..92b912df35 100644 --- a/app/src/main/res/layout/layout_send_currency.xml +++ b/app/src/main/res/layout/layout_send_amount.xml @@ -32,29 +32,42 @@ - - - + app:layout_constraintEnd_toEndOf="parent"> + + + + + +