Updated on 2026-08-14

This commit is contained in:
Tangem 2022-09-15 20:37:05 +03:00
commit e1ea317688
18 changed files with 170 additions and 112 deletions

View file

@ -5,19 +5,23 @@ import androidx.annotation.ColorInt
import androidx.core.graphics.luminance
import androidx.core.graphics.toColorInt
import com.tangem.blockchain.common.Token
import com.tangem.wallet.R
@ColorInt
fun Token.getColor(): Int {
return try {
("#" + this.contractAddress.subSequence(2..7).toString())
.toColorInt()
fun Token.getColor(isTestnet: Boolean = false): Int {
val defaultColor = "#C7C7CC".toColorInt() // equivalent to R.color.lightGray4
return if (isTestnet)
defaultColor
else try {
("#" + this.contractAddress.subSequence(2..7).toString()).toColorInt()
} catch (exception: Exception) {
R.color.lightGray4
defaultColor
}
}
@ColorInt
fun Token.getTextColor(): Int {
return if (this.getColor().luminance > 0.5) Color.BLACK else Color.WHITE
fun Token.getTextColor(isTestnet: Boolean = false): Int = when {
isTestnet -> Color.WHITE
this.getColor().luminance > 0.5 -> Color.BLACK
else -> Color.WHITE
}

View file

@ -113,15 +113,11 @@ sealed class FeeActionUi : SendScreenActionUi {
}
sealed class FeeAction : SendScreenAction {
enum class Error {
ADDRESS_OR_AMOUNT_IS_EMPTY,
REQUEST_FAILED
}
object RequestFee : FeeAction()
sealed class FeeCalculation : FeeAction() {
data class SetFeeResult(val fee: List<Amount>) : FeeCalculation()
data class SetFeeError(val error: Error) : FeeCalculation()
object ClearResult : FeeCalculation()
}
data class ChangeLayoutVisibility(
@ -160,6 +156,12 @@ sealed class SendAction : SendScreenAction {
data class CardSdkError(val error: TangemSdkError): Dialog()
data class BlockchainSdkError(val error: com.tangem.blockchain.common.BlockchainSdkError): Dialog()
}
data class RequestFeeError(
val error: com.tangem.blockchain.common.BlockchainSdkError,
val onRetry: () -> Unit,
) : Dialog()
object Hide : Dialog()
}

View file

@ -2,6 +2,7 @@ package com.tangem.tap.features.send.redux.middlewares
import com.tangem.blockchain.common.Amount
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.BlockchainSdkError
import com.tangem.blockchain.common.TransactionSender
import com.tangem.blockchain.extensions.Result
import com.tangem.common.extensions.isZero
@ -31,8 +32,7 @@ class RequestFeeMiddleware {
val scanResponse = appState.globalState.scanResponse ?: return
if (!SendState.isReadyToRequestFee()) {
dispatch(FeeAction.FeeCalculation.SetFeeError(FeeAction.Error.ADDRESS_OR_AMOUNT_IS_EMPTY))
// dispatch(FeeAction.ChangeLayoutVisibility(main = false, chipGroup = true))
dispatch(FeeAction.FeeCalculation.ClearResult)
dispatch(ReceiptAction.RefreshReceipt)
dispatch(SendAction.ChangeSendButtonState(sendState.getButtonState()))
return
@ -66,14 +66,21 @@ class RequestFeeMiddleware {
}
}
is Result.Failure -> {
dispatch(FeeAction.FeeCalculation.SetFeeError(FeeAction.Error.REQUEST_FAILED))
dispatch(FeeAction.FeeCalculation.ClearResult)
dispatch(FeeAction.ChangeLayoutVisibility(main = false))
val blockchainSdkError = feeResult.error as? BlockchainSdkError ?: return@withContext
dispatch(
SendAction.Dialog.RequestFeeError(
error = blockchainSdkError,
onRetry = { dispatch(FeeAction.RequestFee) },
),
)
}
}
dispatch(AmountActionUi.CheckAmountToSend)
}
}
}
}
@ -93,13 +100,13 @@ class FeeMock {
suspend fun feeStandard(blockchain: Blockchain): List<Amount> = listOf(
Amount(0.001500.toBigDecimal(), blockchain),
Amount(0.0030.toBigDecimal(), blockchain),
Amount(0.0045001.toBigDecimal(), blockchain)
Amount(0.0045001.toBigDecimal(), blockchain),
)
suspend fun feeStandardBig(blockchain: Blockchain): List<Amount> = listOf(
Amount(1.76.toBigDecimal(), blockchain),
Amount(2.30.toBigDecimal(), blockchain),
Amount(3.45.toBigDecimal(), blockchain)
Amount(3.45.toBigDecimal(), blockchain),
)
suspend fun feeSingle(blockchain: Blockchain): List<Amount> = listOf(Amount(0.0015.toBigDecimal(), blockchain))

View file

@ -36,7 +36,7 @@ class FeeReducer : SendInternalReducer {
private fun handleAction(action: FeeAction, sendState: SendState, state: FeeState): SendState {
val result = when (action) {
is FeeAction.RequestFee -> {
state.copy(error = null)
state
}
is FeeAction.ChangeLayoutVisibility -> {
fun getVisibility(current: Boolean, proposed: Boolean?): Boolean = proposed ?: current
@ -56,7 +56,6 @@ class FeeReducer : SendInternalReducer {
selectedFeeType = feeType,
feeList = fees,
currentFee = currentFee,
error = null,
feeIsApproximate = isFeeApproximate(sendState),
)
} else {
@ -67,16 +66,14 @@ class FeeReducer : SendInternalReducer {
selectedFeeType = feeType,
feeList = fees,
currentFee = currentFee,
error = null,
feeIsApproximate = isFeeApproximate(sendState),
)
}
}
is FeeAction.FeeCalculation.SetFeeError -> {
FeeAction.FeeCalculation.ClearResult -> {
state.copy(
feeList = null,
currentFee = null,
error = action.error,
)
}
}

View file

@ -59,10 +59,10 @@ private class SendReducer : SendInternalReducer {
is SendAction.Dialog.TezosWarningDialog -> sendState.copy(dialog = action)
is SendAction.Dialog.SendTransactionFails.CardSdkError -> sendState.copy(dialog = action)
is SendAction.Dialog.SendTransactionFails.BlockchainSdkError -> sendState.copy(dialog = action)
is SendAction.Dialog.RequestFeeError -> sendState.copy(dialog = action)
is SendAction.Dialog.Hide -> sendState.copy(dialog = null)
is SendAction.Warnings.Set -> sendState.copy(sendWarningsList = action.warningList)
is SendAction.SendSpecificTransaction ->
handleSendSpecificTransactionAction(action, sendState)
is SendAction.SendSpecificTransaction -> handleSendSpecificTransactionAction(action, sendState)
else -> return sendState
}

View file

@ -1,7 +1,6 @@
package com.tangem.tap.features.send.redux.states
import com.tangem.blockchain.common.Amount
import com.tangem.tap.features.send.redux.FeeAction
import java.math.BigDecimal
/**
@ -21,12 +20,11 @@ data class FeeState(
val controlsLayoutIsVisible: Boolean = false,
val feeChipGroupIsVisible: Boolean = true,
val includeFeeSwitcherIsEnabled: Boolean = true,
val error: FeeAction.Error? = null,
) : SendScreenState {
override val stateId: StateId = StateId.FEE
fun isReady(): Boolean = error == null && currentFee != null
fun isReady(): Boolean = currentFee != null
fun getCurrentFeeValue(): BigDecimal = currentFee?.value ?: BigDecimal.ZERO
}

View file

@ -0,0 +1,32 @@
package com.tangem.tap.features.send.ui.dialogs
import android.content.Context
import androidx.appcompat.app.AlertDialog
import com.tangem.tap.common.feedback.SendTransactionFailedEmail
import com.tangem.tap.common.redux.global.GlobalAction
import com.tangem.tap.features.send.redux.SendAction
import com.tangem.tap.store
import com.tangem.wallet.R
/**
[REDACTED_AUTHOR]
*/
class RequestFeeErrorDialog {
companion object {
fun create(context: Context, dialog: SendAction.Dialog.RequestFeeError): AlertDialog {
val errorMessage = dialog.error.customMessage
return AlertDialog.Builder(context).apply {
setTitle(R.string.send_error_fee_request_failed)
setMessage(context.getString(R.string.alert_failed_to_send_transaction_message, errorMessage))
setNegativeButton(R.string.alert_button_send_feedback) { _, _ ->
store.dispatch(GlobalAction.SendEmail(SendTransactionFailedEmail(errorMessage)))
}
setPositiveButton(R.string.common_retry) { _, _ -> dialog.onRetry() }
setNeutralButton(R.string.common_cancel) { _, _ -> }
setOnDismissListener { store.dispatch(SendAction.Dialog.Hide) }
}.create()
}
}
}

View file

@ -20,7 +20,6 @@ import com.tangem.tap.domain.MultiMessageError
import com.tangem.tap.domain.assembleErrors
import com.tangem.tap.features.BaseStoreFragment
import com.tangem.tap.features.send.redux.AddressPayIdVerifyAction.Error
import com.tangem.tap.features.send.redux.FeeAction
import com.tangem.tap.features.send.redux.SendAction
import com.tangem.tap.features.send.redux.states.AddressPayIdState
import com.tangem.tap.features.send.redux.states.AmountState
@ -35,12 +34,12 @@ import com.tangem.tap.features.send.redux.states.TransactionExtrasState
import com.tangem.tap.features.send.redux.states.XlmMemoType
import com.tangem.tap.features.send.ui.FeeUiHelper
import com.tangem.tap.features.send.ui.SendFragment
import com.tangem.tap.features.send.ui.dialogs.RequestFeeErrorDialog
import com.tangem.tap.features.send.ui.dialogs.SendTransactionFailsDialog
import com.tangem.tap.features.send.ui.dialogs.TezosWarningDialog
import com.tangem.tap.features.wallet.redux.WalletState.Companion.ROUGH_SIGN
import com.tangem.tap.features.wallet.redux.WalletState.Companion.UNKNOWN_AMOUNT_SIGN
import com.tangem.tap.features.wallet.ui.adapters.WarningMessagesAdapter
import com.tangem.tap.store
import com.tangem.wallet.R
/**
@ -62,10 +61,7 @@ class SendStateSubscriber(fragment: BaseStoreFragment) :
when (it) {
StateId.SEND_SCREEN -> handleSendScreen(fg, state)
StateId.ADDRESS_PAY_ID -> handleAddressPayIdState(fg, state.addressPayIdState)
StateId.TRANSACTION_EXTRAS -> handleTransactionExtrasState(
fg,
state.transactionExtrasState
)
StateId.TRANSACTION_EXTRAS -> handleTransactionExtrasState(fg, state.transactionExtrasState)
StateId.AMOUNT -> handleAmountState(fg, state.amountState)
StateId.FEE -> handleFeeState(fg, state.feeState)
StateId.RECEIPT -> handleReceiptState(fg, state.receiptState)
@ -144,6 +140,12 @@ class SendStateSubscriber(fragment: BaseStoreFragment) :
dialog?.show()
}
}
is SendAction.Dialog.RequestFeeError -> {
if (dialog == null) {
dialog = RequestFeeErrorDialog.create(fg.requireContext(), state.dialog)
dialog?.show()
}
}
else -> {
dialog?.dismiss()
dialog = null
@ -161,7 +163,7 @@ class SendStateSubscriber(fragment: BaseStoreFragment) :
toolbar.title = fg.getString(
R.string.send_title_currency_format,
state.amountState.mainCurrency.currencySymbol
state.amountState.mainCurrency.currencySymbol,
)
}
@ -233,11 +235,11 @@ class SendStateSubscriber(fragment: BaseStoreFragment) :
val balanceText = when (state.mainCurrency.type) {
MainCurrencyType.FIAT -> fg.getString(
R.string.send_balance_subtitle_format,
state.viewBalanceValue, state.mainCurrency.currencySymbol
state.viewBalanceValue, state.mainCurrency.currencySymbol,
).remove(":")
MainCurrencyType.CRYPTO -> fg.getString(
R.string.send_balance_subtitle_format,
state.mainCurrency.currencySymbol, state.viewBalanceValue
state.mainCurrency.currencySymbol, state.viewBalanceValue,
)
}
@ -263,14 +265,6 @@ class SendStateSubscriber(fragment: BaseStoreFragment) :
swIncludeFee.isChecked = state.feeIsIncluded
}
if (state.error == FeeAction.Error.REQUEST_FAILED) {
fg.showRetrySnackbar(
fg.requireContext().getString(R.string.send_error_fee_request_failed)
) {
store.dispatch(FeeAction.RequestFee)
}
}
val chipId = FeeUiHelper.toId(state.selectedFeeType)
if (chipGroup.checkedChipId != chipId && chipId != View.NO_ID) chipGroup.check(chipId)
}
@ -299,7 +293,7 @@ class SendStateSubscriber(fragment: BaseStoreFragment) :
val willSent = getString(
R.string.send_total_subtitle_format,
receipt.willSentCrypto, receipt.symbols.crypto
receipt.willSentCrypto, receipt.symbols.crypto,
)
llTotalContainer.tvWillBeSentValue.update(willSent)
@ -334,7 +328,7 @@ class SendStateSubscriber(fragment: BaseStoreFragment) :
val willSent = getString(
R.string.send_total_subtitle_asset_format,
receipt.symbols.token ?: "", receipt.willSentToken,
receipt.symbols.crypto, receipt.willSentFeeCoin
receipt.symbols.crypto, receipt.willSentFeeCoin,
)
llTotalContainer.tvWillBeSentValue.update(willSent)
}

View file

@ -41,6 +41,7 @@ import com.tangem.tap.features.wallet.redux.WalletState
import com.tangem.tap.features.wallet.redux.WalletState.Companion.UNKNOWN_AMOUNT_SIGN
import com.tangem.tap.features.wallet.ui.adapters.PendingTransactionsAdapter
import com.tangem.tap.features.wallet.ui.adapters.WalletDetailWarningMessagesAdapter
import com.tangem.tap.features.wallet.ui.images.load
import com.tangem.tap.features.wallet.ui.test.TestWalletDetails
import com.tangem.tap.store
import com.tangem.wallet.R

View file

@ -15,6 +15,7 @@ import com.tangem.tap.common.extensions.show
import com.tangem.tap.features.wallet.redux.WalletAction
import com.tangem.tap.features.wallet.redux.WalletData
import com.tangem.tap.features.wallet.ui.BalanceStatus
import com.tangem.tap.features.wallet.ui.images.load
import com.tangem.tap.store
import com.tangem.wallet.R
import com.tangem.wallet.databinding.ItemCurrencyWalletBinding
@ -62,7 +63,7 @@ class WalletAdapter
fun bind(wallet: WalletData) = with(binding) {
val status = wallet.currencyData.status
// Skip changes when on refreshing status
if (status == BalanceStatus.Refreshing || wallet.fiatRateString == null) return@with
if (status == BalanceStatus.Refreshing) return@with
val statusMessage = when (status) {
BalanceStatus.TransactionInProgress -> {

View file

@ -21,7 +21,7 @@ private const val VOYR = "VOYRME"
class CurrencyIconRequest(
private val currencyImageView: ImageFilterView,
private val currencyTextView: TextView,
private val currencyTextView: TextView?,
private val token: Token?,
private val blockchain: Blockchain,
) {
@ -38,7 +38,7 @@ class CurrencyIconRequest(
loadBlockchainIconBase(
onStart = {
currencyImageView.colorFilter = null
}
},
)
}
@ -46,7 +46,7 @@ class CurrencyIconRequest(
loadBlockchainIconBase(
onStart = {
currencyImageView.saturation = 0f
}
},
)
}
@ -57,7 +57,11 @@ class CurrencyIconRequest(
},
onSuccess = {
currencyImageView.colorFilter = null
}
},
onError = {
currencyImageView.setColorFilter(it.getColor())
currencyTextView?.setTextColor(it.getTextColor())
},
)
}
@ -65,7 +69,12 @@ class CurrencyIconRequest(
loadTokenIconBase(
onStart = {
currencyImageView.saturation = 0f
}
},
onError = {
currencyImageView.saturation = 0f
currencyImageView.setColorFilter(it.getColor(true))
currencyTextView?.setTextColor(it.getTextColor(true))
},
)
}
@ -94,15 +103,19 @@ class CurrencyIconRequest(
data = getTokenIcon(token, blockchain),
placeholderRes = R.drawable.shape_circle,
onStart = {
currencyTextView.text = token.symbol.take(1)
currencyTextView.setTextColor(token.getTextColor())
currencyTextView?.text = token.name.take(1)
currencyTextView?.setTextColor(token.getTextColor())
onStart(token)
},
onSuccess = {
currencyTextView.text = null
currencyTextView?.text = null
onSuccess(token)
},
onError = { onError(token) },
onError = {
// for some reason the onStart doesn't call if an error occurs
currencyTextView?.text = token.name.take(1)
onError(token)
},
)
}
}
@ -122,7 +135,7 @@ private inline fun ImageView.loadIcon(
.listener(
onStart = { onStart() },
onSuccess = { _, _ -> onSuccess() },
onError = { _, _ -> onError() }
onError = { _, _ -> onError() },
)
.target(imageView = this)
.build()

View file

@ -4,13 +4,11 @@ import android.content.Context
import android.util.AttributeSet
import android.view.LayoutInflater
import android.widget.TextView
import androidx.annotation.DrawableRes
import androidx.constraintlayout.utils.widget.ImageFilterView
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.view.isVisible
import com.tangem.blockchain.common.DerivationStyle
import com.tangem.tangem_sdk_new.extensions.dpToPx
import com.tangem.tap.common.extensions.getRoundIconRes
import com.tangem.tap.features.wallet.models.Currency
import com.tangem.wallet.databinding.ViewCurrencyIconBinding
import kotlin.math.roundToInt
@ -25,46 +23,50 @@ class CurrencyIconView @JvmOverloads constructor(
this,
)
private val currencyImageView: ImageFilterView
val currencyImageView: ImageFilterView
get() = binding.ivCurrency
private val currencyTextView: TextView
val currencyTextView: TextView
get() = binding.tvTokenLetter
private var isBlockchainIconVisible: Boolean
get() = binding.ivBlockchain.isVisible
set(value) = binding.ivBlockchain::isVisible.set(value)
val blockchainBadge: ImageFilterView
get() = binding.ivBlockchainBadge
private var isBadgeVisible: Boolean
get() = binding.badge.isVisible
set(value) = binding.badge::isVisible.set(value)
var isBlockchainBadgeVisible: Boolean
get() = binding.ivBlockchainBadge.isVisible
set(value) = binding.ivBlockchainBadge::isVisible.set(value)
@DrawableRes
private var blockchainIconRes: Int? = null
set(value) {
if (value != null && value != field && isBlockchainIconVisible) {
binding.ivBlockchain.setImageResource(value)
field = value
}
}
var isCustomCurrencyBadgeVisible: Boolean
get() = binding.customBadge.isVisible
set(value) = binding.customBadge::isVisible.set(value)
init {
minWidth = dpToPx(48f).roundToInt()
minHeight = dpToPx(48f).roundToInt()
}
}
fun load(
currency: Currency,
derivationStyle: DerivationStyle?,
) {
isBlockchainIconVisible = currency.isToken()
isBadgeVisible = currency.isCustomCurrency(derivationStyle)
blockchainIconRes = currency.blockchain.getRoundIconRes()
fun CurrencyIconView.load(
currency: Currency,
derivationStyle: DerivationStyle?,
) {
isCustomCurrencyBadgeVisible = currency.isCustomCurrency(derivationStyle)
CurrencyIconRequest(
currencyImageView = currencyImageView,
currencyTextView = currencyTextView,
token = (currency as? Currency.Token)?.token,
blockchain = currency.blockchain,
).load()
if (currency.isToken()) {
// load a blockchain icon into the blockchain badge
isBlockchainBadgeVisible = true
CurrencyIconRequest(
currencyImageView = currencyImageView,
currencyTextView = currencyTextView,
token = (currency as? Currency.Token)?.token,
currencyImageView = blockchainBadge,
currencyTextView = null,
token = null,
blockchain = currency.blockchain,
).load()
}

View file

@ -35,12 +35,13 @@
android:textColor="@android:color/white"
android:textSize="25sp"
android:textStyle="bold"
android:translationY="-1dp"
tools:text="J" />
</androidx.cardview.widget.CardView>
<ImageView
android:id="@+id/iv_blockchain"
<androidx.constraintlayout.utils.widget.ImageFilterView
android:id="@+id/iv_blockchain_badge"
android:layout_width="18dp"
android:layout_height="18dp"
android:background="@drawable/shape_circle"
@ -54,7 +55,7 @@
tools:visibility="visible" />
<View
android:id="@+id/badge"
android:id="@+id/custom_badge"
android:layout_width="10dp"
android:layout_height="10dp"
android:background="@drawable/shape_badge_custom_currency"

View file

@ -2,7 +2,7 @@ ext.versions = [
kotlin : '1.6.10',
build_gradle : '7.1.3',
tangem_card_sdk : 'develop-159',
tangem_blockchain_sdk: 'develop-107',
tangem_blockchain_sdk: 'develop-111',
// tangem_blockchain_sdk: '0.0.1',
]

View file

@ -5,7 +5,7 @@ import com.tangem.blockchain.common.Blockchain
fun Blockchain.Companion.fromNetworkId(networkId: String): Blockchain? {
return when (networkId) {
"arbitrum-one" -> Blockchain.Arbitrum
"arbitrum-one/test" -> Blockchain.ArbitrumTestnet
"arbitrum/test" -> Blockchain.ArbitrumTestnet
"avalanche", "avalanche-2" -> Blockchain.Avalanche
"avalanche/test", "avalanche-2/test" -> Blockchain.AvalancheTestnet
"binancecoin" -> Blockchain.Binance
@ -40,6 +40,7 @@ fun Blockchain.Companion.fromNetworkId(networkId: String): Blockchain? {
"xdai" -> Blockchain.Gnosis
"polkadot" -> Blockchain.Polkadot
"polkadot/test" -> Blockchain.PolkadotTestnet
"kusama" -> Blockchain.Kusama
else -> null
}
}
@ -84,6 +85,7 @@ fun Blockchain.toNetworkId(): String {
Blockchain.Gnosis -> "xdai"
Blockchain.Polkadot -> "polkadot"
Blockchain.PolkadotTestnet -> "polkadot/test"
Blockchain.Kusama -> "kusama"
}
}
@ -101,15 +103,16 @@ fun Blockchain.toCoinId(): String {
Blockchain.Avalanche, Blockchain.AvalancheTestnet -> "avalanche-2"
Blockchain.Solana, Blockchain.SolanaTestnet -> "solana"
Blockchain.Fantom, Blockchain.FantomTestnet -> "fantom"
Blockchain.Tron, Blockchain.TronTestnet -> "tron"
Blockchain.Polkadot, Blockchain.PolkadotTestnet -> "polkadot"
Blockchain.Ducatus -> "ducatus"
Blockchain.Litecoin -> "litecoin"
Blockchain.RSK -> "rootstock"
Blockchain.Tezos -> "tezos"
Blockchain.XRP -> "ripple"
Blockchain.Dogecoin -> "dogecoin"
Blockchain.Tron, Blockchain.TronTestnet -> "tron"
Blockchain.Gnosis -> "xdai"
Blockchain.Polkadot, Blockchain.PolkadotTestnet -> "polkadot"
Blockchain.Kusama -> "kusama"
Blockchain.Unknown -> "unknown"
}
}

View file

@ -18,8 +18,7 @@ fun Card.supportedBlockchains(): List<Blockchain> {
Blockchain.fromCurve(EllipticCurve.Secp256k1)
}
else -> {
Blockchain.fromCurve(EllipticCurve.Secp256k1) +
Blockchain.fromCurve(EllipticCurve.Ed25519)
(Blockchain.fromCurve(EllipticCurve.Secp256k1) + Blockchain.fromCurve(EllipticCurve.Ed25519)).distinct()
}
}
val filtered = supportedBlockchains.filter { isTestCard == it.isTestnet() }

View file

@ -0,0 +1,20 @@
package com.tangem.domain.features
import com.google.common.truth.Truth
import com.tangem.blockchain.common.Blockchain
import com.tangem.domain.common.extensions.fromNetworkId
import com.tangem.domain.common.extensions.toNetworkId
import org.junit.Test
class BlockchainTests {
@Test
fun allNetworkIdsAreImplemented() {
val unimplementedIds = Blockchain.values()
.toMutableList()
.apply { remove(Blockchain.Unknown) }
.map { it to Blockchain.fromNetworkId(it.toNetworkId()) }
.mapNotNull { if(it.second == null) it.first else null }
Truth.assertThat(unimplementedIds).isEmpty()
}
}

View file

@ -1,16 +0,0 @@
package com.tangem.domain.features
import org.junit.Assert.assertEquals
import org.junit.Test
/**
* Example local unit test, which will execute on the development machine (host).
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
class ExampleUnitTest {
@Test
fun addition_isCorrect() {
assertEquals(4, 2 + 2)
}
}