Updated on 2026-08-14

This commit is contained in:
Tangem 2021-11-12 16:14:54 +00:00
commit 63dc3d5b57
29 changed files with 352 additions and 121 deletions

View file

@ -38,6 +38,7 @@ android {
}
debug_beta {
initWith release
debuggable true
versionNameSuffix "-beta"
applicationIdSuffix ".debug"
buildConfigField 'String', 'CONFIG_ENVIRONMENT', '\"prod\"'

View file

@ -15,9 +15,8 @@ fun Picasso.loadCurrenciesIcon(
imageView: ImageView,
textView: TextView,
token: Token? = null,
blockchain: Blockchain?,
blockchain: Blockchain,
) {
val blockchain = blockchain ?: Blockchain.Ethereum
val url = if (token != null) {
IconsUtil.getTokenIconUri(blockchain, token)

View file

@ -42,12 +42,12 @@ class CurrenciesRepository(val context: Application) {
}
fun saveAddedTokens(cardId: String, tokens: Collection<Token>) {
saveTokens(cardId, loadSavedTokens(cardId) + tokens)
saveTokens(cardId, loadSavedTokens(cardId) + tokens.distinct())
}
fun saveAddedBlockchain(cardId: String, blockchain: Blockchain) {
val blockchains = loadSavedBlockchains(cardId) + blockchain
saveBlockchains(cardId, blockchains)
saveBlockchains(cardId, blockchains.distinct())
}
fun removeToken(cardId: String, token: Token) {
@ -79,7 +79,7 @@ class CurrenciesRepository(val context: Application) {
}
private fun saveTokens(cardId: String, tokens: List<Token>) {
val json = tokensAdapter.toJson(tokens.map { TokenDao.fromToken(it) }.distinct())
val json = tokensAdapter.toJson(tokens.distinct().map { TokenDao.fromToken(it) })
context.rewriteFile(json, getFileNameForTokens(cardId))
}
@ -93,7 +93,7 @@ class CurrenciesRepository(val context: Application) {
}
private fun saveBlockchains(cardId: String, blockchains: List<Blockchain>) {
val json = blockchainsAdapter.toJson(blockchains)
val json = blockchainsAdapter.toJson(blockchains.distinct())
context.rewriteFile(json, getFileNameForBlockchains(cardId))
}

View file

@ -6,12 +6,13 @@ import com.tangem.common.KeyPair
import com.tangem.common.card.EllipticCurve
import com.tangem.common.core.CardSession
import com.tangem.common.core.CardSessionRunnable
import com.tangem.common.core.TangemSdkError
import com.tangem.common.core.TangemError
import com.tangem.common.extensions.hexToBytes
import com.tangem.operations.wallet.CreateWalletCommand
import com.tangem.operations.wallet.CreateWalletResponse
import com.tangem.operations.wallet.PurgeWalletCommand
import com.tangem.tap.domain.extensions.getSingleWallet
import com.tangem.wallet.R
class CreateSecondTwinWalletTask(
private val firstPublicKey: String,
@ -25,7 +26,7 @@ class CreateSecondTwinWalletTask(
val publicKey = card?.getSingleWallet()?.publicKey
if (publicKey != null) {
if (!card.cardId.startsWith(TwinsHelper.getPairCardSeries(firstCardId) ?: "")) {
callback(CompletionResult.Failure(TangemSdkError.WrongCardType()))
callback(CompletionResult.Failure(WrongTwinCard()))
return
}
@ -65,4 +66,10 @@ class CreateSecondTwinWalletTask(
}
}
}
private class WrongTwinCard : TangemError {
override val code: Int = 50005
override var customMessage: String = code.toString()
override val messageResId = R.string.twins_wrong_card_error
}
}

View file

@ -114,6 +114,7 @@ class AdditionalEmailInfo {
// wallets
internal val walletsInfo = mutableListOf<EmailWalletInfo>()
internal val tokens = mutableMapOf<Blockchain, Collection<Token>>()
internal var onSendErrorWalletInfo: EmailWalletInfo? = null
var signedHashesCount: String = ""
@ -146,6 +147,7 @@ class AdditionalEmailInfo {
fun setWalletsInfo(walletManagers: List<WalletManager>) {
walletsInfo.clear()
tokens.clear()
walletManagers.forEach { manager ->
walletsInfo.add(
EmailWalletInfo(
@ -155,6 +157,9 @@ class AdditionalEmailInfo {
host = manager.currentHost
)
)
if (manager.cardTokens.isNotEmpty()) {
tokens[manager.wallet.blockchain] = manager.cardTokens
}
}
}
@ -308,6 +313,14 @@ class FeedbackEmail : EmailData {
}
appendBlankLine(builder)
infoHolder.tokens.forEach { tokens ->
appendDelimiter(builder)
builder.appendKeyValue("Blockchain", tokens.key.fullName)
builder.appendKeyValue("Tokens", tokens.value.map { "${it.name} - ${it.symbol}" }.toString())
}
appendDelimiter(builder)
appendBlankLine(builder)
// appendKeyValue("Outputs count", infoHolder.outputsCount)
builder.appendKeyValue("Phone model", infoHolder.phoneModel)
builder.appendKeyValue("OS version", infoHolder.osVersion)

View file

@ -15,9 +15,8 @@ class CreateWalletInterruptDialog {
companion object {
fun create(state: TwinCardsAction.Wallet.ShowInterruptDialog, context: Context): AlertDialog {
return MaterialAlertDialogBuilder(context)
.setMessage(R.string.twins_recreate_alert)
.setPositiveButton(R.string.common_ok) { _, _ -> state.onOk() }
.setNegativeButton(R.string.common_cancel) { _, _ -> }
.setMessage(R.string.onboarding_twin_exit_warning)
.setPositiveButton(R.string.warning_button_ok) { _, _ -> }
.setOnDismissListener { store.dispatchDialogHide() }
.create()
}

View file

@ -58,6 +58,10 @@ sealed class TransactionExtrasAction : SendScreenActionUi {
data class HandleUserInput(val data: String) : XlmMemo()
}
sealed class BinanceMemo : TransactionExtrasAction() {
data class HandleUserInput(val data: String) : BinanceMemo()
}
sealed class XrpDestinationTag : TransactionExtrasAction() {
data class HandleUserInput(val data: String) : XrpDestinationTag()
}

View file

@ -14,7 +14,8 @@ class TransactionExtrasReducer : SendInternalReducer {
return when (action) {
is Prepare -> handleInitialization(action, sendState)
Release -> handleRelease(action, sendState)
is XlmMemo -> handleMemo(action, sendState, sendState.transactionExtrasState)
is XlmMemo -> handleXlmMemo(action, sendState, sendState.transactionExtrasState)
is BinanceMemo -> handleBinanceMemo(action, sendState, sendState.transactionExtrasState)
is XrpDestinationTag -> handleXrpTag(action, sendState, sendState.transactionExtrasState)
else -> sendState
}
@ -40,6 +41,7 @@ class TransactionExtrasReducer : SendInternalReducer {
}
}
Blockchain.Stellar -> TransactionExtrasState(xlmMemo = XlmMemoState())
Blockchain.Binance -> TransactionExtrasState(binanceMemo = BinanceMemoState())
else -> emptyResult
}
return updateLastState(sendState.copy(transactionExtrasState = result), result)
@ -50,7 +52,7 @@ class TransactionExtrasReducer : SendInternalReducer {
return updateLastState(sendState.copy(transactionExtrasState = result), result)
}
private fun handleMemo(
private fun handleXlmMemo(
action: XlmMemo,
sendState: SendState,
infoState: TransactionExtrasState,
@ -94,6 +96,26 @@ class TransactionExtrasReducer : SendInternalReducer {
return updateLastState(sendState.copy(transactionExtrasState = result), result)
}
private fun handleBinanceMemo(
action: BinanceMemo,
sendState: SendState,
infoState: TransactionExtrasState,
): SendState {
val result = when (action) {
is BinanceMemo.HandleUserInput -> {
val tag = action.data.toBigIntegerOrNull()
if (tag != null) {
val input = InputViewValue(action.data, true)
val tagState = BinanceMemoState(input, tag)
infoState.copy(binanceMemo = tagState)
} else {
infoState
}
}
}
return updateLastState(sendState.copy(transactionExtrasState = result), result)
}
private fun handleXrpTag(
action: XrpDestinationTag,
sendState: SendState,

View file

@ -27,6 +27,7 @@ data class AddressPayIdState(
data class TransactionExtrasState(
val xlmMemo: XlmMemoState? = null,
val binanceMemo: BinanceMemoState? = null,
val xrpDestinationTag: XrpDestinationTagState? = null
) : IdStateHolder {
override val stateId: StateId = StateId.TRANSACTION_EXTRAS
@ -54,6 +55,16 @@ data class XlmMemoState(
}
}
data class BinanceMemoState(
val viewFieldValue: InputViewValue = InputViewValue(""),
val memo: BigInteger? = null,
val error: TransactionExtraError? = null
) {
companion object {
val MAX_NUMBER: BigInteger = BigInteger("FFFFFFFFFFFFFFFF", 16)
}
}
// tag must contains only digits
data class XrpDestinationTagState(
val viewFieldValue: InputViewValue = InputViewValue(""),
@ -67,5 +78,6 @@ data class XrpDestinationTagState(
enum class TransactionExtraError {
INVALID_DESTINATION_TAG,
INVALID_XLM_MEMO
INVALID_XLM_MEMO,
INVALID_BINANCE_MEMO
}

View file

@ -115,7 +115,7 @@ class SendFragment : BaseStoreFragment(R.layout.fragment_send) {
}
private fun setupTransactionExtrasLayout() {
etMemo.inputtedTextAsFlow()
etXlmMemo.inputtedTextAsFlow()
.debounce(400)
.filter {
val info = store.state.sendState.transactionExtrasState
@ -132,6 +132,15 @@ class SendFragment : BaseStoreFragment(R.layout.fragment_send) {
}
.onEach { store.dispatch(TransactionExtrasAction.XrpDestinationTag.HandleUserInput(it)) }
.launchIn(mainScope)
etBinanceMemo.inputtedTextAsFlow()
.debounce(400)
.filter {
val info = store.state.sendState.transactionExtrasState
info.binanceMemo?.viewFieldValue?.value != it
}
.onEach { store.dispatch(TransactionExtrasAction.BinanceMemo.HandleUserInput(it)) }
.launchIn(mainScope)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {

View file

@ -7,6 +7,7 @@ import android.text.SpannableStringBuilder
import android.view.View
import android.view.ViewGroup
import androidx.core.text.bold
import com.tangem.common.extensions.remove
import com.tangem.tap.common.extensions.*
import com.tangem.tap.common.redux.getMessageString
import com.tangem.tap.common.text.DecimalDigitsInputFilter
@ -64,19 +65,20 @@ class SendStateSubscriber(fragment: BaseStoreFragment) : FragmentStateSubscriber
}
showView(fg.xlmMemoContainer, infoState.xlmMemo)
showView(fg.xrpDestinationTagContainer, infoState.xrpDestinationTag)
showView(fg.binanceMemoContainer, infoState.binanceMemo)
infoState.xlmMemo?.let {
fg.etMemo.inputType = when (it.selectedMemoType) {
fg.etXlmMemo.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)
if (!it.viewFieldValue.isFromUserInput) fg.etXlmMemo.setText(it.viewFieldValue.value)
if (it.error != null) {
if (it.error == TransactionExtraError.INVALID_XLM_MEMO) {
fg.tilMemo.error = fg.getText(R.string.send_error_invalid_memo_id)
fg.tilXlmMemo.error = fg.getText(R.string.send_error_invalid_memo_id)
}
} else {
fg.tilMemo.error = null
fg.tilXlmMemo.error = null
}
}
infoState.xrpDestinationTag?.let {
@ -91,6 +93,18 @@ class SendStateSubscriber(fragment: BaseStoreFragment) : FragmentStateSubscriber
fg.etDestinationTag.setText(it.viewFieldValue.value)
}
}
infoState.binanceMemo?.let {
if (infoState.binanceMemo.error != null) {
if (infoState.binanceMemo.error == TransactionExtraError.INVALID_BINANCE_MEMO) {
fg.tilBinanceMemo.error = fg.getText(R.string.send_error_invalid_memo_id)
}
} else {
fg.tilBinanceMemo.error = null
}
if (!it.viewFieldValue.isFromUserInput) {
fg.etBinanceMemo.setText(it.viewFieldValue.value)
}
}
}
private fun handleSendScreen(fg: BaseStoreFragment, state: SendState) {
@ -187,9 +201,13 @@ class SendStateSubscriber(fragment: BaseStoreFragment) : FragmentStateSubscriber
fg.tvAmountCurrency.update(state.mainCurrency.currencySymbol)
(fg as? SendFragment)?.saveMainCurrency(state.mainCurrency.type)
val balanceText = fg.getString(R.string.send_balance_subtitle_format,
state.mainCurrency.currencySymbol,
state.viewBalanceValue)
val balanceText = when (state.mainCurrency.type) {
MainCurrencyType.FIAT -> fg.getString(R.string.send_balance_subtitle_format,
state.viewBalanceValue, state.mainCurrency.currencySymbol).remove(":")
MainCurrencyType.CRYPTO -> fg.getString(R.string.send_balance_subtitle_format,
state.mainCurrency.currencySymbol, state.viewBalanceValue)
}
fg.tvBalance.update(balanceText)
fg.tilAmountToSend.isEnabled = state.inputIsEnabled

View file

@ -0,0 +1,66 @@
package com.tangem.tap.features.tokens.redux
import androidx.annotation.StringRes
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.Token
import com.tangem.wallet.R
sealed class CurrencyListItem {
data class TokenListItem(val token: Token) : CurrencyListItem()
data class BlockchainListItem(val blockchain: Blockchain) : CurrencyListItem()
data class TitleListItem(
@StringRes val titleResId: Int,
var isContentShown: Boolean = true,
val blockchain: Blockchain? = null,
) : CurrencyListItem()
companion object {
fun createListOfCurrencies(
blockchains: List<Blockchain>,
tokens: List<Token>,
): List<CurrencyListItem> {
val blockchainsTitle = R.string.add_tokens_subtitle_blockchains
val ethereumTokensTitle = R.string.add_tokens_subtitle_ethereum_tokens
val bscTokensTitle = R.string.add_tokens_subtitle_bsc_tokens
val binanceTokensTitle = R.string.add_tokens_subtitle_binance_tokens
val ethereumTokens = tokens.filter { it.blockchain == Blockchain.Ethereum }
val bscTokens = tokens.filter { it.blockchain == Blockchain.BSC }
val binanceTokens = tokens.filter { it.blockchain == Blockchain.Binance }
return listOf(TitleListItem(blockchainsTitle)) +
blockchains.map { BlockchainListItem(it) } +
listOf(TitleListItem(ethereumTokensTitle, blockchain = Blockchain.Ethereum)) +
ethereumTokens.map { TokenListItem(it) } +
listOf(TitleListItem(bscTokensTitle, blockchain = Blockchain.BSC)) +
bscTokens.map { TokenListItem(it) } +
listOf(TitleListItem(binanceTokensTitle, blockchain = Blockchain.Binance)) +
binanceTokens.map { TokenListItem(it) }
}
}
}
fun List<CurrencyListItem>.removeTokensForBlockchain(blockchain: Blockchain): List<CurrencyListItem> {
return filterNot {
it is CurrencyListItem.TokenListItem && it.token.blockchain == blockchain
}
}
fun List<CurrencyListItem>.addTokensForBlockchain(
blockchain: Blockchain, fullCurrenciesList: List<CurrencyListItem>,
): List<CurrencyListItem> {
val tokensToAdd = fullCurrenciesList.filter {
it is CurrencyListItem.TokenListItem && it.token.blockchain == blockchain
}
val indexToInsert = indexOfFirst {
it is CurrencyListItem.TitleListItem && it.blockchain == blockchain
} + 1
return this.toMutableList().apply { addAll(indexToInsert, tokensToAdd) }
}
fun List<CurrencyListItem>.toggleHeaderContentShownValue(blockchain: Blockchain) {
map {
if (it is CurrencyListItem.TitleListItem && it.blockchain == blockchain) {
it.isContentShown = !it.isContentShown
}
}
}

View file

@ -1,7 +1,7 @@
package com.tangem.tap.features.tokens.redux
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.Token
import com.tangem.tap.features.tokens.ui.adapters.CurrencyListItem
import com.tangem.tap.features.wallet.redux.WalletData
import org.rekotlin.Action
@ -17,5 +17,6 @@ sealed class TokensAction : Action {
data class SetAddedCurrencies(val wallets: List<WalletData>) : TokensAction()
data class ToggleShowTokensForBlockchain(val isShown: Boolean, val blockchain: Blockchain) : TokensAction()
}

View file

@ -3,7 +3,6 @@ package com.tangem.tap.features.tokens.redux
import com.tangem.tap.common.redux.AppState
import com.tangem.tap.currenciesRepository
import com.tangem.tap.domain.TapWorkarounds.isTestCard
import com.tangem.tap.features.tokens.ui.adapters.CurrencyListItem
import com.tangem.tap.store
import org.rekotlin.Middleware

View file

@ -18,7 +18,7 @@ private fun internalReduce(action: Action, state: AppState): TokensState {
val tokensState = state.tokensState
return when (action) {
is TokensAction.LoadCurrencies.Success -> {
tokensState.copy(currencies = action.currencies)
tokensState.copy(currencies = action.currencies, shownCurrencies = action.currencies)
}
is TokensAction.SetAddedCurrencies -> {
tokensState.copy(addedCurrencies = action.wallets.toCardCurrencies())
@ -28,6 +28,20 @@ private fun internalReduce(action: Action, state: AppState): TokensState {
action.tokens.map { TokenWithAmount(it, null) }
))
}
is TokensAction.ToggleShowTokensForBlockchain -> {
if (action.isShown) {
val shownCurrencies = tokensState.shownCurrencies
.removeTokensForBlockchain(action.blockchain)
shownCurrencies.toggleHeaderContentShownValue(action.blockchain)
tokensState.copy(shownCurrencies = shownCurrencies)
} else {
val shownCurrencies = tokensState.shownCurrencies
.addTokensForBlockchain(action.blockchain, tokensState.currencies)
shownCurrencies.toggleHeaderContentShownValue(action.blockchain)
tokensState.copy(shownCurrencies = shownCurrencies)
}
}
else -> tokensState
}
}

View file

@ -2,15 +2,14 @@ package com.tangem.tap.features.tokens.redux
import com.tangem.blockchain.common.Amount
import com.tangem.blockchain.common.Token
import com.tangem.tap.common.redux.global.CryptoCurrencyName
import com.tangem.tap.domain.tokens.CardCurrencies
import com.tangem.tap.features.tokens.ui.adapters.CurrencyListItem
import org.rekotlin.StateType
data class TokensState(
val addedTokens: LinkedHashSet<TokenWithAmount> = LinkedHashSet(),
val addedCurrencies: CardCurrencies? = null,
val currencies: List<CurrencyListItem> = emptyList(),
val shownCurrencies: List<CurrencyListItem> = emptyList(),
) : StateType
data class TokenWithAmount(

View file

@ -73,7 +73,7 @@ class AddTokensFragment : Fragment(R.layout.fragment_add_tokens),
override fun newState(state: TokensState) {
if (activity == null) return
viewAdapter.addedCurrencies = state.addedCurrencies
viewAdapter.submitUnfilteredList(state.currencies)
viewAdapter.submitUnfilteredList(state.shownCurrencies)
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {

View file

@ -3,18 +3,17 @@ package com.tangem.tap.features.tokens.ui.adapters
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.annotation.StringRes
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import com.squareup.picasso.Picasso
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.Token
import com.tangem.tap.common.extensions.getString
import com.tangem.tap.common.extensions.hide
import com.tangem.tap.common.extensions.loadCurrenciesIcon
import com.tangem.tap.common.extensions.show
import com.tangem.tap.domain.tokens.CardCurrencies
import com.tangem.tap.features.tokens.redux.CurrencyListItem
import com.tangem.tap.features.tokens.redux.TokensAction
import com.tangem.tap.features.wallet.redux.WalletAction
import com.tangem.tap.store
import com.tangem.wallet.R
@ -68,11 +67,11 @@ class CurrenciesAdapter : ListAdapter<CurrencyListItem, RecyclerView.ViewHolder>
object DiffUtilCallback : DiffUtil.ItemCallback<CurrencyListItem>() {
override fun areContentsTheSame(
oldItem: CurrencyListItem, newItem: CurrencyListItem
oldItem: CurrencyListItem, newItem: CurrencyListItem,
) = oldItem == newItem
override fun areItemsTheSame(
oldItem: CurrencyListItem, newItem: CurrencyListItem
oldItem: CurrencyListItem, newItem: CurrencyListItem,
) = oldItem == newItem
}
@ -161,38 +160,21 @@ class CurrenciesAdapter : ListAdapter<CurrencyListItem, RecyclerView.ViewHolder>
class TitleViewHolder(val view: View) :
RecyclerView.ViewHolder(view) {
fun bind(title: CurrencyListItem.TitleListItem) {
view.tv_subtitle.text = view.getString(title.titleResId).toUpperCase(Locale.US)
}
}
}
sealed class CurrencyListItem {
data class TokenListItem(val token: Token) : CurrencyListItem()
data class BlockchainListItem(val blockchain: Blockchain) : CurrencyListItem()
data class TitleListItem(@StringRes val titleResId: Int) : CurrencyListItem()
companion object {
fun createListOfCurrencies(
blockchains: List<Blockchain>,
tokens: List<Token>
): List<CurrencyListItem> {
val blockchainsTitle = R.string.add_tokens_subtitle_blockchains
val ethereumTokensTitle = R.string.add_tokens_subtitle_ethereum_tokens
val bscTokensTitle = R.string.add_tokens_subtitle_bsc_tokens
val binanceTokensTitle = R.string.add_tokens_subtitle_binance_tokens
val ethereumTokens = tokens.filter { it.blockchain == Blockchain.Ethereum }
val bscTokens = tokens.filter { it.blockchain == Blockchain.BSC }
val binanceTokens = tokens.filter { it.blockchain == Blockchain.Binance }
return listOf(TitleListItem(blockchainsTitle)) +
blockchains.map { BlockchainListItem(it) } +
listOf(TitleListItem(ethereumTokensTitle)) +
ethereumTokens.map { TokenListItem(it) } +
listOf(TitleListItem(bscTokensTitle)) +
bscTokens.map { TokenListItem(it) } +
listOf(TitleListItem(binanceTokensTitle)) +
binanceTokens.map { TokenListItem(it) }
view.tv_subtitle.text = view.getString(title.titleResId).uppercase()
if (title.blockchain != null) {
view.cl_subtitle_container.setOnClickListener {
val rotation = if (title.isContentShown) -90f else 0f
store.dispatch(TokensAction.ToggleShowTokensForBlockchain(
title.isContentShown, title.blockchain
))
view.iv_toggle_sublist_visibility.animate().rotation(rotation)
}
view.iv_toggle_sublist_visibility.show()
view.iv_toggle_sublist_visibility.setImageResource(R.drawable.ic_arrow_angle_down)
} else {
view.iv_toggle_sublist_visibility.hide()
}
}
}
}

View file

@ -145,7 +145,7 @@ class WalletDetailsFragment : Fragment(R.layout.fragment_wallet_details), StoreS
Picasso.get().loadCurrenciesIcon(
imageView = iv_currency,
textView = tv_token_letter,
blockchain = wallet.currency?.blockchain,
blockchain = wallet.currency.blockchain,
token = (wallet.currency as? Currency.Token)?.token
)
}

View file

@ -63,7 +63,7 @@ class WalletAdapter
view.card_wallet.setOnClickListener {
store.dispatch(WalletAction.MultiWallet.SelectWallet(wallet))
}
val blockchain = wallet.currency?.blockchain
val blockchain = wallet.currency.blockchain
val token = (wallet.currency as? Currency.Token)?.token
Picasso.get().loadCurrenciesIcon(

View file

@ -0,0 +1,5 @@
<vector android:autoMirrored="true" android:height="24dp"
android:tint="#1C1C1E" android:viewportHeight="24"
android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M7.41,8.59L12,13.17l4.59,-4.58L18,10l-6,6 -6,-6 1.41,-1.41z"/>
</vector>

View file

@ -0,0 +1,11 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:autoMirrored="true"
android:tint="#1C1C1E"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M8.59,16.59L13.17,12 8.59,7.41 10,6l6,6 -6,6 -1.41,-1.41z" />
</vector>

View file

@ -2,6 +2,7 @@
<androidx.constraintlayout.widget.ConstraintLayout 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/cl_subtitle_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="10dp"
@ -20,4 +21,19 @@
app:layout_constraintTop_toTopOf="parent"
tools:text="BLOCKCHAINS" />
<ImageView
android:id="@+id/iv_toggle_sublist_visibility"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:textAllCaps="true"
android:textColor="@color/darkGray1"
android:textSize="13sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@id/tv_subtitle"
app:layout_constraintBottom_toBottomOf="@id/tv_subtitle"
tools:src="@drawable/ic_arrow_angle_right" />
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -52,33 +52,56 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.5"/>
app:layout_constraintGuide_percent="0.5" />
<TextView
android:id="@+id/tv_currency"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
android:ellipsize="end"
android:maxLines="1"
android:paddingStart="16dp"
android:paddingEnd="2dp"
android:textColor="@color/darkGray6"
android:textSize="17sp"
android:textStyle="bold"
app:layout_constraintStart_toEndOf="@id/iv_currency"
android:ellipsize="end"
android:maxLines="1"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toEndOf="@id/vertical_guideline"
app:layout_constraintBottom_toBottomOf="@id/guideline"
tools:text="Bitcoin" />
app:layout_constraintEnd_toStartOf="@+id/tv_amount"
app:layout_constraintStart_toEndOf="@id/iv_currency"
app:layout_constraintTop_toTopOf="parent"
tools:text="Binance Smart Chain Optimal" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/vertical_guideline"
android:id="@+id/guideline2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintGuide_percent="0.5" />
app:layout_constraintGuide_percent="0.45"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/tv_amount"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:layout_marginEnd="4dp"
android:ellipsize="end"
android:gravity="end"
android:maxEms="12"
android:maxLength="12"
android:maxLines="1"
android:textColor="@color/darkGray6"
android:textSize="17sp"
android:textStyle="bold"
android:visibility="visible"
app:layout_constraintEnd_toStartOf="@+id/tv_currency_symbol"
app:layout_constraintHorizontal_bias="1.0"
app:layout_constraintStart_toStartOf="@+id/guideline2"
app:layout_constraintTop_toTopOf="parent"
tools:text="1234567890.1234567890" />
<TextView
android:id="@+id/tv_exchange_rate"
@ -90,8 +113,8 @@
android:textColor="@color/darkGray2"
android:textSize="14sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toTopOf="@id/guideline"
app:layout_constraintStart_toEndOf="@id/iv_currency"
app:layout_constraintTop_toTopOf="@id/guideline"
tools:text="USD 3 588" />
<TextView
@ -105,8 +128,8 @@
android:textSize="14sp"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toTopOf="@id/guideline"
app:layout_constraintStart_toEndOf="@id/iv_currency" />
app:layout_constraintStart_toEndOf="@id/iv_currency"
app:layout_constraintTop_toTopOf="@id/guideline" />
<TextView
android:id="@+id/tv_status_loading"
@ -123,29 +146,10 @@
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<TextView
android:id="@+id/tv_amount"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="2dp"
android:layout_marginTop="16dp"
android:ellipsize="end"
android:gravity="end"
android:maxLines="1"
android:textColor="@color/darkGray6"
android:textSize="17sp"
android:textStyle="bold"
app:layout_constraintEnd_toStartOf="@id/tv_currency_symbol"
app:layout_constraintHorizontal_weight="1"
app:layout_constraintStart_toEndOf="@id/vertical_guideline"
app:layout_constraintTop_toTopOf="parent"
tools:text="3 588" />
<TextView
android:id="@+id/tv_currency_symbol"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="4dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
android:gravity="end"
@ -155,7 +159,6 @@
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_weight="1"
app:layout_constraintStart_toEndOf="@id/tv_amount"
app:layout_constraintTop_toTopOf="parent"
tools:text="BTC" />
@ -168,8 +171,8 @@
android:textColor="@color/darkGray2"
android:textSize="14sp"
app:layout_constraintBottom_toBottomOf="@id/tv_exchange_rate"
app:layout_constraintTop_toTopOf="@id/tv_exchange_rate"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@id/tv_exchange_rate"
tools:text="0.43 USD" />
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -4,9 +4,9 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="52dp"
android:layout_marginBottom="1dp"
android:background="@android:color/white">
android:background="@android:color/white"
android:minHeight="60dp">
<ImageView
android:id="@+id/iv_currency"
@ -14,6 +14,7 @@
android:layout_height="40dp"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:contentDescription="@string/token_icon"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
@ -37,7 +38,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.5"/>
app:layout_constraintGuide_percent="0.5" />
<TextView
android:id="@+id/tv_currency_name"
@ -50,27 +51,27 @@
android:textColor="@color/darkGray6"
android:textSize="17sp"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="@id/guideline"
app:layout_constraintEnd_toStartOf="@id/barrier_chip_button"
app:layout_constraintHorizontal_bias="0"
app:layout_constraintVertical_bias="1"
app:layout_constraintStart_toEndOf="@id/iv_currency"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="@id/guideline"
app:layout_constraintVertical_bias="1"
tools:text="NODLE" />
<TextView
android:id="@+id/tv_currency_symbol"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintVertical_bias="0"
android:paddingStart="16dp"
android:paddingEnd="16dp"
android:textColor="@color/darkGray6"
android:textSize="14sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toTopOf="@id/guideline"
app:layout_constraintHorizontal_bias="0"
app:layout_constraintStart_toEndOf="@id/iv_currency"
app:layout_constraintTop_toTopOf="@id/guideline"
app:layout_constraintVertical_bias="0"
tools:text="NODLE" />
<com.google.android.material.chip.Chip
@ -90,11 +91,11 @@
<com.google.android.material.chip.Chip
android:id="@+id/btn_token_added"
android:enabled="false"
style="@style/Widget.MaterialComponents.Chip.Action"
android:layout_width="102dp"
android:layout_height="wrap_content"
android:layout_marginEnd="24dp"
android:enabled="false"
android:src="@drawable/ic_inactive"
android:text="@string/add_token_added"
android:textAlignment="center"

View file

@ -9,17 +9,29 @@
<TextView
android:id="@+id/tv_currency"
android:layout_width="wrap_content"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:ellipsize="end"
android:maxLines="1"
android:paddingStart="16dp"
android:paddingTop="16dp"
android:paddingEnd="16dp"
android:textColor="@color/darkGray6"
android:textSize="20sp"
android:textStyle="bold"
app:layout_constraintEnd_toStartOf="@+id/tv_amount"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="Bitcoin" />
tools:text="Binance Smart Chain Optimal" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintGuide_percent="0.45"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/tv_status_verified"
@ -90,24 +102,27 @@
<TextView
android:id="@+id/tv_amount"
android:layout_width="0dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:ellipsize="end"
android:gravity="end"
android:maxEms="12"
android:maxLength="12"
android:maxLines="1"
android:textColor="@color/darkGray6"
android:textSize="20sp"
android:textStyle="bold"
android:visibility="visible"
app:autoSizeMaxTextSize="20sp"
app:autoSizeMinTextSize="12sp"
app:autoSizeStepGranularity="1sp"
app:autoSizeTextType="uniform"
app:layout_constraintBottom_toBottomOf="@id/tv_currency"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_weight="1"
app:layout_constraintStart_toEndOf="@id/tv_currency"
tools:text="0.43 BTC" />
app:layout_constraintHorizontal_bias="1.0"
app:layout_constraintStart_toStartOf="@+id/guideline2"
tools:text="111230.98123128293475943 BTC" />
<TextView
android:id="@+id/tv_fiat_amount"
@ -179,8 +194,7 @@
app:layout_constraintHorizontal_bias="1"
app:layout_constraintStart_toEndOf="@id/tv_base_currency"
app:layout_constraintTop_toBottomOf="@id/v_divider"
tools:text="0.00434143 ETH"
/>
tools:text="0.00434143 ETH" />
<androidx.constraintlayout.widget.Group
android:id="@+id/group_base_currency"

View file

@ -45,10 +45,10 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end"
android:gravity="end"
android:layout_marginTop="4dp"
tools:text="@string/send_total_subtitle_format"
android:textColor="@color/darkGray1" />
android:gravity="end"
android:textColor="@color/darkGray1"
tools:text="123.29837729 ADA 39487593.109342039402938049 will be sent" />
</LinearLayout>

View file

@ -140,7 +140,7 @@
<!-- </com.google.android.material.chip.ChipGroup>-->
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/tilMemo"
android:id="@+id/tilXlmMemo"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/send_extras_hint_memo"
@ -148,7 +148,7 @@
app:errorIconDrawable="@null">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/etMemo"
android:id="@+id/etXlmMemo"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/backgroundLightGray"
@ -163,6 +163,38 @@
</LinearLayout>
<FrameLayout
android:id="@+id/binanceMemoContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone"
tools:visibility="visible">
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/tilBinanceMemo"
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/etBinanceMemo"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/backgroundLightGray"
android:ellipsize="end"
android:inputType="number"
android:paddingStart="0dp"
android:paddingEnd="0dp"
android:singleLine="true"
android:textSize="16sp"
tools:text="123" />
</com.google.android.material.textfield.TextInputLayout>
</FrameLayout>
<FrameLayout
android:id="@+id/xrpDestinationTagContainer"
android:layout_width="match_parent"

View file

@ -29,6 +29,7 @@
<string name="twins_recreate_subtitle" translatable="false">The wallet creation procedure consists of three steps. You must complete it to the end, otherwise you will have to start from the beginning.</string>
<string name="twins_recreate_button_format" translatable="false">Tap the card #%s</string>
<string name="twins_recreate_alert" translatable="false">You have already started the process of recreating twin wallet. If you interrupt it, you won\'t be able to use your twin cards until you start it again and complete recreating the wallet.</string>
<string name="twins_wrong_card_error" translatable="false">You\'ve tapped the same card. To create twin wallet you need to tap a second twin card.</string>
<string name="notification_twins_recreate_success" translatable="false">The twin address was successfully created</string>
@ -90,6 +91,7 @@
<string name="custom_token_token_symbol_input_placeholder" translatable="false">ex. USDC</string>
<string name="add_token_added" translatable="false">Added</string>
<string name="remove_token" translatable="false">Remove token</string>
<string name="token_icon" translatable="false">Token icon</string>
//Details
<string name="details_section_title_blockchain" translatable="false">Blockchain</string>
@ -198,10 +200,12 @@
<string name="address_qr_code_message_token_format" translatable="false">Send only %s (%s) from %s network to this address. Sending any other currency will result in its irreversible loss.</string>
<string name="onboarding_twins_interrupt_warning" translatable="false">If the process of re-creating the wallet gets interrupted in any way, youll have to start over.</string>
<string name="onboarding_twin_exit_warning" translatable="false">The twinning process is partly complete. You cant exit it now.</string>
<string name="onboarding_error_create_primary_wallet" translatable="false">Internal error: can\'t create wallet manager</string>
<string name="wallet_balance_blockchain_unreachable_try_later" translatable="false">Blockchain is unreachable. Try later</string>
<string name="warning_button_ok" translatable="false">Ok, Got it!</string>
</resources>