diff --git a/app/src/main/java/com/tangem/data/Blockchain.java b/app/src/main/java/com/tangem/data/Blockchain.java index 11cb8eecf2..9e95661c57 100644 --- a/app/src/main/java/com/tangem/data/Blockchain.java +++ b/app/src/main/java/com/tangem/data/Blockchain.java @@ -12,6 +12,7 @@ public enum Blockchain { Ethereum("ETH", "ETH", 1.0, R.drawable.ic_logo_ethereum, "Ethereum"), EthereumTestNet("ETH/test", "ETH", 1.0, R.drawable.ic_logo_ethereum_testnet, "Ethereum Testnet"), Token("Token", "ETH", 1.0, R.drawable.ic_logo_bat_token, "Ethereum"), + NftToken("NftToken", "", 1.0, R.drawable.ic_logo_bat_token, "Ethereum"), BitcoinCash("BCH", "BCH", 100000000.0, R.drawable.ic_logo_bitcoin_cash, "Bitcoin Cash"), Litecoin("LTC", "LTC", 100000000.0, R.drawable.ic_logo_bitcoin, "Litecoin"), Rootstock("RSK", "RBTC", 1.0, R.drawable.ic_logo_bitcoin, "Rootstock"), diff --git a/app/src/main/java/com/tangem/domain/wallet/CoinEngine.java b/app/src/main/java/com/tangem/domain/wallet/CoinEngine.java index 1f53e9b4bd..9340405a2e 100644 --- a/app/src/main/java/com/tangem/domain/wallet/CoinEngine.java +++ b/app/src/main/java/com/tangem/domain/wallet/CoinEngine.java @@ -361,5 +361,9 @@ public abstract class CoinEngine { return true; } + public boolean isNftToken() { + return false; + } + } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/domain/wallet/CoinEngineFactory.kt b/app/src/main/java/com/tangem/domain/wallet/CoinEngineFactory.kt index adf693ade8..9d99f0121f 100644 --- a/app/src/main/java/com/tangem/domain/wallet/CoinEngineFactory.kt +++ b/app/src/main/java/com/tangem/domain/wallet/CoinEngineFactory.kt @@ -8,6 +8,7 @@ import com.tangem.domain.wallet.token.TokenEngine import com.tangem.domain.wallet.bch.BtcCashEngine import com.tangem.data.Blockchain import com.tangem.domain.wallet.ltc.LtcEngine +import com.tangem.domain.wallet.nftToken.NftTokenEngine import com.tangem.domain.wallet.rsk.RskEngine import com.tangem.domain.wallet.rsk.RskTokenEngine @@ -28,6 +29,7 @@ object CoinEngineFactory { Blockchain.BitcoinCash -> BtcCashEngine() Blockchain.Ethereum, Blockchain.EthereumTestNet -> EthEngine() Blockchain.Token -> TokenEngine() + Blockchain.NftToken -> NftTokenEngine() Blockchain.Litecoin -> LtcEngine() Blockchain.Rootstock -> RskEngine() Blockchain.RootstockToken -> RskTokenEngine() @@ -47,6 +49,8 @@ object CoinEngineFactory { EthEngine(context) else if (Blockchain.Token == context.blockchain) TokenEngine(context) + else if (Blockchain.NftToken == context.blockchain) + NftTokenEngine(context) else if (Blockchain.Litecoin == context.blockchain) LtcEngine(context) else if (Blockchain.Rootstock == context.blockchain) diff --git a/app/src/main/java/com/tangem/domain/wallet/TangemContext.java b/app/src/main/java/com/tangem/domain/wallet/TangemContext.java index 7e642c01b3..1537c56082 100644 --- a/app/src/main/java/com/tangem/domain/wallet/TangemContext.java +++ b/app/src/main/java/com/tangem/domain/wallet/TangemContext.java @@ -11,7 +11,7 @@ import com.tangem.tangemcard.data.TangemCardExtensionsKt; public class TangemContext { -// public static final String EXTRA_BLOCKCHAIN_DATA = "BLOCKCHAIN_DATA"; + // public static final String EXTRA_BLOCKCHAIN_DATA = "BLOCKCHAIN_DATA"; private Context context; private TangemCard card; private CoinData coinData; @@ -28,13 +28,15 @@ public class TangemContext { public Blockchain getBlockchain() { if (card == null) return Blockchain.Unknown; - Blockchain blockchain=Blockchain.fromId(card.getBlockchainID()); - if( (blockchain==Blockchain.Ethereum || blockchain==Blockchain.EthereumTestNet)&& card.isToken() ) - { - return Blockchain.Token; + Blockchain blockchain = Blockchain.fromId(card.getBlockchainID()); + if ((blockchain == Blockchain.Ethereum || blockchain == Blockchain.EthereumTestNet) && card.isToken()) { + if (card.getTokenSymbol().startsWith("NFT:")) { + return Blockchain.NftToken; + } else { + return Blockchain.Token; + } } - if( (blockchain==Blockchain.Rootstock)&& card.isToken() ) - { + if ((blockchain == Blockchain.Rootstock) && card.isToken()) { return Blockchain.RootstockToken; } return blockchain; @@ -48,13 +50,15 @@ public class TangemContext { // private String blockchainName = ""; public String getBlockchainName() { - Blockchain blockchain=getBlockchain(); - if( blockchain==Blockchain.Token || blockchain==Blockchain.RootstockToken ) { + Blockchain blockchain = getBlockchain(); + if (blockchain == Blockchain.Token || blockchain == Blockchain.RootstockToken) { String token = card.getTokenSymbol(); return token + "
" + getBlockchain().getOfficialName() + " smart contract token"; - }else { - return blockchain.getOfficialName(); } + if (blockchain == Blockchain.NftToken) { + return card.getTokenSymbol().substring(4) + "
" + getBlockchain().getOfficialName() + " smart contract token"; + } + return blockchain.getOfficialName(); } public Context getContext() { @@ -84,6 +88,7 @@ public class TangemContext { public void setError(String error) { this.error = error; } + public void setError(int valueId) { this.error = getString(valueId); } @@ -93,7 +98,7 @@ public class TangemContext { } public boolean hasError() { - return error!=null && !error.isEmpty(); + return error != null && !error.isEmpty(); } public void setMessage(String value) { @@ -154,13 +159,13 @@ public class TangemContext { public void setDenomination(byte[] denomination) { try { - CoinEngine engine= CoinEngineFactory.INSTANCE.create(getBlockchain()); - CoinEngine.InternalAmount internalAmount=engine.convertToInternalAmount(denomination); - CoinEngine.Amount amount=engine.convertToAmount(internalAmount); - card.setDenomination(denomination,amount.toString()); + CoinEngine engine = CoinEngineFactory.INSTANCE.create(getBlockchain()); + CoinEngine.InternalAmount internalAmount = engine.convertToInternalAmount(denomination); + CoinEngine.Amount amount = engine.convertToAmount(internalAmount); + card.setDenomination(denomination, amount.toString()); } catch (Exception e) { e.printStackTrace(); - card.setDenomination(denomination,"N/A"); + card.setDenomination(denomination, "N/A"); } } diff --git a/app/src/main/java/com/tangem/domain/wallet/nftToken/NftTokenEngine.java b/app/src/main/java/com/tangem/domain/wallet/nftToken/NftTokenEngine.java new file mode 100644 index 0000000000..2e9e73e016 --- /dev/null +++ b/app/src/main/java/com/tangem/domain/wallet/nftToken/NftTokenEngine.java @@ -0,0 +1,359 @@ +package com.tangem.domain.wallet.nftToken; + +import android.net.Uri; +import android.os.Bundle; +import android.text.InputFilter; +import android.util.Log; + +import com.tangem.data.Blockchain; +import com.tangem.data.network.ServerApiInfura; +import com.tangem.data.network.model.InfuraResponse; +import com.tangem.domain.wallet.BTCUtils; +import com.tangem.domain.wallet.BalanceValidator; +import com.tangem.domain.wallet.CoinData; +import com.tangem.domain.wallet.CoinEngine; +import com.tangem.domain.wallet.Keccak256; +import com.tangem.domain.wallet.TangemContext; +import com.tangem.domain.wallet.eth.EthData; +import com.tangem.domain.wallet.token.TokenData; +import com.tangem.tangemcard.data.TangemCard; +import com.tangem.tangemcard.tasks.SignTask; +import com.tangem.wallet.R; + +import java.math.BigInteger; + + +public class NftTokenEngine extends CoinEngine { + + private static final String TAG = NftTokenEngine.class.getSimpleName(); + public TokenData coinData = null; + + public NftTokenEngine(TangemContext ctx) throws Exception { + super(ctx); + if (ctx.getCoinData() == null) { + coinData = new TokenData(); + ctx.setCoinData(coinData); + } else if (ctx.getCoinData() instanceof TokenData) { + coinData = (TokenData) ctx.getCoinData(); + } else if (ctx.getCoinData() instanceof EthData) { + // special case with receive card data substitution from server at the moment + Bundle B = new Bundle(); + ctx.getCoinData().saveToBundle(B); + coinData = new TokenData(); + coinData.loadFromBundle(B); + ctx.setCoinData(coinData); + } else { + throw new Exception("Invalid type of Blockchain data for " + this.getClass().getSimpleName()); + } + } + + public NftTokenEngine() { + super(); + } + + public Blockchain getBlockchain() { + return Blockchain.NftToken; + } + + @Override + public boolean awaitingConfirmation() { + return false; + } + + + @Override + public String getBalanceHTML() { + if (hasBalanceInfo()) { + if (isBalanceNotZero()) { + return "AUTHENTIC";// + getBalanceCurrency(); + } else { + return "NOT FOUND"; + } + } else { + return ""; + } + } + + @Override + public String getBalanceCurrency() { + return ctx.getCard().getTokenSymbol().substring(4); + } + + @Override + public InputFilter[] getAmountInputFilters() { + return new InputFilter[0]; + } + + @Override + public String getOfflineBalanceHTML() { + return ctx.getString(R.string.not_implemented); + } + + protected String getContractAddress(TangemCard card) { + return card.getContractAddress(); + } + + public boolean isNeedCheckNode() { + return false; + } + + @Override + public String getBalanceEquivalent() { + return null; + } + + @Override + public boolean validateAddress(String address) { + if (address == null || address.isEmpty()) { + return false; + } + + if (!address.startsWith("0x") && !address.startsWith("0X")) { + return false; + } + + if (address.length() != 42) { + return false; + } + + return true; + } + + @Override + public boolean isBalanceNotZero() { + if (coinData == null) return false; + if (coinData.getBalanceInInternalUnits() == null) return false; + return coinData.getBalanceInInternalUnits().notZero(); + } + + @Override + public String calculateAddress(byte[] pkUncompressed) { + Keccak256 kec = new Keccak256(); + int lenPk = pkUncompressed.length; + if (lenPk < 2) { + throw new IllegalArgumentException("Uncompress public key length is invalid"); + } + byte[] cleanKey = new byte[lenPk - 1]; + for (int i = 0; i < cleanKey.length; ++i) { + cleanKey[i] = pkUncompressed[i + 1]; + } + byte[] r = kec.digest(cleanKey); + + byte[] address = new byte[20]; + for (int i = 0; i < 20; ++i) { + address[i] = r[i + 12]; + } + + return String.format("0x%s", BTCUtils.toHex(address)); + } + + @Override + public Amount convertToAmount(InternalAmount internalAmount) throws Exception { + throw new Exception("Not implemented"); + } + + @Override + public Amount convertToAmount(String strAmount, String currency) { + return null; + } + + @Override + public InternalAmount convertToInternalAmount(Amount amount) throws Exception { + throw new Exception("Not implemented"); + } + + @Override + public InternalAmount convertToInternalAmount(byte[] bytes) throws Exception { + throw new Exception("Not implemented"); + } + + @Override + public byte[] convertToByteArray(InternalAmount amount) throws Exception { + throw new Exception("Not implemented"); + } + + @Override + public CoinData createCoinData() { + return new TokenData(); + } + + @Override + public String getUnspentInputsDescription() { + return ""; + } + + @Override + public boolean hasBalanceInfo() { + return coinData != null && coinData.getBalanceInInternalUnits() != null; + } + + + @Override + public Uri getWalletExplorerUri() { + return Uri.parse("https://etherscan.io/token/" + getContractAddress(ctx.getCard()) + "?a=" + ctx.getCoinData().getWallet()); + } + + @Override + public Uri getShareWalletUri() { + return Uri.parse("ethereum:" + ctx.getCoinData().getWallet()); + } + + @Override + public boolean isExtractPossible() { + return false; + } + + @Override + public boolean checkNewTransactionAmount(Amount amount) { + return false; + } + + @Override + public boolean checkNewTransactionAmountAndFee(Amount amount, Amount fee, Boolean isFeeIncluded) { + return false; + } + + @Override + public boolean validateBalance(BalanceValidator balanceValidator) { + if (coinData.getBalanceInInternalUnits() == null) { + balanceValidator.setScore(0); + balanceValidator.setFirstLine("No connection"); + balanceValidator.setSecondLine("Authenticity cannot be verified. Swipe down to refresh."); + return false; + } + + if (coinData.isBalanceReceived()) { + if (isBalanceNotZero()) { + balanceValidator.setScore(100); + balanceValidator.setFirstLine("Verified in blockchain"); + balanceValidator.setSecondLine(""); + } else { + balanceValidator.setScore(0); + balanceValidator.setFirstLine("Authenticity was not verified"); + balanceValidator.setSecondLine(""); + } + } + + return true; + } + + @Override + public Amount getBalance() { + return null; + } + + + @Override + public String evaluateFeeEquivalent(String fee) { + return null; + } + + @Override + public String getFeeCurrency() { + return null; + } + + @Override + public SignTask.TransactionToSign constructTransaction(Amount amountValue, Amount feeValue, boolean IncFee, String targetAddress) throws Exception { + throw new Exception("Not implemented"); + } + + @Override + public void requestBalanceAndUnspentTransactions(BlockchainRequestsCallbacks blockchainRequestsCallbacks) { + final ServerApiInfura serverApiInfura = new ServerApiInfura(); + // request requestData listener + ServerApiInfura.ResponseListener responseListener = new ServerApiInfura.ResponseListener() { + @Override + public void onSuccess(String method, InfuraResponse infuraResponse) { + try { + if (validateAddress(getContractAddress(ctx.getCard()))) { + String balanceCap = infuraResponse.getResult(); + balanceCap = balanceCap.substring(2); + BigInteger l = new BigInteger(balanceCap, 16); + coinData.setBalanceInInternalUnits(new InternalAmount(l, ctx.getCard().tokenSymbol)); + coinData.setBalanceReceived(true); +// Log.i("$TAG eth_call", balanceCap) + } else { + ctx.setError("Smart contract address not defined"); + } + } catch (Exception e) { + e.printStackTrace(); + } + blockchainRequestsCallbacks.onComplete(!ctx.hasError()); + } + + @Override + public void onFail(String method, String message) { + if (!serverApiInfura.isRequestsSequenceCompleted()) { + ctx.setError(message); + blockchainRequestsCallbacks.onComplete(false); + } + } + }; + serverApiInfura.setResponseListener(responseListener); + + serverApiInfura.requestData(ServerApiInfura.INFURA_ETH_CALL, 67, coinData.getWallet(), getContractAddress(ctx.getCard()), ""); + } + + @Override + public void requestFee(BlockchainRequestsCallbacks blockchainRequestsCallbacks, String targetAddress, Amount amount) { + ServerApiInfura serverApiInfura = new ServerApiInfura(); + // request requestData eth gasPrice listener + ServerApiInfura.ResponseListener responseListener = new ServerApiInfura.ResponseListener() { + @Override + public void onSuccess(String method, InfuraResponse infuraResponse) { + String gasPrice = infuraResponse.getResult(); + gasPrice = gasPrice.substring(2); + + // rounding gas price to integer gwei + BigInteger l = new BigInteger(gasPrice, 16); + + Log.i(TAG, "Infura gas price: " + gasPrice + " (" + l.toString() + ")"); + BigInteger m; + if (!amount.getCurrency().equals(Blockchain.Ethereum.getCurrency())) + m = BigInteger.valueOf(60000); + else m = BigInteger.valueOf(21000); + + Log.i(TAG, "fee multiplier: " + m.toString()); + + InternalAmount weiMinFee = new InternalAmount(l.multiply(m), "wei"); + InternalAmount weiNormalFee = new InternalAmount(l.multiply(BigInteger.valueOf(12)).divide(BigInteger.valueOf(10)).multiply(m), "wei"); + InternalAmount weiMaxFee = new InternalAmount(l.multiply(BigInteger.valueOf(15)).divide(BigInteger.valueOf(10)).multiply(m), "wei"); + Log.i(TAG, "min fee : " + weiMinFee.toValueString() + " wei"); + Log.i(TAG, "normal fee: " + weiNormalFee.toValueString() + " wei"); + Log.i(TAG, "max fee : " + weiMaxFee.toValueString() + " wei"); + + try { + coinData.minFee = convertToAmount(weiMinFee); + coinData.normalFee = convertToAmount(weiNormalFee); + coinData.maxFee = convertToAmount(weiMaxFee); + Log.i(TAG, "min fee : " + coinData.minFee.toString()); + Log.i(TAG, "normal fee: " + coinData.normalFee.toString()); + Log.i(TAG, "max fee : " + coinData.maxFee.toString()); + } catch (Exception e) { + e.printStackTrace(); + } + blockchainRequestsCallbacks.onComplete(true); + } + + @Override + public void onFail(String method, String message) { + ctx.setError(message); + blockchainRequestsCallbacks.onComplete(false); + } + }; + serverApiInfura.setResponseListener(responseListener); + + serverApiInfura.requestData(ServerApiInfura.INFURA_ETH_GAS_PRICE, 67, coinData.getWallet(), "", ""); + } + + @Override + public void requestSendTransaction(BlockchainRequestsCallbacks blockchainRequestsCallbacks, byte[] txForSend) { + } + + public boolean isNftToken() { + return true; + } + + +} diff --git a/app/src/main/java/com/tangem/domain/wallet/rsk/RskTokenEngine.java b/app/src/main/java/com/tangem/domain/wallet/rsk/RskTokenEngine.java index af34eef559..c33b7902ac 100644 --- a/app/src/main/java/com/tangem/domain/wallet/rsk/RskTokenEngine.java +++ b/app/src/main/java/com/tangem/domain/wallet/rsk/RskTokenEngine.java @@ -39,7 +39,7 @@ public class RskTokenEngine extends TokenEngine { @Override public Uri getWalletExplorerUri() { - return Uri.parse("https://explorer.rsk.co/address/" + ctx.getCoinData().getWallet()); + return Uri.parse("https://explorer.rsk.co/address/" + ctx.getCoinData().getWallet() + "?__tab=tokens"); } // Only RSK explorer for now @Override @@ -105,15 +105,11 @@ public class RskTokenEngine extends TokenEngine { // case ServerApiRootstock.ROOTSTOCK_ETH_CALL: { try { - if (validateAddress(getContractAddress(ctx.getCard()))) { - String balanceCap = rootstockResponse.getResult(); - balanceCap = balanceCap.substring(2); - BigInteger l = new BigInteger(balanceCap, 16); - coinData.setBalanceInInternalUnits(new CoinEngine.InternalAmount(l, ctx.getCard().tokenSymbol)); + String balanceCap = rootstockResponse.getResult(); + balanceCap = balanceCap.substring(2); + BigInteger l = new BigInteger(balanceCap, 16); + coinData.setBalanceInInternalUnits(new CoinEngine.InternalAmount(l, ctx.getCard().tokenSymbol)); // Log.i("$TAG eth_call", balanceCap) - } else { - ctx.setError("Smart contract address not defined"); - } if (blockchainRequestsCallbacks.allowAdvance()) { serverApiRootstock.requestData(ServerApiRootstock.ROOTSTOCK_ETH_GET_BALANCE, 67, coinData.getWallet(), "", ""); @@ -147,7 +143,12 @@ public class RskTokenEngine extends TokenEngine { }; serverApiRootstock.setResponseListener(responseListener); - serverApiRootstock.requestData(ServerApiRootstock.ROOTSTOCK_ETH_CALL, 67, coinData.getWallet(), getContractAddress(ctx.getCard()), ""); + if (validateAddress(getContractAddress(ctx.getCard()))) { + serverApiRootstock.requestData(ServerApiRootstock.ROOTSTOCK_ETH_CALL, 67, coinData.getWallet(), getContractAddress(ctx.getCard()), ""); + } else { + ctx.setError("Smart contract address not defined"); + blockchainRequestsCallbacks.onComplete(false); + } } @Override diff --git a/app/src/main/java/com/tangem/domain/wallet/token/TokenEngine.java b/app/src/main/java/com/tangem/domain/wallet/token/TokenEngine.java index cf1fe4ba00..45fd76eba1 100644 --- a/app/src/main/java/com/tangem/domain/wallet/token/TokenEngine.java +++ b/app/src/main/java/com/tangem/domain/wallet/token/TokenEngine.java @@ -679,15 +679,13 @@ public class TokenEngine extends CoinEngine { // case ServerApiInfura.INFURA_ETH_CALL: { try { - if (validateAddress(getContractAddress(ctx.getCard()))) { - String balanceCap = infuraResponse.getResult(); - balanceCap = balanceCap.substring(2); - BigInteger l = new BigInteger(balanceCap, 16); - coinData.setBalanceInInternalUnits(new CoinEngine.InternalAmount(l, ctx.getCard().tokenSymbol)); + + String balanceCap = infuraResponse.getResult(); + balanceCap = balanceCap.substring(2); + BigInteger l = new BigInteger(balanceCap, 16); + coinData.setBalanceInInternalUnits(new CoinEngine.InternalAmount(l, ctx.getCard().tokenSymbol)); // Log.i("$TAG eth_call", balanceCap) - } else { - ctx.setError("Smart contract address not defined"); - } + if (blockchainRequestsCallbacks.allowAdvance()) { serverApiInfura.requestData(ServerApiInfura.INFURA_ETH_GET_BALANCE, 67, coinData.getWallet(), "", ""); @@ -721,7 +719,12 @@ public class TokenEngine extends CoinEngine { }; serverApiInfura.setResponseListener(responseListener); - serverApiInfura.requestData(ServerApiInfura.INFURA_ETH_CALL, 67, coinData.getWallet(), getContractAddress(ctx.getCard()), ""); + if (validateAddress(getContractAddress(ctx.getCard()))) { + serverApiInfura.requestData(ServerApiInfura.INFURA_ETH_CALL, 67, coinData.getWallet(), getContractAddress(ctx.getCard()), ""); + } else { + ctx.setError("Smart contract address not defined"); + blockchainRequestsCallbacks.onComplete(false); + } } @Override diff --git a/app/src/main/java/com/tangem/presentation/fragment/LoadedWallet.kt b/app/src/main/java/com/tangem/presentation/fragment/LoadedWallet.kt index c1d0a3eafc..1ce7f91a5b 100644 --- a/app/src/main/java/com/tangem/presentation/fragment/LoadedWallet.kt +++ b/app/src/main/java/com/tangem/presentation/fragment/LoadedWallet.kt @@ -620,6 +620,13 @@ class LoadedWallet : androidx.fragment.app.Fragment(), NfcAdapter.ReaderCallback btnExtract.backgroundTintList = inactiveColor } + if (engine.isNftToken) { + btnLoad.isEnabled = false + btnLoad.backgroundTintList = inactiveColor + btnExtract.isEnabled = false + btnExtract.backgroundTintList = inactiveColor + } + // //TODO why ??? // ctx.error = null // ctx.message = null @@ -714,6 +721,7 @@ class LoadedWallet : androidx.fragment.app.Fragment(), NfcAdapter.ReaderCallback Blockchain.Ethereum -> "ethereum" Blockchain.EthereumTestNet -> "ethereum" Blockchain.Token -> "ethereum" + Blockchain.NftToken -> "ethereum" Blockchain.BitcoinCash -> "bitcoin-cash" Blockchain.Litecoin -> "litecoin" Blockchain.Rootstock -> "bitcoin" diff --git a/tangemcard-android/src/main/res/drawable/card_default_nft.png b/tangemcard-android/src/main/res/drawable/card_default_nft.png new file mode 100644 index 0000000000..8f7598bc99 Binary files /dev/null and b/tangemcard-android/src/main/res/drawable/card_default_nft.png differ diff --git a/tangemserver-android/src/main/java/com/tangem/tangemserver/android/data/LocalStorage.kt b/tangemserver-android/src/main/java/com/tangem/tangemserver/android/data/LocalStorage.kt index c437f13f27..707e189b87 100644 --- a/tangemserver-android/src/main/java/com/tangem/tangemserver/android/data/LocalStorage.kt +++ b/tangemserver-android/src/main/java/com/tangem/tangemserver/android/data/LocalStorage.kt @@ -50,7 +50,9 @@ class LocalStorage } if (artworks.count() == 0) { +// forceSave=true only on the last one putResourceArtworkToCatalog(R.drawable.card_default, false) + putResourceArtworkToCatalog(R.drawable.card_default_nft, false) putResourceArtworkToCatalog(R.drawable.card_ru006, false) putResourceArtworkToCatalog(R.drawable.card_ru007, false) putResourceArtworkToCatalog(R.drawable.card_ru011, false) @@ -62,12 +64,12 @@ class LocalStorage putResourceArtworkToCatalog(R.drawable.card_ru020, false) putResourceArtworkToCatalog(R.drawable.card_ru021, false) putResourceArtworkToCatalog(R.drawable.card_ru022, false) - putResourceArtworkToCatalog(R.drawable.card_ru023, true) - putResourceArtworkToCatalog(R.drawable.card_ru024, true) - putResourceArtworkToCatalog(R.drawable.card_ru028, true) - putResourceArtworkToCatalog(R.drawable.card_ru029, true) - putResourceArtworkToCatalog(R.drawable.card_ru030, true) - putResourceArtworkToCatalog(R.drawable.card_ru031, true) + putResourceArtworkToCatalog(R.drawable.card_ru023, false) + putResourceArtworkToCatalog(R.drawable.card_ru024, false) + putResourceArtworkToCatalog(R.drawable.card_ru028, false) + putResourceArtworkToCatalog(R.drawable.card_ru029, false) + putResourceArtworkToCatalog(R.drawable.card_ru030, false) + putResourceArtworkToCatalog(R.drawable.card_ru031, false) putResourceArtworkToCatalog(R.drawable.card_ru032, true) } if (batchesFile.exists()) { @@ -175,14 +177,25 @@ class LocalStorage } } - private fun getDefaultArtworkBitmap(): Bitmap { - val defaultArtworkId = context.resources.getResourceEntryName(R.drawable.card_default) + private fun getDefaultArtworkBitmap(card: TangemCard): Bitmap { + val defaultArtworkId: String + if (card.tokenSymbol.startsWith("NFT:")) { + defaultArtworkId = context.resources.getResourceEntryName(R.drawable.card_default_nft) + } else { + defaultArtworkId = context.resources.getResourceEntryName(R.drawable.card_default) + } val info = artworks[defaultArtworkId] var bitmap: Bitmap? = null if (info != null && !info.isResource) { bitmap = loadArtworkBitmapFromFile(defaultArtworkId) } - if (bitmap == null) return BitmapFactory.decodeResource(context.resources, R.drawable.card_default) + if (bitmap == null) { + if (card.tokenSymbol.startsWith("NFT:")) { + return BitmapFactory.decodeResource(context.resources, R.drawable.card_default_nft) + } else { + return BitmapFactory.decodeResource(context.resources, R.drawable.card_default) + } + } return bitmap } @@ -236,11 +249,11 @@ class LocalStorage if (artworkResourceId != null) { val artworkId = context.resources.getResourceEntryName(artworkResourceId) - return getArtworkBitmap(artworkId) ?: return getDefaultArtworkBitmap() + return getArtworkBitmap(artworkId) ?: return getDefaultArtworkBitmap(card) } - val batchInfo = batches[card.batch] ?: return getDefaultArtworkBitmap() - val artworkId = batchInfo.artworkId ?: return getDefaultArtworkBitmap() - return getArtworkBitmap(artworkId) ?: return getDefaultArtworkBitmap() + val batchInfo = batches[card.batch] ?: return getDefaultArtworkBitmap(card) + val artworkId = batchInfo.artworkId ?: return getDefaultArtworkBitmap(card) + return getArtworkBitmap(artworkId) ?: return getDefaultArtworkBitmap(card) } override fun applySubstitution(card: TangemCard) {