Updated on 2026-08-14

This commit is contained in:
Tangem 2020-12-16 22:54:00 +03:00
parent edecedf446
commit d07ea21d77
13 changed files with 441 additions and 88 deletions

View file

@ -2,6 +2,7 @@ package com.tangem.tap.features.send.redux
import com.tangem.Message
import com.tangem.blockchain.common.Amount
import com.tangem.blockchain.common.Blockchain
import com.tangem.tap.common.redux.ErrorAction
import com.tangem.tap.common.redux.ToastNotificationAction
import com.tangem.tap.domain.TapError
@ -33,7 +34,27 @@ sealed class AddressPayIdActionUi : SendScreenActionUi {
object CheckAddressPayId : AddressPayIdActionUi()
data class SetTruncateHandler(val handler: (String) -> String) : AddressPayIdActionUi()
data class TruncateOrRestore(val truncate: Boolean) : AddressPayIdActionUi()
data class ChangePayIdState(val walletPayIdEnabled: Boolean): AddressPayIdActionUi()
data class ChangePayIdState(val walletPayIdEnabled: Boolean) : AddressPayIdActionUi()
}
sealed class TransactionExtrasAction : SendScreenActionUi {
data class Prepare(
val blockchain: Blockchain,
val walletAddress: String,
val xrpTag: String?,
) : TransactionExtrasAction()
object Release : TransactionExtrasAction()
sealed class XlmMemo : TransactionExtrasAction() {
// data class ChangeSelectedMemo(val memoType: XlmMemoType) : XlmMemo()
data class HandleUserInput(val data: String) : XlmMemo()
}
sealed class XrpDestinationTag : TransactionExtrasAction() {
data class HandleUserInput(val data: String) : XrpDestinationTag()
}
}
sealed class AddressPayIdVerifyAction : SendScreenAction {
@ -122,6 +143,7 @@ sealed class SendAction : SendScreenAction {
val sendAllCallback: () -> Unit,
val reduceAmount: BigDecimal,
) : Dialog()
object Hide : Dialog()
}
}

View file

@ -13,6 +13,7 @@ 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.features.send.redux.FeeAction
import com.tangem.tap.features.send.redux.TransactionExtrasAction
import com.tangem.tap.scope
import com.tangem.tap.store
import kotlinx.coroutines.Dispatchers
@ -82,22 +83,26 @@ internal class AddressPayIdMiddleware {
withContext(Dispatchers.Main) {
when (result) {
is Result.Success -> {
val address = result.data.getAddress()
if (address == null) {
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)
}
}
}
@ -110,8 +115,10 @@ internal class AddressPayIdMiddleware {
val failReason = isValidBlockchainAddressAndNotTheSameAsWallet(wallet, supposedAddress)
if (failReason == null) {
dispatch(SetWalletAddress(supposedAddress, isUserInput))
dispatch(TransactionExtrasAction.Prepare(wallet.blockchain, address, null))
} else {
dispatch(SetAddressError(failReason))
dispatch(TransactionExtrasAction.Release)
}
}

View file

@ -1,6 +1,8 @@
package com.tangem.tap.features.send.redux.middlewares
import com.google.firebase.crashlytics.FirebaseCrashlytics
import com.tangem.blockchain.blockchains.stellar.StellarTransactionExtras
import com.tangem.blockchain.blockchains.xrp.XrpTransactionBuilder
import com.tangem.blockchain.common.*
import com.tangem.blockchain.extensions.Result
import com.tangem.blockchain.extensions.Signer
@ -17,6 +19,7 @@ import com.tangem.tap.domain.extensions.minimalAmount
import com.tangem.tap.features.send.redux.*
import com.tangem.tap.features.send.redux.FeeAction.RequestFee
import com.tangem.tap.features.send.redux.states.SendButtonState
import com.tangem.tap.features.send.redux.states.TransactionExtrasState
import com.tangem.tap.scope
import com.tangem.tap.tangemSdk
import kotlinx.coroutines.Dispatchers
@ -66,14 +69,16 @@ private fun verifyAndSendTransaction(
dispatch(AmountAction.SetAmount(typedAmount.value!!.minus(reduceAmount), false))
dispatch(AmountActionUi.CheckAmountToSend)
}, sendAllCallback = {
sendTransaction(action, walletManager, amountToSend, feeAmount, destinationAddress, card, dispatch)
sendTransaction(action, walletManager, amountToSend, feeAmount, destinationAddress,
sendState.transactionExtrasState, card, dispatch)
}, reduceAmount))
}
transactionErrors.isNotEmpty() -> {
dispatch(SendAction.SendError(createValidateTransactionError(transactionErrors, walletManager)))
}
else -> {
sendTransaction(action, walletManager, amountToSend, feeAmount, destinationAddress, card, dispatch)
sendTransaction(action, walletManager, amountToSend, feeAmount, destinationAddress,
sendState.transactionExtrasState, card, dispatch)
}
}
}
@ -84,11 +89,16 @@ private fun sendTransaction(
amountToSend: Amount,
feeAmount: Amount,
destinationAddress: String,
transactionExtras: TransactionExtrasState,
card: Card,
dispatch: (Action) -> Unit
) {
dispatch(SendAction.ChangeSendButtonState(SendButtonState.PROGRESS))
val txData = walletManager.createTransaction(amountToSend, feeAmount, destinationAddress)
var txData = walletManager.createTransaction(amountToSend, feeAmount, destinationAddress)
transactionExtras.xlmMemo?.memo?.let { txData = txData.copy(extras = StellarTransactionExtras(it)) }
transactionExtras.xrpDestinationTag?.tag?.let { txData = txData.copy(extras = XrpTransactionBuilder.XrpTransactionExtras(it)) }
scope.launch {
walletManager.update()
val isLinkedTerminal = tangemSdk.config.linkedTerminal

View file

@ -25,6 +25,7 @@ class SendScreenReducer {
val reducer: SendInternalReducer = when (action) {
is PrepareSendScreen -> PrepareSendScreenStatesReducer()
is AddressPayIdActionUi, is AddressPayIdVerifyAction -> AddressPayIdReducer()
is TransactionExtrasAction -> TransactionExtrasReducer()
is AmountActionUi, is AmountAction -> AmountReducer()
is FeeActionUi, is FeeAction -> FeeReducer()
is ReceiptAction -> ReceiptReducer()

View file

@ -0,0 +1,105 @@
package com.tangem.tap.features.send.redux.reducers
import com.tangem.blockchain.blockchains.stellar.StellarMemo
import com.tangem.blockchain.common.Blockchain
import com.tangem.tap.features.send.redux.SendScreenAction
import com.tangem.tap.features.send.redux.TransactionExtrasAction.*
import com.tangem.tap.features.send.redux.states.*
/**
[REDACTED_AUTHOR]
*/
class TransactionExtrasReducer : SendInternalReducer {
override fun handle(action: SendScreenAction, sendState: SendState): SendState {
return when (action) {
is Prepare -> handleInitialization(action, sendState)
Release -> handleRelease(action, sendState)
is XlmMemo -> handleMemo(action, sendState, sendState.transactionExtrasState)
is XrpDestinationTag -> handleXrpTag(action, sendState, sendState.transactionExtrasState)
else -> sendState
}
}
private fun handleInitialization(action: Prepare, sendState: SendState): SendState {
val emptyResult = TransactionExtrasState()
val result = when (action.blockchain) {
Blockchain.XRP -> {
// 'r' - without tag, 'x' - with tag
if (action.walletAddress.startsWith("r", true)) {
val tag = action.xrpTag?.toLongOrNull()
if (tag == null) {
TransactionExtrasState(xrpDestinationTag = XrpDestinationTagState())
} else {
TransactionExtrasState(xrpDestinationTag = XrpDestinationTagState(
InputViewValue("$tag", false), tag)
)
}
} else {
emptyResult
}
}
Blockchain.Stellar -> TransactionExtrasState(xlmMemo = XlmMemoState())
else -> emptyResult
}
return updateLastState(sendState.copy(transactionExtrasState = result), result)
}
private fun handleRelease(action: SendScreenAction, sendState: SendState): SendState {
val result = TransactionExtrasState()
return updateLastState(sendState.copy(transactionExtrasState = result), result)
}
private fun handleMemo(
action: XlmMemo,
sendState: SendState,
infoState: TransactionExtrasState,
): SendState {
fun clearMemo(memo: XlmMemoState): XlmMemoState = memo.copy(text = null, id = null)
val result = when (action) {
// is XlmMemo.ChangeSelectedMemo -> {
// val inputViewValue = InputViewValue("", false)
// val memo = infoState.xlmMemo?.copy(
// viewFieldValue = inputViewValue,
// selectedMemoType = action.memoType,
// ) ?: XlmMemoState(inputViewValue, action.memoType)
//
// infoState.copy(xlmMemo = clearMemo(memo))
// }
is XlmMemo.HandleUserInput -> {
val inputViewValue = InputViewValue(action.data, true)
var memo = infoState.xlmMemo?.copy(viewFieldValue = inputViewValue) ?: XlmMemoState(inputViewValue)
memo = clearMemo(memo)
memo = when (infoState.xlmMemo?.selectedMemoType) {
XlmMemoType.TEXT -> memo.copy(text = StellarMemo.Text(action.data))
XlmMemoType.ID -> {
val id = action.data.toIntOrNull()?.toBigInteger()
if (id != null) memo.copy(id = StellarMemo.Id(id)) else memo
}
null -> memo
}
infoState.copy(xlmMemo = memo)
}
}
return updateLastState(sendState.copy(transactionExtrasState = result), result)
}
private fun handleXrpTag(
action: XrpDestinationTag,
sendState: SendState,
infoState: TransactionExtrasState,
): SendState {
val result = when (action) {
is XrpDestinationTag.HandleUserInput -> {
val tag = action.data.toLongOrNull()
if (tag != null) {
val tagState = XrpDestinationTagState(InputViewValue(action.data, true), tag)
infoState.copy(xrpDestinationTag = tagState)
} else {
infoState
}
}
}
return updateLastState(sendState.copy(transactionExtrasState = result), result)
}
}

View file

@ -1,5 +1,6 @@
package com.tangem.tap.features.send.redux.states
import com.tangem.blockchain.blockchains.stellar.StellarMemo
import com.tangem.tap.features.send.redux.AddressPayIdVerifyAction
data class AddressPayIdState(
@ -20,4 +21,34 @@ data class AddressPayIdState(
fun isReady(): Boolean = error == null && destinationWalletAddress?.isNotEmpty() ?: false
fun isPayIdState(): Boolean = destinationWalletAddress != null && destinationWalletAddress != normalFieldValue
}
}
data class TransactionExtrasState(
val xlmMemo: XlmMemoState? = null,
val xrpDestinationTag: XrpDestinationTagState? = null
) : IdStateHolder {
override val stateId: StateId = StateId.ADDRESS_EXTRAS
}
enum class XlmMemoType {
TEXT, ID
}
data class XlmMemoState(
val viewFieldValue: InputViewValue = InputViewValue(""),
val selectedMemoType: XlmMemoType = XlmMemoType.ID,
val text: StellarMemo.Text? = null,
val id: StellarMemo.Id? = null,
) {
val memo: StellarMemo?
get() = when (selectedMemoType) {
XlmMemoType.TEXT -> text
XlmMemoType.ID -> id
}
}
// tag must contains only digits
data class XrpDestinationTagState(
val viewFieldValue: InputViewValue = InputViewValue(""),
val tag: Long? = null
)

View file

@ -22,7 +22,7 @@ interface IdStateHolder {
}
enum class StateId {
SEND_SCREEN, ADDRESS_PAY_ID, AMOUNT, FEE, RECEIPT
SEND_SCREEN, ADDRESS_PAY_ID, ADDRESS_EXTRAS, AMOUNT, FEE, RECEIPT
}
interface SendScreenState : StateType, IdStateHolder
@ -33,6 +33,7 @@ data class SendState(
val tokenConverter: CurrencyConverter? = null,
val lastChangedStates: LinkedHashSet<StateId> = linkedSetOf(),
val addressPayIdState: AddressPayIdState = AddressPayIdState(),
val transactionExtrasState: TransactionExtrasState = TransactionExtrasState(),
val amountState: AmountState = AmountState(),
val feeState: FeeState = FeeState(),
val receiptState: ReceiptState = ReceiptState(),

View file

@ -67,6 +67,7 @@ class SendFragment : BaseStoreFragment(R.layout.fragment_send) {
initSendButtonStates()
setupAddressOrPayIdLayout()
setupTransactionExtrasLayout()
setupAmountLayout()
setupFeeLayout()
@ -106,6 +107,32 @@ class SendFragment : BaseStoreFragment(R.layout.fragment_send) {
}
}
private fun setupTransactionExtrasLayout() {
etMemo.inputtedTextAsFlow()
.debounce(400)
.filter {
val info = store.state.sendState.transactionExtrasState
info.xlmMemo?.viewFieldValue?.value != it
}
.onEach { store.dispatch(TransactionExtrasAction.XlmMemo.HandleUserInput(it)) }
.launchIn(mainScope)
// groupMemo.setOnCheckedChangeListener { group, checkedId ->
// if (checkedId == -1) return@setOnCheckedChangeListener
//
// store.dispatch(TransactionExtrasAction.XlmMemo.ChangeSelectedMemo(MemoUiHelper.toType(checkedId)))
// }
etDestinationTag.inputtedTextAsFlow()
.debounce(400)
.filter {
val info = store.state.sendState.transactionExtrasState
info.xrpDestinationTag?.viewFieldValue?.value != it
}
.onEach { store.dispatch(TransactionExtrasAction.XrpDestinationTag.HandleUserInput(it)) }
.launchIn(mainScope)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (requestCode != ScanQrCodeActivity.SCAN_QR_REQUEST_CODE) return
@ -191,7 +218,7 @@ class SendFragment : BaseStoreFragment(R.layout.fragment_send) {
chipGroup.setOnCheckedChangeListener { group, checkedId ->
if (checkedId == -1) return@setOnCheckedChangeListener
store.dispatch(ChangeSelectedFee(FeeUiHelper.idToFee(checkedId)))
store.dispatch(ChangeSelectedFee(FeeUiHelper.toType(checkedId)))
store.dispatch(CheckAmountToSend)
}
swIncludeFee.setOnCheckedChangeListener { btn, isChecked ->
@ -238,7 +265,7 @@ fun EditText.inputtedTextAsFlow(): Flow<String> = callbackFlow {
class FeeUiHelper {
companion object {
fun feeToId(fee: FeeType): Int {
fun toId(fee: FeeType): Int {
return when (fee) {
FeeType.SINGLE -> View.NO_ID
FeeType.LOW -> R.id.chipLow
@ -247,7 +274,7 @@ class FeeUiHelper {
}
}
fun idToFee(id: Int): FeeType {
fun toType(id: Int): FeeType {
return when (id) {
R.id.chipLow -> FeeType.LOW
R.id.chipNormal -> FeeType.NORMAL
@ -258,6 +285,26 @@ class FeeUiHelper {
}
}
//class MemoUiHelper {
// companion object {
// fun toId(memo: MemoType): Int {
// return when (memo) {
// MemoType.TEXT -> R.id.chipMemoText
// MemoType.ID -> R.id.chipMemoId
// }
// }
//
// fun toType(id: Int): MemoType {
// return when (id) {
// R.id.chipMemoText -> MemoType.TEXT
// R.id.chipMemoId -> MemoType.ID
// else -> MemoType.TEXT
// }
// }
// }
//}
private fun ToggleWidget.setupSendButtonStateModifiers(context: Context) {
mainViewModifiers.clear()
mainViewModifiers.add(ReplaceTextStateModifier(context.getString(R.string.send_title), ""))

View file

@ -2,6 +2,7 @@ package com.tangem.tap.features.send.ui.stateSubscribers
import android.app.Dialog
import android.content.Context
import android.text.InputType
import android.text.SpannableStringBuilder
import android.view.View
import android.view.ViewGroup
@ -43,10 +44,12 @@ class SendStateSubscriber(fragment: BaseStoreFragment) : FragmentStateSubscriber
override fun updateWithNewState(fg: BaseStoreFragment, state: SendState) {
val lastChangedStates = state.lastChangedStates.toList()
state.lastChangedStates.clear()
fg.main_send_container.beginDelayedTransition()
lastChangedStates.forEach {
when (it) {
StateId.SEND_SCREEN -> handleSendScreen(fg, state)
StateId.ADDRESS_PAY_ID -> handleAddressPayIdState(fg, state.addressPayIdState)
StateId.ADDRESS_EXTRAS -> handleTransactionExtrasState(fg, state.transactionExtrasState)
StateId.AMOUNT -> handleAmountState(fg, state.amountState)
StateId.FEE -> handleFeeState(fg, state.feeState)
StateId.RECEIPT -> handleReceiptState(fg, state.receiptState)
@ -54,6 +57,25 @@ class SendStateSubscriber(fragment: BaseStoreFragment) : FragmentStateSubscriber
}
}
private fun handleTransactionExtrasState(fg: BaseStoreFragment, infoState: TransactionExtrasState) {
fun showView(view: View, info: Any?) {
view.show(info != null)
}
showView(fg.xlmMemoContainer, infoState.xlmMemo)
showView(fg.xrpDestinationTagContainer, infoState.xrpDestinationTag)
infoState.xlmMemo?.let {
fg.etMemo.inputType = when (it.selectedMemoType) {
XlmMemoType.TEXT -> InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS
XlmMemoType.ID -> InputType.TYPE_CLASS_NUMBER
}
if (!it.viewFieldValue.isFromUserInput) fg.etMemo.setText(it.viewFieldValue.value)
}
infoState.xrpDestinationTag?.let {
if (!it.viewFieldValue.isFromUserInput) fg.etDestinationTag.setText(it.viewFieldValue.value)
}
}
private fun handleSendScreen(fg: BaseStoreFragment, state: SendState) {
val sendFragment = (fg as? SendFragment) ?: return
@ -106,11 +128,10 @@ class SendStateSubscriber(fragment: BaseStoreFragment) : FragmentStateSubscriber
val hintResId = if (state.walletPayIdEnabled) {
R.string.send_destination_hint_address_payid
}else {
} else {
R.string.send_destination_hint_address
}
til.hint = til.getString(hintResId)
til.parent?.parent?.beginDelayedTransition()
til.error = parsedError
til.isErrorEnabled = parsedError != null
til.helperText = state.destinationWalletAddress
@ -130,10 +151,8 @@ class SendStateSubscriber(fragment: BaseStoreFragment) : FragmentStateSubscriber
}
else -> context.getString(state.error.localizedMessage)
}
fg.amountContainer.parent?.beginDelayedTransition()
fg.tilAmountToSend.enableError(true, message)
} else {
if (fg.tilAmountToSend.isErrorEnabled) fg.amountContainer.parent?.beginDelayedTransition()
fg.tilAmountToSend.enableError(false)
}
@ -168,27 +187,12 @@ class SendStateSubscriber(fragment: BaseStoreFragment) : FragmentStateSubscriber
}
private fun handleFeeState(fg: BaseStoreFragment, state: FeeState) {
var delayedTransitionScheduled = false
fg.chipGroup.fitChipsByGroupWidth()
fg.view?.findViewById<ViewGroup>(R.id.clNetworkFee)?.let {
it.show(state.mainLayoutIsVisible) {
(it.parent as? ViewGroup)?.beginDelayedTransition()
delayedTransitionScheduled = true
}
}
fg.view?.findViewById<ViewGroup>(R.id.clNetworkFee)?.show(state.mainLayoutIsVisible)
fg.imvExpandCollapse.rotation = if (state.controlsLayoutIsVisible) 0f else 180f
fg.llFeeControlsContainer.show(state.controlsLayoutIsVisible) {
if (!delayedTransitionScheduled) {
fg.llFeeControlsContainer.parent?.parent?.beginDelayedTransition()
}
}
fg.chipGroup.show(state.feeChipGroupIsVisible) {
if (!delayedTransitionScheduled) {
fg.llFeeControlsContainer.parent?.parent?.beginDelayedTransition()
}
}
fg.llFeeControlsContainer.show(state.controlsLayoutIsVisible)
fg.chipGroup.show(state.feeChipGroupIsVisible)
fg.swIncludeFee.isEnabled = state.includeFeeSwitcherIsEnabled
if (fg.swIncludeFee.isChecked != state.feeIsIncluded) {
@ -201,7 +205,7 @@ class SendStateSubscriber(fragment: BaseStoreFragment) : FragmentStateSubscriber
}
}
val chipId = FeeUiHelper.feeToId(state.selectedFeeType)
val chipId = FeeUiHelper.toId(state.selectedFeeType)
if (fg.chipGroup.checkedChipId != chipId && chipId != View.NO_ID) fg.chipGroup.check(chipId)
}

View file

@ -23,9 +23,9 @@ data class VerifyPayIdResponse(
val addresses: List<PayIdAddress> = mutableListOf(),
val payId: String? = null,
) {
fun getAddress(): String? {
fun getAddressDetails(): PayIdAddressDetails? {
return if (addresses.isEmpty()) null
else addresses[0].addressDetails.address
else addresses[0].addressDetails
}
}

View file

@ -33,6 +33,7 @@
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<LinearLayout
android:id="@+id/main_send_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">

View file

@ -1,76 +1,197 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/csRecipientAddress"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingStart="16dp"
android:paddingEnd="16dp">
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/tilAddressOrPayId"
android:layout_width="0dp"
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/addressContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/send_destination_hint_address_payid"
app:boxBackgroundColor="@color/backgroundLightGray"
app:errorIconDrawable="@null"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/etAddressOrPayId"
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/tilAddressOrPayId"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:hint="@string/send_destination_hint_address_payid"
app:boxBackgroundColor="@color/backgroundLightGray"
app:errorIconDrawable="@null"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/etAddressOrPayId"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="middle"
android:inputType="textNoSuggestions"
android:paddingStart="0dp"
android:paddingEnd="98dp"
android:singleLine="true"
android:textSize="16sp"
tools:text="139mrsJgyWnJ **** y9BV" />
</com.google.android.material.textfield.TextInputLayout>
<FrameLayout
android:id="@+id/flPaste"
android:layout_width="@dimen/btn_rounded_size"
android:layout_height="@dimen/btn_rounded_size"
android:layout_marginEnd="8dp"
android:background="@drawable/shape_ellipse"
app:layout_constraintBottom_toBottomOf="@+id/flQrCode"
app:layout_constraintEnd_toStartOf="@+id/flQrCode"
app:layout_constraintTop_toTopOf="@+id/flQrCode">
<ImageView
android:id="@+id/imvPaste"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:background="?selectableItemBackgroundBorderless"
android:padding="6dp"
app:srcCompat="@drawable/paste_selector" />
</FrameLayout>
<FrameLayout
android:id="@+id/flQrCode"
android:layout_width="@dimen/btn_rounded_size"
android:layout_height="@dimen/btn_rounded_size"
android:layout_marginTop="10dp"
android:background="@drawable/shape_ellipse"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@+id/tilAddressOrPayId">
<ImageView
android:id="@+id/imvQrCode"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:background="?selectableItemBackgroundBorderless"
android:padding="5dp"
app:srcCompat="@drawable/ic_qr_code_scan" />
</FrameLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
<LinearLayout
android:id="@+id/extrasFieldsContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:orientation="vertical"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/addressContainer">
<LinearLayout
android:id="@+id/xlmMemoContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="middle"
android:inputType="textNoSuggestions"
android:paddingStart="0dp"
android:paddingEnd="98dp"
android:singleLine="true"
android:textSize="16sp"
tools:text="139mrsJgyWnJ **** y9BV" />
android:orientation="vertical"
android:visibility="gone"
tools:visibility="visible">
</com.google.android.material.textfield.TextInputLayout>
<!-- <com.google.android.material.chip.ChipGroup-->
<!-- android:id="@+id/groupMemo"-->
<!-- android:layout_width="match_parent"-->
<!-- android:layout_height="wrap_content"-->
<!-- android:layout_marginTop="8dp"-->
<!-- app:layout_constraintEnd_toEndOf="parent"-->
<!-- app:layout_constraintStart_toStartOf="parent"-->
<!-- app:layout_constraintTop_toTopOf="parent"-->
<!-- app:selectionRequired="true"-->
<!-- app:singleLine="true"-->
<!-- app:singleSelection="true">-->
<FrameLayout
android:id="@+id/flPaste"
android:layout_width="@dimen/btn_rounded_size"
android:layout_height="@dimen/btn_rounded_size"
android:layout_marginEnd="8dp"
android:background="@drawable/shape_ellipse"
app:layout_constraintBottom_toBottomOf="@+id/flQrCode"
app:layout_constraintEnd_toStartOf="@+id/flQrCode"
app:layout_constraintTop_toTopOf="@+id/flQrCode">
<!-- <com.google.android.material.chip.Chip-->
<!-- android:id="@+id/chipMemoText"-->
<!-- style="@style/TapChip"-->
<!-- android:layout_width="wrap_content"-->
<!-- android:layout_height="wrap_content"-->
<!-- android:text="Text" />-->
<ImageView
android:id="@+id/imvPaste"
android:layout_width="wrap_content"
<!-- <com.google.android.material.chip.Chip-->
<!-- android:id="@+id/chipMemoId"-->
<!-- style="@style/TapChip"-->
<!-- android:layout_width="wrap_content"-->
<!-- android:layout_height="wrap_content"-->
<!-- android:text="ID" />-->
<!-- <com.google.android.material.chip.Chip-->
<!-- android:id="@+id/chipMemoHash"-->
<!-- style="@style/TapChip"-->
<!-- android:layout_width="wrap_content"-->
<!-- android:layout_height="wrap_content"-->
<!-- android:text="Hash" />-->
<!-- </com.google.android.material.chip.ChipGroup>-->
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/tilMemo"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/send_extras_hint_memo"
app:boxBackgroundColor="@color/backgroundLightGray"
app:errorIconDrawable="@null">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/etMemo"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="middle"
android:paddingStart="0dp"
android:paddingEnd="0dp"
android:singleLine="true"
android:textSize="16sp"
tools:text="Some memo" />
</com.google.android.material.textfield.TextInputLayout>
</LinearLayout>
<FrameLayout
android:id="@+id/xrpDestinationTagContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:background="?selectableItemBackgroundBorderless"
android:padding="6dp"
app:srcCompat="@drawable/paste_selector" />
android:visibility="gone"
tools:visibility="visible">
</FrameLayout>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/tilDestinationTag"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/send_extras_hint_destination_tag"
app:boxBackgroundColor="@color/backgroundLightGray"
app:errorIconDrawable="@null">
<FrameLayout
android:id="@+id/flQrCode"
android:layout_width="@dimen/btn_rounded_size"
android:layout_height="@dimen/btn_rounded_size"
android:layout_marginTop="10dp"
android:background="@drawable/shape_ellipse"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@+id/tilAddressOrPayId">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/etDestinationTag"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="end"
android:inputType="number"
android:paddingStart="0dp"
android:paddingEnd="0dp"
android:singleLine="true"
android:textSize="16sp"
tools:text="Some destination tag" />
<ImageView
android:id="@+id/imvQrCode"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:background="?selectableItemBackgroundBorderless"
android:padding="5dp"
app:srcCompat="@drawable/ic_qr_code_scan" />
</com.google.android.material.textfield.TextInputLayout>
</FrameLayout>
</FrameLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</LinearLayout>
</LinearLayout>

View file

@ -27,4 +27,7 @@ this wallet.
<string name="common_start" translatable="false">Start</string>
<string name="common_back" translatable="false">Back</string>
<string name="send_extras_hint_memo" translatable="false">Memo</string>
<string name="send_extras_hint_destination_tag" translatable="false">Destination tag</string>
</resources>