Updated on 2026-08-14
This commit is contained in:
commit
82d085161e
27 changed files with 334 additions and 172 deletions
|
|
@ -7,7 +7,6 @@ import android.net.Uri
|
|||
import android.nfc.NfcAdapter
|
||||
import android.nfc.Tag
|
||||
import android.nfc.tech.IsoDep
|
||||
import android.nfc.tech.Ndef
|
||||
import android.nfc.tech.NfcV
|
||||
import android.os.Bundle
|
||||
import android.text.Spannable
|
||||
|
|
@ -55,7 +54,7 @@ import com.tangem.wallet.BuildConfig
|
|||
import com.tangem.wallet.CoinEngineFactory
|
||||
import com.tangem.wallet.R
|
||||
import com.tangem.wallet.TangemContext
|
||||
import com.tangem.wallet.xlmtag.XlmTagEngine
|
||||
import com.tangem.wallet.xlmTag.XlmTagEngine
|
||||
import kotlinx.android.synthetic.main.fragment_main.*
|
||||
import kotlinx.android.synthetic.main.layout_touch_card.*
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ import com.tangem.ui.navigation.NavigationResultListener
|
|||
import com.tangem.util.LOG
|
||||
import com.tangem.util.UtilHelper
|
||||
import com.tangem.wallet.*
|
||||
import com.tangem.wallet.xlmtag.XlmTagEngine
|
||||
import com.tangem.wallet.xlmTag.XlmTagEngine
|
||||
import kotlinx.android.synthetic.main.fr_loaded_wallet.*
|
||||
import kotlinx.android.synthetic.main.layout_btn_details.*
|
||||
import kotlinx.android.synthetic.main.layout_tangem_card.*
|
||||
|
|
@ -67,7 +67,7 @@ class TagFragment : BaseFragment(), NavigationResultListener {
|
|||
btnExtract.backgroundTintList =
|
||||
ContextCompat.getColorStateList(requireContext(), R.color.btn_dark)
|
||||
|
||||
ivTangemCard.setImageResource(R.drawable.card_default_nft)
|
||||
ivTangemCard.setImageResource(R.drawable.card_tgslix)
|
||||
|
||||
tvBalance.setSingleLine(!engine.needMultipleLinesForBalance())
|
||||
tvWallet.text = ctx.coinData.wallet
|
||||
|
|
@ -78,7 +78,6 @@ class TagFragment : BaseFragment(), NavigationResultListener {
|
|||
srl?.setOnRefreshListener { refresh(true) }
|
||||
|
||||
requestBalanceAndUnspentTransactions()
|
||||
// update()
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -129,14 +128,13 @@ class TagFragment : BaseFragment(), NavigationResultListener {
|
|||
else -> tvBalance.text = ""
|
||||
}
|
||||
|
||||
if (ctx.card!!.tokenSymbol.length > 1) {
|
||||
@Suppress("DEPRECATION") val html = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
|
||||
Html.fromHtml(ctx.blockchainName, Html.FROM_HTML_MODE_LEGACY)
|
||||
else
|
||||
Html.fromHtml(ctx.blockchainName)
|
||||
tvBlockchain.text = html
|
||||
} else
|
||||
tvBlockchain.text = ctx.blockchainName
|
||||
|
||||
@Suppress("DEPRECATION") val html = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
|
||||
Html.fromHtml(ctx.blockchainName, Html.FROM_HTML_MODE_LEGACY)
|
||||
else
|
||||
Html.fromHtml(ctx.blockchainName)
|
||||
tvBlockchain.text = html
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ import com.tangem.wallet.token.TokenEngine
|
|||
import com.tangem.wallet.tezos.TezosEngine
|
||||
import com.tangem.wallet.xlm.XlmAssetEngine
|
||||
import com.tangem.wallet.xlm.XlmEngine
|
||||
import com.tangem.wallet.xlmtag.XlmTagEngine
|
||||
import com.tangem.wallet.xlmTag.XlmTagEngine
|
||||
import com.tangem.wallet.xrp.XrpEngine
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -58,14 +58,16 @@ public class TangemContext {
|
|||
public String getBlockchainName() {
|
||||
Blockchain blockchain = getBlockchain();
|
||||
if (blockchain == Blockchain.Token || blockchain == Blockchain.RootstockToken) {
|
||||
String token = card.getTokenSymbol();
|
||||
return token + " <br><small><small> " + getBlockchain().getOfficialName() + " smart contract token</small></small>";
|
||||
return card.getTokenSymbol()+ "<br><small><small> " + getBlockchain().getOfficialName() + " smart contract token</small></small>";
|
||||
}
|
||||
if (blockchain == Blockchain.NftToken) {
|
||||
return card.getTokenSymbol().substring(4) + " <br><small><small> " + getBlockchain().getOfficialName() + " NFT token</small></small>";
|
||||
return card.getTokenSymbol().substring(4) + "<br><small><small> " + getBlockchain().getOfficialName() + " non-fungible token</small></small>";
|
||||
}
|
||||
if (blockchain == Blockchain.StellarAsset) {
|
||||
return card.getTokenSymbol() + " <br><small><small> " + getBlockchain().getOfficialName() + " asset</small></small>";
|
||||
return card.getTokenSymbol() + "<br><small><small> " + getBlockchain().getOfficialName() + " asset</small></small>";
|
||||
}
|
||||
if (blockchain == Blockchain.StellarTag) {
|
||||
return "TANGEM TAG<br><small><small> "+ getBlockchain().getOfficialName() + " non-fungible token </small></small>";
|
||||
}
|
||||
return blockchain.getOfficialName();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -275,11 +275,11 @@ public class BtcCashEngine extends CoinEngine {
|
|||
// return;
|
||||
// }
|
||||
|
||||
if ((ctx.getCard().getOfflineBalance() != null) && !coinData.isBalanceReceived() && (ctx.getCard().getRemainingSignatures() == ctx.getCard().getMaxSignatures()) && coinData.getBalanceInInternalUnits().notZero()) {
|
||||
balanceValidator.setScore(80);
|
||||
balanceValidator.setFirstLine(R.string.balance_validator_first_line_verified_offline);
|
||||
balanceValidator.setSecondLine(R.string.balance_validator_second_line_internet_to_get_balance);
|
||||
}
|
||||
// if ((ctx.getCard().getOfflineBalance() != null) && !coinData.isBalanceReceived() && ctx.getCard().getRemainingSignatures() == ctx.getCard().getMaxSignatures()) {
|
||||
// balanceValidator.setScore(80);
|
||||
// balanceValidator.setFirstLine(R.string.balance_validator_first_line_verified_offline);
|
||||
// balanceValidator.setSecondLine(R.string.balance_validator_second_line_internet_to_get_balance);
|
||||
// }
|
||||
|
||||
// if(card.getFailedBalanceRequestCounter()!=0) {
|
||||
// score -= 5 * card.getFailedBalanceRequestCounter();
|
||||
|
|
|
|||
|
|
@ -247,11 +247,11 @@ public class BinanceEngine extends CoinEngine {
|
|||
}
|
||||
}
|
||||
|
||||
if ((ctx.getCard().getOfflineBalance() != null) && !coinData.isBalanceReceived() && (ctx.getCard().getRemainingSignatures() == ctx.getCard().getMaxSignatures()) && coinData.getBalance().notZero()) {
|
||||
balanceValidator.setScore(80);
|
||||
balanceValidator.setFirstLine(R.string.balance_validator_first_line_verified_offline);
|
||||
balanceValidator.setSecondLine(R.string.balance_validator_second_line_internet_to_get_balance);
|
||||
}
|
||||
// if ((ctx.getCard().getOfflineBalance() != null) && !coinData.isBalanceReceived() && ctx.getCard().getRemainingSignatures() == ctx.getCard().getMaxSignatures()) {
|
||||
// balanceValidator.setScore(80);
|
||||
// balanceValidator.setFirstLine(R.string.balance_validator_first_line_verified_offline);
|
||||
// balanceValidator.setSecondLine(R.string.balance_validator_second_line_internet_to_get_balance);
|
||||
// }
|
||||
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
|
|
|
|||
|
|
@ -310,11 +310,11 @@ public class BtcEngine extends CoinEngine {
|
|||
// return;
|
||||
// }
|
||||
|
||||
if ((ctx.getCard().getOfflineBalance() != null) && !coinData.isBalanceReceived() && (ctx.getCard().getRemainingSignatures() == ctx.getCard().getMaxSignatures()) && coinData.getBalanceInInternalUnits().notZero()) {
|
||||
balanceValidator.setScore(80);
|
||||
balanceValidator.setFirstLine(R.string.balance_validator_first_line_verified_offline);
|
||||
balanceValidator.setSecondLine(R.string.balance_validator_second_line_internet_to_get_balance);
|
||||
}
|
||||
// if ((ctx.getCard().getOfflineBalance() != null) && !coinData.isBalanceReceived() && ctx.getCard().getRemainingSignatures() == ctx.getCard().getMaxSignatures()) {
|
||||
// balanceValidator.setScore(80);
|
||||
// balanceValidator.setFirstLine(R.string.balance_validator_first_line_verified_offline);
|
||||
// balanceValidator.setSecondLine(R.string.balance_validator_second_line_internet_to_get_balance);
|
||||
// }
|
||||
|
||||
// if(card.getFailedBalanceRequestCounter()!=0) {
|
||||
// score -= 5 * card.getFailedBalanceRequestCounter();
|
||||
|
|
|
|||
|
|
@ -251,11 +251,11 @@ public class CardanoEngine extends CoinEngine {
|
|||
}
|
||||
}
|
||||
|
||||
if ((ctx.getCard().getOfflineBalance() != null) && !coinData.isBalanceReceived() && (ctx.getCard().getRemainingSignatures() == ctx.getCard().getMaxSignatures()) && coinData.getBalanceInInternalUnits().notZero()) {
|
||||
balanceValidator.setScore(80);
|
||||
balanceValidator.setFirstLine(R.string.balance_validator_first_line_verified_offline);
|
||||
balanceValidator.setSecondLine(R.string.balance_validator_second_line_internet_to_get_balance);
|
||||
}
|
||||
// if ((ctx.getCard().getOfflineBalance() != null) && !coinData.isBalanceReceived() && ctx.getCard().getRemainingSignatures() == ctx.getCard().getMaxSignatures()) {
|
||||
// balanceValidator.setScore(80);
|
||||
// balanceValidator.setFirstLine(R.string.balance_validator_first_line_verified_offline);
|
||||
// balanceValidator.setSecondLine(R.string.balance_validator_second_line_internet_to_get_balance);
|
||||
// }
|
||||
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
|
|
|
|||
|
|
@ -263,11 +263,11 @@ public class DucatusEngine extends BtcEngine {
|
|||
// return;
|
||||
// }
|
||||
|
||||
if ((ctx.getCard().getOfflineBalance() != null) && !coinData.isBalanceReceived() && (ctx.getCard().getRemainingSignatures() == ctx.getCard().getMaxSignatures()) && coinData.getBalanceInInternalUnits().notZero()) {
|
||||
balanceValidator.setScore(80);
|
||||
balanceValidator.setFirstLine(R.string.balance_validator_first_line_verified_offline);
|
||||
balanceValidator.setSecondLine(R.string.balance_validator_second_line_internet_to_get_balance);
|
||||
}
|
||||
// if ((ctx.getCard().getOfflineBalance() != null) && !coinData.isBalanceReceived() && ctx.getCard().getRemainingSignatures() == ctx.getCard().getMaxSignatures()) {
|
||||
// balanceValidator.setScore(80);
|
||||
// balanceValidator.setFirstLine(R.string.balance_validator_first_line_verified_offline);
|
||||
// balanceValidator.setSecondLine(R.string.balance_validator_second_line_internet_to_get_balance);
|
||||
// }
|
||||
|
||||
// if(card.getFailedBalanceRequestCounter()!=0) {
|
||||
// score -= 5 * card.getFailedBalanceRequestCounter();
|
||||
|
|
|
|||
|
|
@ -293,11 +293,11 @@ public class EosEngine extends CoinEngine {
|
|||
}
|
||||
}
|
||||
|
||||
if ((ctx.getCard().getOfflineBalance() != null) && !coinData.isBalanceReceived() && (ctx.getCard().getRemainingSignatures() == ctx.getCard().getMaxSignatures()) && getBalance().notZero()) {
|
||||
balanceValidator.setScore(80);
|
||||
balanceValidator.setFirstLine(R.string.balance_validator_first_line_verified_offline);
|
||||
balanceValidator.setSecondLine(R.string.balance_validator_second_line_internet_to_verify_online);
|
||||
}
|
||||
// if ((ctx.getCard().getOfflineBalance() != null) && !coinData.isBalanceReceived() && ctx.getCard().getRemainingSignatures() == ctx.getCard().getMaxSignatures()) {
|
||||
// balanceValidator.setScore(80);
|
||||
// balanceValidator.setFirstLine(R.string.balance_validator_first_line_verified_offline);
|
||||
// balanceValidator.setSecondLine(R.string.balance_validator_second_line_internet_to_verify_online);
|
||||
// }
|
||||
|
||||
return true;
|
||||
|
||||
|
|
|
|||
|
|
@ -334,11 +334,11 @@ public class EthEngine extends CoinEngine {
|
|||
}
|
||||
}
|
||||
|
||||
if ((ctx.getCard().getOfflineBalance() != null) && !coinData.isBalanceReceived() && (ctx.getCard().getRemainingSignatures() == ctx.getCard().getMaxSignatures()) && getBalance().notZero()) {
|
||||
balanceValidator.setScore(80);
|
||||
balanceValidator.setFirstLine(R.string.balance_validator_first_line_verified_offline);
|
||||
balanceValidator.setSecondLine(R.string.balance_validator_second_line_internet_to_verify_online);
|
||||
}
|
||||
// if ((ctx.getCard().getOfflineBalance() != null) && !coinData.isBalanceReceived() && ctx.getCard().getRemainingSignatures() == ctx.getCard().getMaxSignatures()) {
|
||||
// balanceValidator.setScore(80);
|
||||
// balanceValidator.setFirstLine(R.string.balance_validator_first_line_verified_offline);
|
||||
// balanceValidator.setSecondLine(R.string.balance_validator_second_line_internet_to_verify_online);
|
||||
// }
|
||||
|
||||
return true;
|
||||
|
||||
|
|
|
|||
|
|
@ -275,11 +275,11 @@ public class LtcEngine extends BtcEngine {
|
|||
// return;
|
||||
// }
|
||||
|
||||
if ((ctx.getCard().getOfflineBalance() != null) && !coinData.isBalanceReceived() && (ctx.getCard().getRemainingSignatures() == ctx.getCard().getMaxSignatures()) && coinData.getBalanceInInternalUnits().notZero()) {
|
||||
balanceValidator.setScore(80);
|
||||
balanceValidator.setFirstLine(R.string.balance_validator_first_line_verified_offline);
|
||||
balanceValidator.setSecondLine(R.string.balance_validator_second_line_internet_to_verify_online);
|
||||
}
|
||||
// if ((ctx.getCard().getOfflineBalance() != null) && !coinData.isBalanceReceived() && ctx.getCard().getRemainingSignatures() == ctx.getCard().getMaxSignatures()) {
|
||||
// balanceValidator.setScore(80);
|
||||
// balanceValidator.setFirstLine(R.string.balance_validator_first_line_verified_offline);
|
||||
// balanceValidator.setSecondLine(R.string.balance_validator_second_line_internet_to_verify_online);
|
||||
// }
|
||||
|
||||
// if(card.getFailedBalanceRequestCounter()!=0) {
|
||||
// score -= 5 * card.getFailedBalanceRequestCounter();
|
||||
|
|
|
|||
|
|
@ -161,18 +161,17 @@ class TezosEngine : CoinEngine {
|
|||
balanceValidator.setSecondLine(R.string.empty_string)
|
||||
}
|
||||
}
|
||||
if (ctx.card.offlineBalance != null &&
|
||||
!coinData!!.isBalanceReceived &&
|
||||
ctx.card.remainingSignatures ==
|
||||
ctx.card.maxSignatures &&
|
||||
balance!!.notZero()
|
||||
) {
|
||||
balanceValidator.setScore(80)
|
||||
balanceValidator.firstLine = R.string.balance_validator_first_line_verified_offline
|
||||
balanceValidator.setSecondLine(
|
||||
R.string.balance_validator_second_line_internet_to_get_balance
|
||||
)
|
||||
}
|
||||
// if (ctx.card.offlineBalance != null &&
|
||||
// !coinData!!.isBalanceReceived &&
|
||||
// ctx.card.remainingSignatures ==
|
||||
// ctx.card.maxSignatures
|
||||
// ) {
|
||||
// balanceValidator.setScore(80)
|
||||
// balanceValidator.firstLine = R.string.balance_validator_first_line_verified_offline
|
||||
// balanceValidator.setSecondLine(
|
||||
// R.string.balance_validator_second_line_internet_to_get_balance
|
||||
// )
|
||||
// }
|
||||
return true
|
||||
} catch (e: java.lang.Exception) {
|
||||
e.printStackTrace()
|
||||
|
|
|
|||
|
|
@ -275,11 +275,11 @@ public class XlmAssetEngine extends CoinEngine {
|
|||
// return;
|
||||
// }
|
||||
|
||||
if ((ctx.getCard().getOfflineBalance() != null) && !coinData.isBalanceReceived() && (ctx.getCard().getRemainingSignatures() == ctx.getCard().getMaxSignatures()) && coinData.getXlmBalance().notZero()) {
|
||||
balanceValidator.setScore(80);
|
||||
balanceValidator.setFirstLine(R.string.balance_validator_first_line_verified_offline);
|
||||
balanceValidator.setSecondLine(R.string.balance_validator_second_line_internet_to_get_balance);
|
||||
}
|
||||
// if ((ctx.getCard().getOfflineBalance() != null) && !coinData.isBalanceReceived() && ctx.getCard().getRemainingSignatures() == ctx.getCard().getMaxSignatures()) {
|
||||
// balanceValidator.setScore(80);
|
||||
// balanceValidator.setFirstLine(R.string.balance_validator_first_line_verified_offline);
|
||||
// balanceValidator.setSecondLine(R.string.balance_validator_second_line_internet_to_get_balance);
|
||||
// }
|
||||
|
||||
// if(card.getFailedBalanceRequestCounter()!=0) {
|
||||
// score -= 5 * card.getFailedBalanceRequestCounter();
|
||||
|
|
|
|||
|
|
@ -243,11 +243,11 @@ public class XlmEngine extends CoinEngine {
|
|||
// return;
|
||||
// }
|
||||
|
||||
if ((ctx.getCard().getOfflineBalance() != null) && !coinData.isBalanceReceived() && (ctx.getCard().getRemainingSignatures() == ctx.getCard().getMaxSignatures()) && coinData.getBalance().notZero()) {
|
||||
balanceValidator.setScore(80);
|
||||
balanceValidator.setFirstLine(R.string.balance_validator_first_line_verified_offline);
|
||||
balanceValidator.setSecondLine(R.string.balance_validator_second_line_internet_to_get_balance);
|
||||
}
|
||||
// if ((ctx.getCard().getOfflineBalance() != null) && !coinData.isBalanceReceived() && ctx.getCard().getRemainingSignatures() == ctx.getCard().getMaxSignatures()) {
|
||||
// balanceValidator.setScore(80);
|
||||
// balanceValidator.setFirstLine(R.string.balance_validator_first_line_verified_offline);
|
||||
// balanceValidator.setSecondLine(R.string.balance_validator_second_line_internet_to_get_balance);
|
||||
// }
|
||||
|
||||
// if(card.getFailedBalanceRequestCounter()!=0) {
|
||||
// score -= 5 * card.getFailedBalanceRequestCounter();
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
package com.tangem.wallet.xlmtag
|
||||
package com.tangem.wallet.xlmTag
|
||||
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package com.tangem.wallet.xlmtag
|
||||
package com.tangem.wallet.xlmTag
|
||||
|
||||
import android.net.Uri
|
||||
import android.os.StrictMode
|
||||
|
|
@ -53,12 +53,18 @@ class XlmTagEngine : CoinEngine {
|
|||
}
|
||||
|
||||
override fun getBalanceHTML(): String {
|
||||
return if (hasBalanceInfo() && isBalanceNotZero && coinData!!.fundsFromTrustedSource) {
|
||||
ctx.getString(R.string.tag_genuine)
|
||||
} else if (coinData!!.fundsSentToTrustedSource) {
|
||||
ctx.getString(R.string.tag_claimed)
|
||||
return if (hasBalanceInfo()) {
|
||||
if (coinData!!.fundsFromTrustedSource) {
|
||||
// if (coinData!!.fundsSentToTrustedSource) { //TODO: shut down for example tags to appear genuine, turn on for production
|
||||
// ctx.getString(R.string.tag_claimed)
|
||||
// } else {
|
||||
ctx.getString(R.string.tag_genuine)
|
||||
// }
|
||||
} else {
|
||||
ctx.getString(R.string.tag_not_genuine)
|
||||
}
|
||||
} else {
|
||||
ctx.getString(R.string.tag_not_genuine)
|
||||
""
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -72,7 +78,7 @@ class XlmTagEngine : CoinEngine {
|
|||
}
|
||||
|
||||
override fun hasBalanceInfo(): Boolean {
|
||||
return if (coinData == null) false else coinData!!.getBalance() != null || coinData!!.isError404
|
||||
return if (coinData == null) false else coinData!!.isBalanceReceived
|
||||
}
|
||||
|
||||
override fun isExtractPossible(): Boolean {
|
||||
|
|
@ -130,40 +136,26 @@ class XlmTagEngine : CoinEngine {
|
|||
}
|
||||
|
||||
override fun validateBalance(balanceValidator: BalanceValidator): Boolean {
|
||||
return try {
|
||||
if (ctx.card.offlineBalance == null && !ctx.coinData.isBalanceReceived || !ctx.coinData.isBalanceReceived && ctx.card.remainingSignatures != ctx.card.maxSignatures) {
|
||||
if (coinData!!.isError404) {
|
||||
balanceValidator.setScore(0)
|
||||
balanceValidator.firstLine = R.string.balance_validator_first_line_no_account
|
||||
balanceValidator.setSecondLine(R.string.balance_validator_second_line_create_account_instruction)
|
||||
} else {
|
||||
balanceValidator.setScore(0)
|
||||
balanceValidator.firstLine = R.string.balance_validator_first_line_unknown_balance
|
||||
balanceValidator.setSecondLine(R.string.balance_validator_second_line_unverified_balance)
|
||||
return false
|
||||
}
|
||||
try {
|
||||
if (!hasBalanceInfo()) {
|
||||
balanceValidator.setScore(0)
|
||||
balanceValidator.firstLine = R.string.balance_validator_first_line_no_connection
|
||||
balanceValidator.setSecondLine(R.string.balance_validator_second_line_authenticity_not_verified)
|
||||
return false
|
||||
}
|
||||
|
||||
if (coinData!!.isBalanceReceived && coinData!!.isBalanceEqual) {
|
||||
if (coinData!!.fundsFromTrustedSource) {
|
||||
balanceValidator.setScore(100)
|
||||
balanceValidator.firstLine = R.string.balance_validator_first_line_verified_balance
|
||||
balanceValidator.setSecondLine(R.string.balance_validator_second_line_confirmed_in_blockchain)
|
||||
if (coinData!!.getBalance()!!.isZero) {
|
||||
balanceValidator.firstLine = R.string.balance_validator_first_line_empty_wallet
|
||||
balanceValidator.setSecondLine(R.string.empty_string)
|
||||
}
|
||||
balanceValidator.firstLine = R.string.balance_validator_first_line_verified_in_blockchain
|
||||
balanceValidator.setSecondLine(R.string.empty_string)
|
||||
} else {
|
||||
balanceValidator.setScore(0)
|
||||
balanceValidator.firstLine = R.string.balance_validator_first_line_authenticity
|
||||
balanceValidator.setSecondLine(R.string.empty_string)
|
||||
}
|
||||
|
||||
if (ctx.card.offlineBalance != null && !coinData!!.isBalanceReceived && ctx.card.remainingSignatures == ctx.card.maxSignatures && coinData!!.getBalance()!!.notZero()) {
|
||||
balanceValidator.setScore(80)
|
||||
balanceValidator.firstLine = R.string.balance_validator_first_line_verified_offline
|
||||
balanceValidator.setSecondLine(R.string.balance_validator_second_line_internet_to_get_balance)
|
||||
}
|
||||
|
||||
true
|
||||
return true
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
false
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -310,7 +302,7 @@ class XlmTagEngine : CoinEngine {
|
|||
val accountKeyPair = KeyPair.fromAccountId(coinData!!.wallet)
|
||||
|
||||
try {
|
||||
var operationsPage = server.payments().forAccount(accountKeyPair).order(RequestBuilder.Order.DESC).execute()
|
||||
var operationsPage = server.payments().forAccount(accountKeyPair).limit(200).order(RequestBuilder.Order.DESC).execute()
|
||||
Log.e("Stellar", operationsPage.records.toString())
|
||||
|
||||
operations.addAll(operationsPage.records)
|
||||
|
|
@ -324,6 +316,8 @@ class XlmTagEngine : CoinEngine {
|
|||
}
|
||||
|
||||
parsePayments(blockchainRequestsCallbacks)
|
||||
|
||||
CoroutineScope(Dispatchers.Main).launch { blockchainRequestsCallbacks.onComplete(!ctx.hasError()) }
|
||||
} catch (e: Exception) {
|
||||
if (e.message != null) {
|
||||
ctx.error = e.message
|
||||
|
|
@ -342,53 +336,11 @@ class XlmTagEngine : CoinEngine {
|
|||
Log.e("Stellar",
|
||||
"fundsFromTrustedSource $coinData!!.fundsFromTrustedSource, " +
|
||||
"fundsSentToTrustedSource $coinData!!.fundsSentToTrustedSource")
|
||||
blockchainRequestsCallbacks.onComplete(!ctx.hasError())
|
||||
coinData!!.isBalanceReceived = true
|
||||
}
|
||||
|
||||
override fun requestBalanceAndUnspentTransactions(blockchainRequestsCallbacks: BlockchainRequestsCallbacks) {
|
||||
CoroutineScope(Dispatchers.IO).launch { requestPayments(blockchainRequestsCallbacks) }
|
||||
val serverApi = ServerApiStellar(Blockchain.Stellar)
|
||||
val listener: ServerApiStellar.Listener = object : ServerApiStellar.Listener {
|
||||
override fun onSuccess(request: StellarRequest.Base) {
|
||||
Log.i(TAG, "onSuccess: " + request.javaClass.simpleName)
|
||||
if (request is StellarRequest.Balance) {
|
||||
coinData!!.accountResponse = request.accountResponse
|
||||
coinData!!.validationNodeDescription = serverApi.currentURL
|
||||
|
||||
blockchainRequestsCallbacks.onProgress()
|
||||
} else if (request is Ledgers) {
|
||||
coinData!!.setLedgerResponse(request.ledgerResponse)
|
||||
if (serverApi.isRequestsSequenceCompleted) {
|
||||
} else {
|
||||
blockchainRequestsCallbacks.onProgress()
|
||||
}
|
||||
} else {
|
||||
ctx.error = "Invalid request logic"
|
||||
blockchainRequestsCallbacks.onComplete(false)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFail(request: StellarRequest.Base) {
|
||||
Log.i(TAG, "onFail: " + request.javaClass.simpleName + " " + request.error)
|
||||
if (request.errorResponse != null && request.errorResponse.code == 404) {
|
||||
coinData!!.isError404 = true
|
||||
} else {
|
||||
ctx.error = request.error
|
||||
}
|
||||
if (serverApi.isRequestsSequenceCompleted) {
|
||||
if (ctx.hasError()) {
|
||||
blockchainRequestsCallbacks.onComplete(false)
|
||||
} else {
|
||||
blockchainRequestsCallbacks.onComplete(true)
|
||||
}
|
||||
} else {
|
||||
blockchainRequestsCallbacks.onProgress()
|
||||
}
|
||||
}
|
||||
}
|
||||
serverApi.setListener(listener)
|
||||
serverApi.requestData(ctx, StellarRequest.Balance(coinData!!.wallet))
|
||||
serverApi.requestData(ctx, Ledgers())
|
||||
}
|
||||
|
||||
@Throws(Exception::class)
|
||||
|
|
@ -463,7 +415,7 @@ class XlmTagEngine : CoinEngine {
|
|||
|
||||
private val multiplier = BigDecimal("10000000")
|
||||
|
||||
const val OPERATIONS_LIMIT = 50
|
||||
const val OPERATIONS_LIMIT = 200
|
||||
private val TRUSTED_DESTINATION = "GAYPZMHFZERB42ONEJ4CY6ADDVTINEXMY6OZ5G6CLR4HHVKOSNJSZGMM"
|
||||
private val TRUSTED_SOURCE = "GAZY7H4BWWEVB6QGB4RV3LW7DH5NO5CD5O6JCEQXA7N2UCGZSAPJFYW2"
|
||||
}
|
||||
|
|
@ -228,11 +228,11 @@ public class XrpEngine extends CoinEngine {
|
|||
}
|
||||
}
|
||||
|
||||
if ((ctx.getCard().getOfflineBalance() != null) && !coinData.isBalanceReceived() && (ctx.getCard().getRemainingSignatures() == ctx.getCard().getMaxSignatures()) && coinData.getBalanceInInternalUnits().notZero()) {
|
||||
balanceValidator.setScore(80);
|
||||
balanceValidator.setFirstLine(R.string.balance_validator_first_line_verified_offline);
|
||||
balanceValidator.setSecondLine(R.string.balance_validator_second_line_internet_to_get_balance);
|
||||
}
|
||||
// if ((ctx.getCard().getOfflineBalance() != null) && !coinData.isBalanceReceived() && ctx.getCard().getRemainingSignatures() == ctx.getCard().getMaxSignatures()) {
|
||||
// balanceValidator.setScore(80);
|
||||
// balanceValidator.setFirstLine(R.string.balance_validator_first_line_verified_offline);
|
||||
// balanceValidator.setSecondLine(R.string.balance_validator_second_line_internet_to_get_balance);
|
||||
// }
|
||||
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ class LocalStorage
|
|||
artworks = HashMap()
|
||||
}
|
||||
|
||||
if (artworks.count() < 31) {
|
||||
if (artworks.count() < 32) {
|
||||
// forceSave=true only on the last one
|
||||
putResourceArtworkToCatalog(R.drawable.card_default, false)
|
||||
putResourceArtworkToCatalog(R.drawable.card_default_nft, false)
|
||||
|
|
@ -78,6 +78,7 @@ class LocalStorage
|
|||
putResourceArtworkToCatalog(R.drawable.card_ru043, false)
|
||||
putResourceArtworkToCatalog(R.drawable.card_tg044, false)
|
||||
putResourceArtworkToCatalog(R.drawable.card_tg046, false)
|
||||
putResourceArtworkToCatalog(R.drawable.card_tgslix, false)
|
||||
putResourceArtworkToCatalog(R.drawable.card_bc00, false)
|
||||
putResourceArtworkToCatalog(R.drawable.card_ff32, true)
|
||||
}
|
||||
|
|
|
|||
BIN
server-android/src/main/res/drawable/card_tgslix.png
Normal file
BIN
server-android/src/main/res/drawable/card_tgslix.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 49 KiB |
|
|
@ -10,7 +10,8 @@ data class CardEnvironment(
|
|||
val pin2: String = DEFAULT_PIN2,
|
||||
val cardId: String? = null,
|
||||
val terminalKeys: KeyPair? = null,
|
||||
val encryptionKey: ByteArray? = null
|
||||
val encryptionKey: ByteArray? = null,
|
||||
val cvc: ByteArray? = null
|
||||
) {
|
||||
|
||||
companion object {
|
||||
|
|
|
|||
|
|
@ -122,6 +122,37 @@ class CardManager(
|
|||
runTask(task, cardId, callback)
|
||||
}
|
||||
|
||||
/**
|
||||
* This command will create a new wallet on the card having ‘Empty’ state.
|
||||
* A key pair WalletPublicKey / WalletPrivateKey is generated and securely stored in the card.
|
||||
* App will need to obtain Wallet_PublicKey from the response of [CreateWalletCommand] or [ReadCommand]
|
||||
* and then transform it into an address of corresponding blockchain wallet
|
||||
* according to a specific blockchain algorithm.
|
||||
* WalletPrivateKey is never revealed by the card and will be used by [SignCommand] and [CheckWalletCommand].
|
||||
* RemainingSignature is set to MaxSignatures.
|
||||
* @param cardId CID, Unique Tangem card ID number.
|
||||
*/
|
||||
fun createWallet(cardId: String,
|
||||
callback: (result: TaskEvent<CreateWalletResponse>) -> Unit) {
|
||||
val createWalletCommand = CreateWalletCommand(cardId)
|
||||
val task = SingleCommandTask(createWalletCommand)
|
||||
runTask(task, cardId, callback)
|
||||
}
|
||||
|
||||
/**
|
||||
* This command deletes all wallet data. If Is_Reusable flag is enabled during personalization,
|
||||
|
||||
* If Is_Reusable flag is disabled, the card switches to ‘Purged’ state.
|
||||
* ‘Purged’ state is final, it makes the card useless.
|
||||
* @param cardId CID, Unique Tangem card ID number.
|
||||
*/
|
||||
fun purgeWallet(cardId: String,
|
||||
callback: (result: TaskEvent<PurgeWalletResponse>) -> Unit) {
|
||||
val purgeWalletCommand = PurgeWalletCommand(cardId)
|
||||
val task = SingleCommandTask(purgeWalletCommand)
|
||||
runTask(task, cardId, callback)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
*/
|
||||
|
|
@ -157,6 +188,6 @@ class CardManager(
|
|||
}
|
||||
|
||||
private fun fetchCardEnvironment(cardId: String?): CardEnvironment {
|
||||
return cardEnvironmentRepository[cardId] ?: CardEnvironment()
|
||||
return cardEnvironmentRepository[cardId] ?: CardEnvironment(cardId = cardId)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
package com.tangem.commands
|
||||
|
||||
import com.tangem.CardEnvironment
|
||||
import com.tangem.common.apdu.CommandApdu
|
||||
import com.tangem.common.apdu.Instruction
|
||||
import com.tangem.common.apdu.ResponseApdu
|
||||
import com.tangem.common.extensions.calculateSha256
|
||||
import com.tangem.common.extensions.hexToBytes
|
||||
import com.tangem.common.tlv.Tlv
|
||||
import com.tangem.common.tlv.TlvMapper
|
||||
import com.tangem.common.tlv.TlvTag
|
||||
import com.tangem.tasks.TaskError
|
||||
|
||||
class CreateWalletResponse(
|
||||
/**
|
||||
* CID, Unique Tangem card ID number.
|
||||
*/
|
||||
val cardId: String,
|
||||
/**
|
||||
* Current status of the card [1 - Empty, 2 - Loaded, 3- Purged]
|
||||
*/
|
||||
val status: CardStatus,
|
||||
/**
|
||||
|
||||
*/
|
||||
val walletPublicKey: ByteArray
|
||||
) : CommandResponse
|
||||
|
||||
/**
|
||||
* This command will create a new wallet on the card having ‘Empty’ state.
|
||||
* A key pair WalletPublicKey / WalletPrivateKey is generated and securely stored in the card.
|
||||
* App will need to obtain Wallet_PublicKey from the response of [CreateWalletCommand] or [ReadCommand]
|
||||
* and then transform it into an address of corresponding blockchain wallet
|
||||
* according to a specific blockchain algorithm.
|
||||
* WalletPrivateKey is never revealed by the card and will be used by [SignCommand] and [CheckWalletCommand].
|
||||
* RemainingSignature is set to MaxSignatures.
|
||||
*
|
||||
* @property cardId CID, Unique Tangem card ID number.
|
||||
*/
|
||||
class CreateWalletCommand(
|
||||
private val cardId: String
|
||||
) : CommandSerializer<CreateWalletResponse>() {
|
||||
|
||||
override fun serialize(cardEnvironment: CardEnvironment): CommandApdu {
|
||||
val tlvData = mutableListOf(
|
||||
Tlv(TlvTag.Pin, cardEnvironment.pin1.calculateSha256()),
|
||||
Tlv(TlvTag.CardId, cardId.hexToBytes()),
|
||||
Tlv(TlvTag.Pin2, cardEnvironment.pin2.calculateSha256())
|
||||
)
|
||||
if (cardEnvironment.cvc != null) {
|
||||
tlvData.add(Tlv(TlvTag.Cvc, cardEnvironment.cvc))
|
||||
}
|
||||
|
||||
return CommandApdu(Instruction.CreateWallet, tlvData)
|
||||
}
|
||||
|
||||
override fun deserialize(cardEnvironment: CardEnvironment, responseApdu: ResponseApdu): CreateWalletResponse? {
|
||||
val tlvData = responseApdu.getTlvData() ?: return null
|
||||
|
||||
return try {
|
||||
val mapper = TlvMapper(tlvData)
|
||||
CreateWalletResponse(
|
||||
cardId = mapper.map(TlvTag.CardId),
|
||||
status = mapper.map(TlvTag.Status),
|
||||
walletPublicKey = mapper.map(TlvTag.WalletPublicKey)
|
||||
)
|
||||
} catch (exception: Exception) {
|
||||
throw TaskError.SerializeCommandError()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
package com.tangem.commands
|
||||
|
||||
import com.tangem.CardEnvironment
|
||||
import com.tangem.common.apdu.CommandApdu
|
||||
import com.tangem.common.apdu.Instruction
|
||||
import com.tangem.common.apdu.ResponseApdu
|
||||
import com.tangem.common.extensions.calculateSha256
|
||||
import com.tangem.common.extensions.hexToBytes
|
||||
import com.tangem.common.tlv.Tlv
|
||||
import com.tangem.common.tlv.TlvMapper
|
||||
import com.tangem.common.tlv.TlvTag
|
||||
import com.tangem.tasks.TaskError
|
||||
|
||||
class PurgeWalletResponse(
|
||||
/**
|
||||
* CID, Unique Tangem card ID number.
|
||||
*/
|
||||
val cardId: String,
|
||||
/**
|
||||
* Current status of the card [1 - Empty, 2 - Loaded, 3- Purged]
|
||||
*/
|
||||
val status: CardStatus
|
||||
) : CommandResponse
|
||||
|
||||
/**
|
||||
* This command deletes all wallet data. If Is_Reusable flag is enabled during personalization,
|
||||
|
||||
* If Is_Reusable flag is disabled, the card switches to ‘Purged’ state.
|
||||
* ‘Purged’ state is final, it makes the card useless.
|
||||
* @property cardId CID, Unique Tangem card ID number.
|
||||
*/
|
||||
class PurgeWalletCommand(
|
||||
private val cardId: String
|
||||
) : CommandSerializer<PurgeWalletResponse>() {
|
||||
|
||||
override fun serialize(cardEnvironment: CardEnvironment): CommandApdu {
|
||||
val tlvData = mutableListOf(
|
||||
Tlv(TlvTag.Pin, cardEnvironment.pin1.calculateSha256()),
|
||||
Tlv(TlvTag.CardId, cardId.hexToBytes()),
|
||||
Tlv(TlvTag.Pin2, cardEnvironment.pin2.calculateSha256())
|
||||
)
|
||||
return CommandApdu(Instruction.PurgeWallet, tlvData)
|
||||
}
|
||||
|
||||
override fun deserialize(cardEnvironment: CardEnvironment, responseApdu: ResponseApdu): PurgeWalletResponse? {
|
||||
val tlvData = responseApdu.getTlvData() ?: return null
|
||||
|
||||
return try {
|
||||
val mapper = TlvMapper(tlvData)
|
||||
PurgeWalletResponse(
|
||||
cardId = mapper.map(TlvTag.CardId),
|
||||
status = mapper.map(TlvTag.Status))
|
||||
} catch (exception: Exception) {
|
||||
throw TaskError.SerializeCommandError()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -52,7 +52,7 @@ enum class TlvTag(val code: Int) {
|
|||
Pause(0x1C),
|
||||
|
||||
ManufactureId(0x20),
|
||||
ManufacturerSignature(0x21),
|
||||
ManufacturerSignature(0x86),
|
||||
|
||||
IssuerDataPublicKey(0x30),
|
||||
IssuerTransactionPublicKey(0x31),
|
||||
|
|
|
|||
|
|
@ -45,6 +45,8 @@ class MainActivity : AppCompatActivity() {
|
|||
tv_card_cid?.text = cardId
|
||||
btn_sign.isEnabled = true
|
||||
btn_read_issuer_data.isEnabled = true
|
||||
btn_purge_wallet.isEnabled = true
|
||||
btn_create_wallet.isEnabled = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -103,6 +105,32 @@ class MainActivity : AppCompatActivity() {
|
|||
}
|
||||
}
|
||||
}
|
||||
btn_purge_wallet?.setOnClickListener { _ ->
|
||||
cardManager.purgeWallet(
|
||||
cardId) {
|
||||
when (it) {
|
||||
is TaskEvent.Completion -> {
|
||||
if (it.error != null) runOnUiThread { tv_card_cid?.text = it.error!!::class.simpleName }
|
||||
}
|
||||
is TaskEvent.Event -> runOnUiThread {
|
||||
tv_card_cid?.text = it.data.status.name
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
btn_create_wallet?.setOnClickListener { _ ->
|
||||
cardManager.createWallet(
|
||||
cardId) {
|
||||
when (it) {
|
||||
is TaskEvent.Completion -> {
|
||||
if (it.error != null) runOnUiThread { tv_card_cid?.text = it.error!!::class.simpleName }
|
||||
}
|
||||
is TaskEvent.Event -> runOnUiThread {
|
||||
tv_card_cid?.text = it.data.status.name
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun createSampleHashes(): Array<ByteArray> {
|
||||
|
|
|
|||
|
|
@ -9,12 +9,12 @@
|
|||
|
||||
<TextView
|
||||
android:paddingBottom="48dp"
|
||||
android:layout_marginTop="48dp"
|
||||
android:id="@+id/tv_card_cid"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="16dp"
|
||||
android:layout_marginBottom="80dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
|
|
@ -25,6 +25,7 @@
|
|||
android:layout_width="200dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Read and verify card"
|
||||
app:layout_constraintVertical_bias="0.3"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
|
|
@ -63,5 +64,27 @@
|
|||
app:layout_constraintTop_toBottomOf="@id/btn_read_issuer_data"
|
||||
android:enabled="false"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/btn_purge_wallet"
|
||||
android:layout_marginTop="24dp"
|
||||
android:layout_width="200dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Purge wallet"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/btn_write_issuer_data"
|
||||
android:enabled="false"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/btn_create_wallet"
|
||||
android:layout_marginTop="24dp"
|
||||
android:layout_width="200dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Create wallet"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/btn_purge_wallet"
|
||||
android:enabled="false"/>
|
||||
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
Loading…
Add table
Add a link
Reference in a new issue