Updated on 2026-08-14

This commit is contained in:
Tangem 2020-09-07 17:59:13 +03:00
commit 9b3f6994a9
12 changed files with 228 additions and 16 deletions

View file

@ -6,7 +6,6 @@ import com.google.zxing.BarcodeFormat
import com.google.zxing.EncodeHintType
import com.google.zxing.qrcode.QRCodeWriter
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel
import com.tangem.blockchain.common.Blockchain
import java.math.BigDecimal
import java.math.RoundingMode
import java.text.DecimalFormat
@ -34,16 +33,16 @@ fun String.toQrCode(): Bitmap {
return bmp
}
fun BigDecimal.toFormattedString(blockchain: Blockchain): String {
fun BigDecimal.toFormattedString(decimals: Int): String {
val symbols = DecimalFormatSymbols(Locale.US)
symbols.decimalSeparator = '.'
val df = DecimalFormat()
df.decimalFormatSymbols = symbols
df.maximumFractionDigits = blockchain.decimals()
df.maximumFractionDigits = decimals
df.minimumFractionDigits = 0
df.isGroupingUsed = false
val bd = BigDecimal(unscaledValue(), scale())
bd.setScale(blockchain.decimals(), BigDecimal.ROUND_DOWN)
bd.setScale(decimals, BigDecimal.ROUND_DOWN)
return df.format(bd)
}

View file

@ -24,6 +24,10 @@ fun Fragment.getDrawable(@DrawableRes drawableResId: Int): Drawable? {
return ContextCompat.getDrawable(requireContext(), drawableResId)
}
fun Context.getDrawableCompat(@DrawableRes drawableResId: Int): Drawable? {
return ContextCompat.getDrawable(this, drawableResId)
}
fun View.show(show: Boolean) {
if (show) this.visibility = View.VISIBLE else this.visibility = View.GONE
}

View file

@ -0,0 +1,36 @@
package com.tangem.tap.features.wallet.models
import com.tangem.blockchain.common.TransactionData
import com.tangem.tap.common.extensions.toFormattedString
data class PendingTransaction(
val address: String,
val amount: String,
val currency: String,
val type: PendingTransactionType
)
enum class PendingTransactionType { Incoming, Outcoming }
fun TransactionData.toPendingTransaction(walletAddress: String): PendingTransaction {
val type: PendingTransactionType = if (this.sourceAddress == walletAddress) {
PendingTransactionType.Outcoming
} else {
PendingTransactionType.Incoming
}
val address = if (this.sourceAddress == walletAddress) {
this.destinationAddress
} else {
this.sourceAddress
}
return PendingTransaction(
address,
this.amount.value?.toFormattedString(amount.decimals) ?: "?",
this.amount.currencySymbol,
type
)
}
fun List<TransactionData>.toPendingTransactions(walletAddress: String): List<PendingTransaction>{
return this.map { it.toPendingTransaction(walletAddress) }
}

View file

@ -7,6 +7,7 @@ import com.tangem.tap.common.extensions.toFiatString
import com.tangem.tap.common.extensions.toFormattedString
import com.tangem.tap.common.extensions.toQrCode
import com.tangem.tap.common.redux.AppState
import com.tangem.tap.features.wallet.models.toPendingTransactions
import com.tangem.tap.features.wallet.ui.BalanceStatus
import com.tangem.tap.features.wallet.ui.BalanceWidgetData
import com.tangem.tap.features.wallet.ui.TokenData
@ -37,7 +38,7 @@ private fun internalReduce(action: Action, state: AppState): WalletState {
} else {
AddressData(wallet.address, wallet.shareUrl, wallet.exploreUrl)
}
val currentArtworkId = state.globalState.scanNoteResponse?.verifyResponse?.artworkInfo?.id
val currentArtworkId = state.globalState.scanNoteResponse?.verifyResponse?.artworkInfo?.id
val cardImage = if (newState.cardImage?.artworkId == currentArtworkId) {
newState.cardImage
} else {
@ -61,7 +62,7 @@ private fun internalReduce(action: Action, state: AppState): WalletState {
val tokenFiatRate = state.globalState.fiatRates.getRateForCryptoCurrency(token.currencySymbol)
val tokenFiatAmount = tokenFiatRate?.let { token.value?.toFiatString(it) }
TokenData(
token.value?.toFormattedString(action.wallet.blockchain) ?: "",
token.value?.toFormattedString(token.decimals) ?: "",
token.currencySymbol, tokenFiatAmount)
} else {
null
@ -70,16 +71,20 @@ private fun internalReduce(action: Action, state: AppState): WalletState {
val fiatRate = state.globalState.fiatRates.getRateForCryptoCurrency(action.wallet.blockchain.currency)
val fiatAmount = fiatRate?.let { amount?.toFiatString(it) }
val pendingTransactions = action.wallet.transactions
.toPendingTransactions(action.wallet.address)
val sendButtonEnabled = amount?.isZero() == false || token?.value?.isZero() == false
newState = newState.copy(
state = ProgressState.Done, wallet = action.wallet,
currencyData = BalanceWidgetData(
BalanceStatus.VerifiedOnline, action.wallet.blockchain.fullName,
currencySymbol = action.wallet.blockchain.currency,
amount?.toFormattedString(action.wallet.blockchain),
amount?.toFormattedString(action.wallet.blockchain.decimals()),
token = tokenData,
fiatAmount = fiatAmount
),
pendingTransactions = pendingTransactions,
mainButton = WalletMainButton.SendButton(sendButtonEnabled)
)
}

View file

@ -3,6 +3,7 @@ package com.tangem.tap.features.wallet.redux
import android.graphics.Bitmap
import com.tangem.blockchain.common.Wallet
import com.tangem.tap.common.entities.Button
import com.tangem.tap.features.wallet.models.PendingTransaction
import com.tangem.tap.features.wallet.ui.BalanceWidgetData
import org.rekotlin.StateType
@ -10,6 +11,7 @@ data class WalletState(
val state: ProgressState = ProgressState.Done,
val cardImage: Artwork? = null,
val wallet: Wallet? = null,
val pendingTransactions: List<PendingTransaction> = emptyList(),
val addressData: AddressData? = null,
val currencyData: BalanceWidgetData = BalanceWidgetData(),
val payIdData: PayIdData = PayIdData(),

View file

@ -0,0 +1,62 @@
package com.tangem.tap.features.wallet.ui
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import com.tangem.tap.common.extensions.getDrawableCompat
import com.tangem.tap.features.wallet.models.PendingTransaction
import com.tangem.tap.features.wallet.models.PendingTransactionType
import com.tangem.wallet.R
import kotlinx.android.synthetic.main.layout_pending_transaction.view.*
class PendingTransactionsAdapter
: ListAdapter<PendingTransaction, PendingTransactionsAdapter.TransactionsViewHolder>(DiffUtilCallback) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TransactionsViewHolder {
val layout = LayoutInflater.from(parent.context)
.inflate(R.layout.layout_pending_transaction, parent, false)
return TransactionsViewHolder(layout)
}
override fun onBindViewHolder(holder: TransactionsViewHolder, position: Int) {
holder.bind(currentList[position])
}
object DiffUtilCallback : DiffUtil.ItemCallback<PendingTransaction>() {
override fun areContentsTheSame(
oldItem: PendingTransaction, newItem: PendingTransaction
) = oldItem == newItem
override fun areItemsTheSame(
oldItem: PendingTransaction, newItem: PendingTransaction
) = oldItem == newItem
}
class TransactionsViewHolder(val view: View) :
RecyclerView.ViewHolder(view) {
fun bind(transaction: PendingTransaction) {
val transactionDescriptionRes = when (transaction.type) {
PendingTransactionType.Incoming -> R.string.wallet_pending_transaction_incoming
PendingTransactionType.Outcoming -> R.string.wallet_pending_transaction_outcoming
}
val image = when (transaction.type) {
PendingTransactionType.Incoming -> R.drawable.ic_arrow_down
PendingTransactionType.Outcoming -> R.drawable.ic_arrow_right
}
view.tv_pending_transaction.text = view.context.getString(
transactionDescriptionRes, transaction.amount, transaction.currency
)
view.tv_pending_transaction_address.text = transaction.address
view.iv_pending_transaction.setImageDrawable(view.context.getDrawableCompat(image))
}
}
}

View file

@ -5,6 +5,7 @@ import android.os.Bundle
import android.view.View
import androidx.activity.OnBackPressedCallback
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.transition.TransitionInflater
import com.tangem.tap.common.extensions.getDrawable
import com.tangem.tap.common.extensions.hide
@ -26,6 +27,9 @@ class WalletFragment : Fragment(R.layout.fragment_wallet), StoreSubscriber<Walle
private var qrDialog: QrDialog? = null
private var payIdDialog: PayIdDialog? = null
private lateinit var viewAdapter: PendingTransactionsAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
activity?.onBackPressedDispatcher?.addCallback(this, object : OnBackPressedCallback(true) {
@ -55,7 +59,6 @@ class WalletFragment : Fragment(R.layout.fragment_wallet), StoreSubscriber<Walle
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
toolbar.setNavigationOnClickListener {
store.dispatch(NavigationAction.PopBackTo())
}
@ -63,8 +66,17 @@ class WalletFragment : Fragment(R.layout.fragment_wallet), StoreSubscriber<Walle
btn_scan.setOnClickListener {
store.dispatch(WalletAction.Scan)
}
setupTransactionsRecyclerView()
}
private fun setupTransactionsRecyclerView() {
viewAdapter = PendingTransactionsAdapter()
rv_pending_transaction.layoutManager = LinearLayoutManager(context)
rv_pending_transaction.adapter = viewAdapter
}
override fun newState(state: WalletState) {
if (activity == null) return
@ -76,6 +88,8 @@ class WalletFragment : Fragment(R.layout.fragment_wallet), StoreSubscriber<Walle
setupAddressCard(state)
setupCardImage(state.cardImage?.artwork)
viewAdapter.submitList(state.pendingTransactions)
if (state.qrCode != null && state.addressData?.shareUrl != null) {
if (qrDialog == null) qrDialog = QrDialog(requireContext())
qrDialog?.showQr(state.qrCode, state.addressData.shareUrl)

View file

@ -0,0 +1,14 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="14dp"
android:height="14dp"
android:viewportWidth="10"
android:viewportHeight="12">
<group
android:scaleX="0.8"
android:pivotX="3"
>
<path
android:pathData="M5.853,11.6645L9.7814,8.075C10.0495,7.8439 10.0734,7.3871 9.84,7.1191C9.6067,6.8511 9.1615,6.824 8.9084,7.0724L6.0712,9.6665L6.0712,0.8398C6.0712,0.468 5.7781,0.1667 5.4165,0.1667C5.0549,0.1667 4.7618,0.468 4.7618,0.8398L4.7618,9.6665L1.9246,7.0724C1.6715,6.824 1.2271,6.8576 0.9937,7.1256C0.7604,7.3936 0.7835,7.8439 1.0516,8.075L4.98,11.6645C5.2976,11.8989 5.5827,11.8802 5.853,11.6645L5.853,11.6645Z"
android:fillColor="#1C1C1E"/>
</group>
</vector>

View file

@ -1,9 +1,16 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="14dp"
android:height="11dp"
android:viewportWidth="14"
android:viewportHeight="11">
<path
android:fillColor="#F9F9FA"
android:pathData="M13.7974,4.9765L9.4899,0.2623C9.2126,-0.0594 8.6644,-0.088 8.3428,0.192C8.0212,0.472 7.9888,1.0063 8.2869,1.3099L11.3997,4.7146L0.8077,4.7146C0.3616,4.7146 0,5.0664 0,5.5003C0,5.9342 0.3616,6.2859 0.8077,6.2859L11.3997,6.2859L8.2869,9.6906C7.9888,9.9942 8.0291,10.5275 8.3507,10.8076C8.6723,11.0876 9.2126,11.0599 9.4899,10.7382L13.7974,6.0241C14.0786,5.6429 14.0562,5.3008 13.7974,4.9765L13.7974,4.9765Z" />
android:height="14dp"
android:viewportWidth="12"
android:viewportHeight="10">
<group
android:scaleX="1"
android:scaleY="0.8"
android:pivotY="7"
>
<path
android:pathData="M11.6645,4.147L8.075,0.2186C7.8439,-0.0495 7.3871,-0.0734 7.1191,0.16C6.8511,0.3933 6.824,0.8385 7.0724,1.0916L9.6665,3.9288L0.8398,3.9288C0.468,3.9288 0.1667,4.2219 0.1667,4.5835C0.1667,4.9451 0.468,5.2382 0.8398,5.2382L9.6664,5.2382L7.0724,8.0754C6.824,8.3285 6.8576,8.7729 7.1256,9.0063C7.3936,9.2396 7.8439,9.2165 8.075,8.9485L11.6645,5.02C11.8989,4.7024 11.8802,4.4173 11.6645,4.147L11.6645,4.147Z"
android:fillColor="#1C1C1E"/>
</group>
</vector>

View file

@ -34,9 +34,12 @@
android:id="@+id/sv_wallet"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true">
android:fillViewport="true"
android:overScrollMode="never"
>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/cl_wallet"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="33dp">
@ -54,12 +57,21 @@
android:src="@drawable/card_default"
app:layout_constraintTop_toTopOf="parent" />
<androidx.recyclerview.widget.RecyclerView
android:layout_marginTop="12dp"
android:id="@+id/rv_pending_transaction"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:nestedScrollingEnabled="false"
android:overScrollMode="never"
app:layout_constraintTop_toBottomOf="@id/iv_card"/>
<include
android:id="@+id/l_card_balance"
layout="@layout/card_balance"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@id/iv_card" />
app:layout_constraintTop_toBottomOf="@id/rv_pending_transaction" />
<include

View file

@ -0,0 +1,55 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/fl_pending_transaction"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.google.android.material.card.MaterialCardView
android:id="@+id/card_pending_transaction"
android:layout_width="match_parent"
android:layout_height="36dp"
android:background="@android:color/white"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:layout_marginTop="2dp"
android:layout_marginBottom="2dp"
android:elevation="3dp">
<LinearLayout
android:gravity="center_vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingStart="16dp"
android:paddingEnd="16dp"
>
<ImageView
android:id="@+id/iv_pending_transaction"
android:layout_marginEnd="12dp"
android:layout_width="11dp"
android:layout_height="11dp"
android:src="@drawable/ic_arrow_right"
/>
<TextView
android:id="@+id/tv_pending_transaction"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:text="Receiving 0.001 BTS from "
android:textSize="12sp"/>
<TextView
android:id="@+id/tv_pending_transaction_address"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="middle"
android:singleLine="true"
android:textSize="12sp"
android:paddingStart="2dp"
tools:text="0x4313bcd3425235bda1232141"/>
</LinearLayout>
</com.google.android.material.card.MaterialCardView>
</FrameLayout>

View file

@ -25,6 +25,8 @@
<string name="wallet_no_account">Account is not created</string>
<string name="wallet_no_account_description">Load %1s+ %2s to create account</string>
<string name="wallet_tokens">Tokens</string>
<string name="wallet_pending_transaction_incoming">Receiving %1s %2s from </string>
<string name="wallet_pending_transaction_outcoming">Sending %1s %2s to </string>
<string name="wallet_dialog_pay_id_button">Create PayID</string>
<string name="wallet_create_payid_empty">Enter desired PayID first</string>