Updated on 2026-08-14
This commit is contained in:
commit
074eb9d673
35 changed files with 691 additions and 480 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -16,7 +16,7 @@ local.properties
|
|||
.idea/codeStyles
|
||||
|
||||
# LayoutInspector files
|
||||
.captures
|
||||
/captures
|
||||
|
||||
# OS-specific files
|
||||
.DS_Store
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ import com.tangem.blockchain.common.AmountType
|
|||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchain.common.TransactionData
|
||||
import com.tangem.common.extensions.hexToBytes
|
||||
import org.kethereum.DEFAULT_GAS_LIMIT
|
||||
import org.kethereum.crypto.api.ec.ECDSASignature
|
||||
import org.kethereum.crypto.determineRecId
|
||||
import org.kethereum.crypto.impl.ec.canonicalise
|
||||
|
|
@ -22,6 +21,7 @@ class EthereumTransactionBuilder(private val walletPublicKey: ByteArray, blockch
|
|||
|
||||
private val chainId = when (blockchain) {
|
||||
Blockchain.Ethereum -> Chain.Mainnet.id
|
||||
Blockchain.RSK -> Chain.RskMainnet.id
|
||||
else -> throw Exception("${blockchain.fullName} blockchain is not supported by EthereumTransactionBuilder")
|
||||
}
|
||||
|
||||
|
|
@ -36,14 +36,15 @@ class EthereumTransactionBuilder(private val walletPublicKey: ByteArray, blockch
|
|||
|
||||
val to: Address
|
||||
val value: BigInteger
|
||||
val input: ByteArray//data for smart contract
|
||||
val input: ByteArray //data for smart contract
|
||||
|
||||
if (transactionData.amount.type == AmountType.Coin) { //ETH transfer
|
||||
if (transactionData.amount.type == AmountType.Coin) { //coin transfer
|
||||
to = Address(transactionData.destinationAddress)
|
||||
value = bigIntegerAmount
|
||||
input = ByteArray(0)
|
||||
} else { //Token transfer
|
||||
to = Address(transactionData.contractAddress ?: throw Exception("Contract address is not specified!"))
|
||||
} else { //token transfer
|
||||
to = Address(transactionData.contractAddress
|
||||
?: throw Exception("Contract address is not specified!"))
|
||||
value = BigInteger.ZERO
|
||||
input = createErc20TransferData(transactionData.destinationAddress, bigIntegerAmount)
|
||||
}
|
||||
|
|
@ -104,8 +105,8 @@ enum class Chain(val id: Int) {
|
|||
Morden(2),
|
||||
Ropsten(3),
|
||||
Rinkeby(4),
|
||||
RootstockMainnet(30),
|
||||
RootstockTestnet(31),
|
||||
RskMainnet(30),
|
||||
RskTestnet(31),
|
||||
Kovan(42),
|
||||
EthereumClassicMainnet(61),
|
||||
EthereumClassicTestnet(62),
|
||||
|
|
|
|||
|
|
@ -2,21 +2,13 @@ package com.tangem.blockchain.blockchains.ethereum
|
|||
|
||||
import android.util.Log
|
||||
import com.tangem.blockchain.blockchains.ethereum.network.EthereumNetworkManager
|
||||
import com.tangem.blockchain.blockchains.ethereum.network.EthereumResponse
|
||||
import com.tangem.blockchain.blockchains.ethereum.network.EthereumInfoResponse
|
||||
import com.tangem.blockchain.common.*
|
||||
import com.tangem.blockchain.extensions.Result
|
||||
import com.tangem.blockchain.extensions.SimpleResult
|
||||
import com.tangem.common.CompletionResult
|
||||
import com.tangem.common.extensions.toHexString
|
||||
import org.kethereum.DEFAULT_GAS_LIMIT
|
||||
import org.kethereum.crypto.api.ec.ECDSASignature
|
||||
import org.kethereum.crypto.determineRecId
|
||||
import org.kethereum.crypto.impl.ec.canonicalise
|
||||
import org.kethereum.extensions.transactions.encodeRLP
|
||||
import org.kethereum.keccakshortcut.keccak
|
||||
import org.kethereum.model.*
|
||||
import java.math.BigDecimal
|
||||
import java.math.BigInteger
|
||||
|
||||
class EthereumWalletManager(
|
||||
cardId: String,
|
||||
|
|
@ -39,7 +31,7 @@ class EthereumWalletManager(
|
|||
}
|
||||
}
|
||||
|
||||
private fun updateWallet(data: EthereumResponse) {
|
||||
private fun updateWallet(data: EthereumInfoResponse) {
|
||||
wallet.amounts[AmountType.Coin]?.value = data.balance
|
||||
wallet.amounts[AmountType.Token]?.value = data.tokenBalance
|
||||
txCount = data.txCount
|
||||
|
|
|
|||
|
|
@ -4,15 +4,16 @@ import com.squareup.moshi.JsonClass
|
|||
import retrofit2.http.Body
|
||||
import retrofit2.http.Headers
|
||||
import retrofit2.http.POST
|
||||
import retrofit2.http.Path
|
||||
|
||||
interface InfuraApi {
|
||||
interface EthereumApi {
|
||||
@Headers("Content-Type: application/json")
|
||||
@POST("v3/613a0b14833145968b1f656240c7d245")
|
||||
suspend fun postToInfura(@Body body: InfuraBody?): InfuraResponse
|
||||
@POST("{apiKey}")
|
||||
suspend fun post(@Body body: EthereumBody?, @Path("apiKey") apiKey: String): EthereumResponse
|
||||
}
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class InfuraBody(
|
||||
data class EthereumBody(
|
||||
val jsonrpc: String = "2.0",
|
||||
val id: Int = 67,
|
||||
val method: String? = null,
|
||||
|
|
@ -21,7 +22,7 @@ data class InfuraBody(
|
|||
|
||||
data class EthCallParams(private val data: String, private val to: String)
|
||||
|
||||
enum class InfuraMethod(val value: String) {
|
||||
enum class EthereumMethod(val value: String) {
|
||||
GET_BALANCE("eth_getBalance"),
|
||||
GET_TRANSACTION_COUNT("eth_getTransactionCount"),
|
||||
GET_PENDING_COUNT("eth_getPendingCount"),
|
||||
|
|
@ -5,6 +5,7 @@ import com.tangem.blockchain.extensions.Result
|
|||
import com.tangem.blockchain.extensions.SimpleResult
|
||||
import com.tangem.blockchain.extensions.retryIO
|
||||
import com.tangem.blockchain.network.API_INFURA
|
||||
import com.tangem.blockchain.network.API_RSK
|
||||
import com.tangem.blockchain.network.createRetrofitInstance
|
||||
import kotlinx.coroutines.Deferred
|
||||
import kotlinx.coroutines.async
|
||||
|
|
@ -16,16 +17,24 @@ import java.math.RoundingMode
|
|||
|
||||
|
||||
class EthereumNetworkManager(blockchain: Blockchain) {
|
||||
private val infuraPath = "v3/"
|
||||
|
||||
private val api: InfuraApi by lazy {
|
||||
private val api: EthereumApi by lazy {
|
||||
val baseUrl = when (blockchain) {
|
||||
Blockchain.Ethereum -> API_INFURA
|
||||
Blockchain.Ethereum -> API_INFURA + infuraPath
|
||||
Blockchain.RSK -> API_RSK
|
||||
else -> throw Exception("${blockchain.fullName} blockchain is not supported by EthereumNetworkManager")
|
||||
}
|
||||
createRetrofitInstance(baseUrl).create(InfuraApi::class.java)
|
||||
createRetrofitInstance(baseUrl).create(EthereumApi::class.java)
|
||||
}
|
||||
|
||||
private val provider: InfuraProvider by lazy { InfuraProvider(api) }
|
||||
private val apiKey = when (blockchain) {
|
||||
Blockchain.Ethereum -> INFURA_API_KEY
|
||||
Blockchain.RSK -> ""
|
||||
else -> throw Exception("${blockchain.fullName} blockchain is not supported by EthereumNetworkManager")
|
||||
}
|
||||
|
||||
private val provider: EthereumProvider by lazy { EthereumProvider(api, apiKey) }
|
||||
|
||||
suspend fun sendTransaction(transaction: String): SimpleResult {
|
||||
return try {
|
||||
|
|
@ -50,17 +59,17 @@ class EthereumNetworkManager(blockchain: Blockchain) {
|
|||
}
|
||||
}
|
||||
|
||||
suspend fun getInfo(address: String, contractAddress: String? = null): Result<EthereumResponse> {
|
||||
suspend fun getInfo(address: String, contractAddress: String? = null): Result<EthereumInfoResponse> {
|
||||
return try {
|
||||
coroutineScope {
|
||||
val balanceResponse = retryIO { async { provider.getBalance(address) } }
|
||||
val txCountResponse = retryIO { async { provider.getTxCount(address) } }
|
||||
val pendingTxCountResponse = retryIO { async { provider.getPendingTxCount(address) } }
|
||||
var tokenBalanceResponse: Deferred<InfuraResponse>? = null
|
||||
var tokenBalanceResponse: Deferred<EthereumResponse>? = null
|
||||
if (contractAddress != null) {
|
||||
tokenBalanceResponse = retryIO { async { provider.getTokenBalance(address, contractAddress) } }
|
||||
}
|
||||
Result.Success(EthereumResponse(
|
||||
Result.Success(EthereumInfoResponse(
|
||||
balanceResponse.await().result!!.parseAmount(),
|
||||
tokenBalanceResponse?.await()?.result?.parseAmount(),
|
||||
txCountResponse.await().result?.responseToNumber()?.toLong() ?: 0,
|
||||
|
|
@ -96,9 +105,11 @@ class EthereumNetworkManager(blockchain: Blockchain) {
|
|||
|
||||
}
|
||||
|
||||
data class EthereumResponse(
|
||||
data class EthereumInfoResponse(
|
||||
val balance: BigDecimal,
|
||||
val tokenBalance: BigDecimal?,
|
||||
val txCount: Long,
|
||||
val pendingTxCount: Long
|
||||
)
|
||||
)
|
||||
|
||||
private const val INFURA_API_KEY = "613a0b14833145968b1f656240c7d245"
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
package com.tangem.blockchain.blockchains.ethereum.network
|
||||
|
||||
class EthereumProvider(private val api: EthereumApi, private val apiKey: String) {
|
||||
suspend fun getBalance(address: String) = api.post(createEthereumBody(EthereumMethod.GET_BALANCE, address), apiKey)
|
||||
suspend fun getTokenBalance(address: String, contractAddress: String) = api.post(createEthereumBody(EthereumMethod.CALL, address, contractAddress), apiKey)
|
||||
suspend fun getTxCount(address: String) = api.post(createEthereumBody(EthereumMethod.GET_TRANSACTION_COUNT, address), apiKey)
|
||||
suspend fun getPendingTxCount(address: String) = api.post(createEthereumBody(EthereumMethod.GET_PENDING_COUNT, address), apiKey)
|
||||
suspend fun getGasPrice() = api.post(createEthereumBody(EthereumMethod.GAS_PRICE), apiKey)
|
||||
suspend fun sendTransaction(transaction: String) = api.post(createEthereumBody(EthereumMethod.SEND_RAW_TRANSACTION, transaction = transaction), apiKey)
|
||||
}
|
||||
|
||||
private fun createEthereumBody(
|
||||
method: EthereumMethod,
|
||||
address: String? = null,
|
||||
contractAddress: String? = null,
|
||||
transaction: String? = null): EthereumBody {
|
||||
|
||||
return when (method) {
|
||||
EthereumMethod.GET_BALANCE ->
|
||||
EthereumBody(method = EthereumMethod.GET_BALANCE.value, params = listOf(address ?: "", "latest"))
|
||||
EthereumMethod.GET_TRANSACTION_COUNT ->
|
||||
EthereumBody(method = EthereumMethod.GET_TRANSACTION_COUNT.value, params = listOf(address ?: "", "latest"))
|
||||
EthereumMethod.GET_PENDING_COUNT ->
|
||||
EthereumBody(method = EthereumMethod.GET_TRANSACTION_COUNT.value, params = listOf(address ?: "", "pending"))
|
||||
EthereumMethod.GAS_PRICE ->
|
||||
EthereumBody(method = EthereumMethod.GAS_PRICE.value)
|
||||
EthereumMethod.SEND_RAW_TRANSACTION ->
|
||||
EthereumBody(method = EthereumMethod.SEND_RAW_TRANSACTION.value, params = listOf(transaction ?: ""))
|
||||
EthereumMethod.CALL -> {
|
||||
EthereumBody(
|
||||
method = EthereumMethod.CALL.value,
|
||||
params = listOf(EthCallParams(
|
||||
"0x70a08231000000000000000000000000" + address?.substring(2), contractAddress
|
||||
?: ""),
|
||||
"latest"
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4,7 +4,7 @@ import com.squareup.moshi.Json
|
|||
import com.squareup.moshi.JsonClass
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class InfuraResponse(
|
||||
data class EthereumResponse(
|
||||
@Json(name = "jsonrpc")
|
||||
val jsonrpc: String = "",
|
||||
|
||||
|
|
@ -15,11 +15,11 @@ data class InfuraResponse(
|
|||
val result: String? = null,
|
||||
|
||||
@Json(name = "error")
|
||||
val error: InfuraError? = null
|
||||
val error: EthereumError? = null
|
||||
)
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class InfuraError(
|
||||
data class EthereumError(
|
||||
@Json(name = "code")
|
||||
val code: Int? = null,
|
||||
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
package com.tangem.blockchain.blockchains.ethereum.network
|
||||
|
||||
class InfuraProvider(private val api: InfuraApi) {
|
||||
suspend fun getBalance(address: String) = api.postToInfura(createInfuraBody(InfuraMethod.GET_BALANCE, address))
|
||||
suspend fun getTokenBalance(address: String, contractAddress: String) = api.postToInfura(createInfuraBody(InfuraMethod.CALL, address, contractAddress))
|
||||
suspend fun getTxCount(address: String) = api.postToInfura(createInfuraBody(InfuraMethod.GET_TRANSACTION_COUNT, address))
|
||||
suspend fun getPendingTxCount(address: String) = api.postToInfura(createInfuraBody(InfuraMethod.GET_PENDING_COUNT, address))
|
||||
suspend fun getGasPrice() = api.postToInfura(createInfuraBody(InfuraMethod.GAS_PRICE))
|
||||
suspend fun sendTransaction(transaction: String) = api.postToInfura(createInfuraBody(InfuraMethod.SEND_RAW_TRANSACTION, transaction = transaction))
|
||||
}
|
||||
|
||||
private fun createInfuraBody(
|
||||
method: InfuraMethod,
|
||||
address: String? = null,
|
||||
contractAddress: String? = null,
|
||||
transaction: String? = null): InfuraBody {
|
||||
|
||||
return when (method) {
|
||||
InfuraMethod.GET_BALANCE ->
|
||||
InfuraBody(method = InfuraMethod.GET_BALANCE.value, params = listOf(address ?: "", "latest"))
|
||||
InfuraMethod.GET_TRANSACTION_COUNT ->
|
||||
InfuraBody(method = InfuraMethod.GET_TRANSACTION_COUNT.value, params = listOf(address ?: "", "latest"))
|
||||
InfuraMethod.GET_PENDING_COUNT ->
|
||||
InfuraBody(method = InfuraMethod.GET_TRANSACTION_COUNT.value, params = listOf(address ?: "", "pending"))
|
||||
InfuraMethod.GAS_PRICE ->
|
||||
InfuraBody(method = InfuraMethod.GAS_PRICE.value)
|
||||
InfuraMethod.SEND_RAW_TRANSACTION ->
|
||||
InfuraBody(method = InfuraMethod.SEND_RAW_TRANSACTION.value, params = listOf(transaction ?: ""))
|
||||
InfuraMethod.CALL -> {
|
||||
InfuraBody(
|
||||
method = InfuraMethod.CALL.value,
|
||||
params = listOf(EthCallParams(
|
||||
"0x70a08231000000000000000000000000" + address?.substring(2), contractAddress
|
||||
?: ""),
|
||||
"latest"
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -8,8 +8,6 @@ import com.tangem.blockchain.blockchains.ethereum.EthereumAddressService
|
|||
import com.tangem.blockchain.blockchains.stellar.StellarAddressService
|
||||
import com.tangem.blockchain.blockchains.xrp.XrpAddressService
|
||||
|
||||
import java.math.BigDecimal
|
||||
|
||||
enum class Blockchain(
|
||||
val id: String,
|
||||
val currency: String,
|
||||
|
|
@ -20,7 +18,7 @@ enum class Blockchain(
|
|||
BitcoinTestnet("BTC/test", "BTCt", "Bitcoin Testnet"),
|
||||
BitcoinCash("BCH", "BCH", "Bitcoin Cash"),
|
||||
Ethereum("ETH", "ETH", "Ethereum"),
|
||||
Rootstock("", "", ""),
|
||||
RSK("RSK", "RBTC", "RSK"),
|
||||
Cardano("CARDANO", "ADA", "Cardano"),
|
||||
XRP("XRP", "XRP", "XRP Ledger"),
|
||||
Binance("BINANCE", "BNB", "Binance"),
|
||||
|
|
@ -30,7 +28,7 @@ enum class Blockchain(
|
|||
fun decimals(): Int = when (this) {
|
||||
Bitcoin, BitcoinTestnet, BitcoinCash, Binance, BinanceTestnet -> 8
|
||||
Cardano, XRP -> 6
|
||||
Ethereum, Rootstock -> 18
|
||||
Ethereum, RSK -> 18
|
||||
Stellar -> 7
|
||||
Unknown -> 0
|
||||
}
|
||||
|
|
@ -48,8 +46,7 @@ enum class Blockchain(
|
|||
Bitcoin -> BitcoinAddressService()
|
||||
BitcoinTestnet -> BitcoinAddressService(true)
|
||||
BitcoinCash -> BitcoinCashAddressService()
|
||||
Ethereum -> EthereumAddressService()
|
||||
Rootstock -> throw Exception("unsupported blockchain")
|
||||
Ethereum, RSK -> EthereumAddressService()
|
||||
Cardano -> CardanoAddressService()
|
||||
XRP -> XrpAddressService()
|
||||
Binance -> BinanceAddressService()
|
||||
|
|
@ -75,7 +72,7 @@ enum class Blockchain(
|
|||
} else {
|
||||
"https://etherscan.io/token/${token.contractAddress}?a=$address"
|
||||
}
|
||||
Rootstock -> {
|
||||
RSK -> {
|
||||
var url = "https://explorer.rsk.co/address/$address"
|
||||
if (token != null) {
|
||||
url += "?__tab=tokens"
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@ import com.tangem.blockchain.blockchains.bitcoincash.BitcoinCashWalletManager
|
|||
import com.tangem.blockchain.blockchains.cardano.CardanoTransactionBuilder
|
||||
import com.tangem.blockchain.blockchains.cardano.CardanoWalletManager
|
||||
import com.tangem.blockchain.blockchains.cardano.network.CardanoNetworkManager
|
||||
import com.tangem.blockchain.blockchains.ethereum.Chain
|
||||
import com.tangem.blockchain.blockchains.ethereum.EthereumTransactionBuilder
|
||||
import com.tangem.blockchain.blockchains.ethereum.EthereumWalletManager
|
||||
import com.tangem.blockchain.blockchains.ethereum.network.EthereumNetworkManager
|
||||
|
|
@ -61,7 +60,7 @@ object WalletManagerFactory {
|
|||
BitcoinCashNetworkManager()
|
||||
)
|
||||
}
|
||||
Blockchain.Ethereum -> {
|
||||
Blockchain.Ethereum, Blockchain.RSK -> {
|
||||
return EthereumWalletManager(
|
||||
cardId, wallet,
|
||||
EthereumTransactionBuilder(walletPublicKey, blockchain),
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ const val API_INFURA = "https://mainnet.infura.io/"
|
|||
const val API_SOCHAIN_V2 = "https://chain.so/"
|
||||
const val API_ESTIMATEFEE = "https://estimatefee.com/"
|
||||
const val API_UPDATE_VERSION = "https://raw.githubusercontent.com/"
|
||||
const val API_ROOTSTOCK = "https://public-node.rsk.co/"
|
||||
const val API_RSK = "https://public-node.rsk.co/"
|
||||
const val API_BLOCKCYPHER = "https://api.blockcypher.com/"
|
||||
const val API_BINANCE = "https://dex.binance.org/"
|
||||
const val API_BINANCE_TESTNET = "https://testnet-dex.binance.org/"
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ data class NdefRecord(
|
|||
URI, AAR, TEXT
|
||||
}
|
||||
|
||||
@delegate:Transient
|
||||
val valueInBytes: ByteArray by lazy { value.toByteArray() }
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,11 @@ interface DefaultConverter<A, B> {
|
|||
fun convert(from: A, default: B): B
|
||||
}
|
||||
|
||||
interface TwoWayConverter<A, B> {
|
||||
fun aToB(from: A): B
|
||||
fun bToA(from: B): A
|
||||
}
|
||||
|
||||
interface ItemsToModel<M> : DefaultConverter<List<Item>, M>
|
||||
interface ModelToItems<M> : Converter<M, List<Item>>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
package com.tangem.devkit.extensions
|
||||
|
||||
import android.content.ClipData
|
||||
import android.content.ClipboardManager
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.fragment.app.Fragment
|
||||
|
|
@ -7,6 +10,13 @@ import androidx.fragment.app.Fragment
|
|||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
fun Context.copyToClipboard(value: Any, label: String = "") {
|
||||
val clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as? ClipboardManager ?: return
|
||||
|
||||
val clip: ClipData = ClipData.newPlainText(label, value.toString())
|
||||
clipboard.setPrimaryClip(clip)
|
||||
}
|
||||
|
||||
fun Fragment.shareText(text: String) {
|
||||
requireActivity().shareText(text)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,139 +0,0 @@
|
|||
package com.tangem.devkit.extensions
|
||||
|
||||
import android.app.Application
|
||||
import android.content.Context
|
||||
import com.tangem.commands.*
|
||||
import com.tangem.commands.personalization.entities.CardConfig
|
||||
import com.tangem.commands.personalization.entities.NdefRecord
|
||||
import java.util.*
|
||||
|
||||
fun CardConfig.Companion.create(application: Application): CardConfig {
|
||||
|
||||
val preferences = application.getSharedPreferences("prefs", Context.MODE_PRIVATE)
|
||||
|
||||
val signingMethodMaskBuilder = SigningMethodMaskBuilder()
|
||||
if (preferences.getBoolean("personalization_SigningMethod_0", false)) {
|
||||
signingMethodMaskBuilder.add(SigningMethod.SignHash)
|
||||
}
|
||||
if (preferences.getBoolean("personalization_SigningMethod_1", false)) {
|
||||
signingMethodMaskBuilder.add(SigningMethod.SignRaw)
|
||||
}
|
||||
if (preferences.getBoolean("personalization_SigningMethod_2", false)) {
|
||||
signingMethodMaskBuilder.add(SigningMethod.SignHashValidateByIssuer)
|
||||
}
|
||||
if (preferences.getBoolean("personalization_SigningMethod_3", false)) {
|
||||
signingMethodMaskBuilder.add(SigningMethod.SignRawValidateByIssuer)
|
||||
}
|
||||
if (preferences.getBoolean("personalization_SigningMethod_4", false)) {
|
||||
signingMethodMaskBuilder.add(SigningMethod.SignHashValidateByIssuerWriteIssuerData)
|
||||
}
|
||||
if (preferences.getBoolean("personalization_SigningMethod_5", false)) {
|
||||
signingMethodMaskBuilder.add(SigningMethod.SignRawValidateByIssuerWriteIssuerData)
|
||||
}
|
||||
if (preferences.getBoolean("personalization_SigningMethod_6", false)) {
|
||||
signingMethodMaskBuilder.add(SigningMethod.SignHash)
|
||||
}
|
||||
val signingMethod = signingMethodMaskBuilder.build()
|
||||
|
||||
val isNote = preferences.getBoolean("personalization_ProductMask_IsNote", true)
|
||||
val isTag = preferences.getBoolean("personalization_ProductMask_IsTag", false)
|
||||
val isIdCard = preferences.getBoolean("personalization_ProductMask_IsIDCard", false)
|
||||
|
||||
val productMaskBuilder = ProductMaskBuilder()
|
||||
if (isNote) productMaskBuilder.add(Product.Note)
|
||||
if (isTag) productMaskBuilder.add(Product.Tag)
|
||||
if (isIdCard) productMaskBuilder.add(Product.IdCard)
|
||||
val productMask = productMaskBuilder.build()
|
||||
|
||||
var tokenSymbol: String? = null
|
||||
var tokenContractAddress: String? = null
|
||||
var tokenDecimal: Int? = null
|
||||
if (preferences.getBoolean("personalization_isToken", false)) {
|
||||
tokenSymbol = preferences.getString("personalization_token_symbol", "")
|
||||
tokenContractAddress = preferences.getString("personalization_token_contract_address", "")
|
||||
tokenDecimal = preferences.getString("personalization_token_decimal", "")!!.toInt()
|
||||
}
|
||||
|
||||
val cardData = CardData(
|
||||
blockchainName = preferences.getString("personalization_Blockchain", "BTC"),
|
||||
batchId = preferences.getString("personalization_card_batch", "FFFF"),
|
||||
productMask = productMask,
|
||||
tokenSymbol = tokenSymbol,
|
||||
tokenContractAddress = tokenContractAddress,
|
||||
tokenDecimal = tokenDecimal,
|
||||
issuerName = null,
|
||||
manufactureDateTime = Calendar.getInstance().time,
|
||||
manufacturerSignature = null)
|
||||
|
||||
|
||||
val ndefAar = preferences.getString("personalization_NDEF_AAR", "Release APP")
|
||||
val ndefUri = preferences.getString("personalization_NDEF_URI", "https://tangem.com")
|
||||
|
||||
val ndefs = mutableListOf<NdefRecord>()
|
||||
if (!ndefUri.isNullOrEmpty()) {
|
||||
ndefs.add(NdefRecord(NdefRecord.Type.URI, ndefUri))
|
||||
}
|
||||
if (ndefAar != "None") {
|
||||
val type = NdefRecord.Type.AAR
|
||||
val value = when (ndefAar) {
|
||||
"Debug APP" -> {
|
||||
"com.tangem.wallet.debug"
|
||||
}
|
||||
"Release APP" -> {
|
||||
"com.tangem.wallet"
|
||||
}
|
||||
"--- CUSTOM ---" -> {
|
||||
preferences.getString("personalization_NDEF_CUSTOM_AAR", "com.tangem.wallet")!!
|
||||
}
|
||||
else -> ""
|
||||
}
|
||||
ndefs.add(NdefRecord(type, value))
|
||||
}
|
||||
|
||||
return CardConfig(
|
||||
cardData = cardData,
|
||||
curveID = EllipticCurve.byName(preferences.getString("personalization_CurveId", "secp256k1")!!)
|
||||
?: EllipticCurve.Secp256k1,
|
||||
signingMethods = signingMethod,
|
||||
createWallet = preferences.getBoolean("personalization_CreateWallet", true),
|
||||
maxSignatures = preferences.getString("personalization_MaxSignatures", "1000")!!.toInt(),
|
||||
isReusable = preferences.getBoolean("personalization_SettingsMask_IsReusable", true),
|
||||
protocolAllowUnencrypted = preferences.getBoolean("personalization_SettingsMask_AllowEncryption_None", true),
|
||||
protocolAllowStaticEncryption = preferences.getBoolean("personalization_SettingsMask_AllowEncryption_Fast", true),
|
||||
useActivation = preferences.getBoolean("personalization_SettingsMask_NeedActivation", false),
|
||||
|
||||
useOneCommandAtTime = preferences.getBoolean("personalization_SettingsMask_OneApduAtOnce", false),
|
||||
useCvc = preferences.getBoolean("personalization_SettingsMask_UseCVC", false),
|
||||
useBlock = preferences.getBoolean("personalization_SettingsMask_UseBlock", false),
|
||||
allowSwapPin = preferences.getBoolean("personalization_SettingsMask_AllowSwapPIN", true),
|
||||
allowSwapPin2 = preferences.getBoolean("personalization_SettingsMask_AllowSwapPIN2", true),
|
||||
useNdef = preferences.getBoolean("personalization_SettingsMask_UseNDEF", true),
|
||||
useDynamicNdef = preferences.getBoolean("personalization_SettingsMask_UseDynamicNDEF", true),
|
||||
protectIssuerDataAgainstReplay = preferences.getBoolean("personalization_SettingsMask_ProtectIssuerDataAgainstReplay", true),
|
||||
forbidDefaultPin = preferences.getBoolean("personalization_SettingsMask_ForbidDefaultPIN", false),
|
||||
smartSecurityDelay = preferences.getBoolean("personalization_SettingsMask_SmartSecurityDelay", false),
|
||||
pauseBeforePin2 = preferences.getString("personalization_PauseBeforePIN2", "15")!!.toInt() * 1000,
|
||||
allowSelectBlockchain = preferences.getBoolean("personalization_SettingsMask_AllowSelectBlockchain", false),
|
||||
forbidPurgeWallet = preferences.getBoolean("personalization_SettingsMask_ForbidPurgeWallet", false)
|
||||
?: false,
|
||||
disablePrecomputedNdef = preferences.getBoolean("personalization_SettingsMask_DisablePrecomputedNDEF", false)
|
||||
?: false,
|
||||
skipSecurityDelayIfValidatedByIssuer = preferences.getBoolean("personalization_SettingsMask_SkipSecurityDelayIfValidatedByIssuer", true),
|
||||
skipCheckPIN2andCVCIfValidatedByIssuer = preferences.getBoolean("personalization_SettingsMask_SkipCheckPIN2andCVCIfValidatedByIssuer", true),
|
||||
|
||||
skipSecurityDelayIfValidatedByLinkedTerminal = preferences.getBoolean("personalization_SettingsMask_SkipSecurityDelayIfValidatedByLinkedTerminal", true),
|
||||
restrictOverwriteIssuerDataEx = preferences.getBoolean("personalization_SettingsMask_RestrictOverwriteIssuerDataEx", true),
|
||||
|
||||
requireTerminalTxSignature = preferences.getBoolean("personalization_SettingsMask_RequireTerminalTxSignature", false),
|
||||
requireTerminalCertSignature = preferences.getBoolean("personalization_SettingsMask_RequireTerminalCertSignature", false),
|
||||
checkPin3onCard = preferences.getBoolean("personalization_SettingsMask_CheckPIN3onCard", true),
|
||||
|
||||
cvc = preferences.getString("personalization_cvc", "000") ?: "000",
|
||||
pin = preferences.getString("personalization_pin", "000000") ?: "000000",
|
||||
pin2 = preferences.getString("personalization_pin2", "000") ?: "000",
|
||||
pin3 = preferences.getString("personalization_pin3", "123") ?: "123",
|
||||
hexCrExKey = preferences.getString("personalization_CrEx_Key", "00112233445566778899AABBCCDDEEFFFFEEDDCCBBAA998877665544332211000000111122223333444455556666777788889999AAAABBBBCCCCDDDDEEEEFFFF"),
|
||||
|
||||
ndefRecords = ndefs
|
||||
)
|
||||
}
|
||||
|
|
@ -11,9 +11,10 @@ import com.tangem.devkit.ucase.tunnel.ActionView
|
|||
import com.tangem.devkit.ucase.tunnel.ItemError
|
||||
import com.tangem.devkit.ucase.variants.personalize.CardNumberId
|
||||
import com.tangem.devkit.ucase.variants.personalize.converter.PersonalizationConfigConverter
|
||||
import com.tangem.devkit.ucase.variants.personalize.converter.PersonalizationConfigToCardConfig
|
||||
import com.tangem.devkit.ucase.variants.personalize.converter.PersonalizationJsonConverter
|
||||
import com.tangem.devkit.ucase.variants.personalize.dto.DefaultPersonalizationParams
|
||||
import com.tangem.devkit.ucase.variants.personalize.dto.PersonalizationConfig
|
||||
import com.tangem.devkit.ucase.variants.personalize.dto.toCardConfig
|
||||
import ru.dev.gbixahue.eu4d.lib.kotlin.stringOf
|
||||
|
||||
/**
|
||||
|
|
@ -37,10 +38,10 @@ class PersonalizeAction : BaseAction() {
|
|||
val acquirer = DefaultPersonalizationParams.acquirer()
|
||||
val manufacturer = DefaultPersonalizationParams.manufacturer()
|
||||
|
||||
val personalizeConfig = PersonalizationConfigConverter().convert(itemList, PersonalizationConfig.default())
|
||||
val cardConfig = PersonalizationConfigToCardConfig().convert(personalizeConfig)
|
||||
val config = PersonalizationConfigConverter().convert(itemList, PersonalizationConfig.default())
|
||||
val jsonDto = PersonalizationJsonConverter().bToA(config)
|
||||
|
||||
attrs.tangemSdk.personalize(cardConfig, issuer, manufacturer, acquirer) {
|
||||
attrs.tangemSdk.personalize(jsonDto.toCardConfig(), issuer, manufacturer, acquirer) {
|
||||
handleResult(payload, it, null, attrs, callback)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,10 @@ import com.tangem.devkit.commons.Store
|
|||
import com.tangem.devkit.ucase.domain.actions.PersonalizeAction
|
||||
import com.tangem.devkit.ucase.domain.paramsManager.ActionCallback
|
||||
import com.tangem.devkit.ucase.variants.personalize.converter.PersonalizationConfigConverter
|
||||
import com.tangem.devkit.ucase.variants.personalize.converter.PersonalizationJsonConverter
|
||||
import com.tangem.devkit.ucase.variants.personalize.dto.PersonalizationConfig
|
||||
import com.tangem.devkit.ucase.variants.personalize.dto.PersonalizationJson
|
||||
import ru.dev.gbixahue.eu4d.lib.android.global.log.Log
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
|
|
@ -27,6 +30,27 @@ class PersonalizationItemsManager(
|
|||
action.executeMainAction(this, getAttrsForAction(tangemSdk), callback)
|
||||
}
|
||||
|
||||
fun importJsonConfig(jsonString: String) {
|
||||
if (jsonString.isEmpty()) return
|
||||
|
||||
val jsonDto = try {
|
||||
PersonalizationJson.getJsonConverter().fromJson(jsonString, PersonalizationJson::class.java)
|
||||
} catch (ex: Exception) {
|
||||
Log.e(this, "Can't convert imported string to Json object. Error: $ex")
|
||||
return
|
||||
}
|
||||
|
||||
val config = PersonalizationJsonConverter().aToB(jsonDto)
|
||||
updateByItemList(converter.convert(config))
|
||||
}
|
||||
|
||||
fun exportJsonConfig(): String {
|
||||
val config = converter.convert(itemList, PersonalizationConfig.default())
|
||||
val jsonDto = PersonalizationJsonConverter().bToA(config)
|
||||
val jsonString = PersonalizationJson.getJsonConverter().toJson(jsonDto)
|
||||
return jsonString
|
||||
}
|
||||
|
||||
@OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
|
||||
fun onDestroy() {
|
||||
val config = converter.convert(itemList, PersonalizationConfig.default())
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@ class PersonalizationResources() {
|
|||
initCommon(holder)
|
||||
initSigningMethod(holder)
|
||||
initSignHashExProp(holder)
|
||||
initDenomination(holder)
|
||||
initToken(holder)
|
||||
initProductMask(holder)
|
||||
initSettingsMask(holder)
|
||||
|
|
@ -72,11 +71,6 @@ class PersonalizationResources() {
|
|||
holder.register(SignHashExPropId.CheckPin3, Resources(R.string.pers_item_check_pin3_on_card, R.string.info_pers_item_check_pin3_on_card))
|
||||
}
|
||||
|
||||
private fun initDenomination(holder: TypedHolder<Id, Resources>) {
|
||||
holder.register(DenominationId.WriteOnPersonalize, Resources(R.string.pers_item_write_on_personalize, R.string.info_pers_item_write_on_personalize))
|
||||
holder.register(DenominationId.Denomination, Resources(R.string.pers_item_denomination, R.string.info_pers_item_denomination))
|
||||
}
|
||||
|
||||
private fun initToken(holder: TypedHolder<Id, Resources>) {
|
||||
holder.register(TokenId.ItsToken, Resources(R.string.pers_item_its_token, R.string.info_pers_item_its_token))
|
||||
holder.register(TokenId.Symbol, Resources(R.string.pers_item_symbol, R.string.info_pers_item_symbol))
|
||||
|
|
|
|||
|
|
@ -54,11 +54,6 @@ enum class SignHashExPropId : PersonalizationId {
|
|||
CheckPin3,
|
||||
}
|
||||
|
||||
enum class DenominationId : PersonalizationId {
|
||||
WriteOnPersonalize,
|
||||
Denomination,
|
||||
}
|
||||
|
||||
enum class TokenId : PersonalizationId {
|
||||
ItsToken,
|
||||
Symbol,
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import com.tangem.devkit._arch.structure.Id
|
|||
import com.tangem.devkit._arch.structure.abstraction.KeyValue
|
||||
import com.tangem.devkit.ucase.variants.personalize.*
|
||||
import com.tangem.devkit.ucase.variants.personalize.dto.PersonalizationConfig
|
||||
import com.tangem.devkit.ucase.variants.personalize.dto.PersonalizationJson
|
||||
import ru.dev.gbixahue.eu4d.lib.kotlin.common.BaseTypedHolder
|
||||
|
||||
/**
|
||||
|
|
@ -16,9 +17,9 @@ class ConfigValuesHolder : BaseTypedHolder<Id, Value>() {
|
|||
fun init(default: PersonalizationConfig) {
|
||||
register(CardNumberId.Series, Value(default.series))
|
||||
register(CardNumberId.Number, Value(default.startNumber))
|
||||
register(CardNumberId.BatchId, Value(default.batchId))
|
||||
register(CardNumberId.BatchId, Value(default.cardData.batch))
|
||||
register(CommonId.Curve, Value(default.curveID, Helper.listOfCurves()))
|
||||
register(CommonId.Blockchain, Value(default.blockchain, Helper.listOfBlockchain()))
|
||||
register(CommonId.Blockchain, Value(default.cardData.blockchain, Helper.listOfBlockchain()))
|
||||
register(CommonId.BlockchainCustom, Value(default.blockchainCustom))
|
||||
register(CommonId.MaxSignatures, Value(default.MaxSignatures))
|
||||
register(CommonId.CreateWallet, Value(default.createWallet))
|
||||
|
|
@ -34,12 +35,10 @@ class ConfigValuesHolder : BaseTypedHolder<Id, Value>() {
|
|||
register(SignHashExPropId.RequireTerminalCertSig, Value(default.requireTerminalCertSignature))
|
||||
register(SignHashExPropId.RequireTerminalTxSig, Value(default.requireTerminalTxSignature))
|
||||
register(SignHashExPropId.CheckPin3, Value(default.checkPIN3onCard))
|
||||
register(DenominationId.WriteOnPersonalize, Value(default.writeOnPersonalization))
|
||||
register(DenominationId.Denomination, Value(default.denomination))
|
||||
register(TokenId.ItsToken, Value(default.itsToken))
|
||||
register(TokenId.Symbol, Value(default.symbol))
|
||||
register(TokenId.ContractAddress, Value(default.contractAddress))
|
||||
register(TokenId.Decimal, Value(default.decimal))
|
||||
register(TokenId.Symbol, Value(default.cardData.token_symbol))
|
||||
register(TokenId.ContractAddress, Value(default.cardData.token_contract_address))
|
||||
register(TokenId.Decimal, Value(default.cardData.token_decimal))
|
||||
register(ProductMaskId.Note, Value(default.cardData.product_note))
|
||||
register(ProductMaskId.Tag, Value(default.cardData.product_tag))
|
||||
register(ProductMaskId.IdCard, Value(default.cardData.product_id_card))
|
||||
|
|
@ -49,7 +48,7 @@ class ConfigValuesHolder : BaseTypedHolder<Id, Value>() {
|
|||
register(SettingsMaskId.ForbidPurge, Value(default.forbidPurgeWallet))
|
||||
register(SettingsMaskId.AllowSelectBlockchain, Value(default.allowSelectBlockchain))
|
||||
register(SettingsMaskId.UseBlock, Value(default.useBlock))
|
||||
register(SettingsMaskId.OneApdu, Value(default.oneApdu))
|
||||
register(SettingsMaskId.OneApdu, Value(default.useOneCommandAtTime))
|
||||
register(SettingsMaskId.UseCvc, Value(default.useCVC))
|
||||
register(SettingsMaskId.AllowSwapPin, Value(default.allowSwapPIN))
|
||||
register(SettingsMaskId.AllowSwapPin2, Value(default.allowSwapPIN2))
|
||||
|
|
@ -94,7 +93,7 @@ internal class Helper {
|
|||
|
||||
fun listOfBlockchain(): List<KeyValue> {
|
||||
return mutableListOf(
|
||||
KeyValue("--- CUSTOM ---", ""),
|
||||
KeyValue(PersonalizationJson.CUSTOM, PersonalizationJson.CUSTOM),
|
||||
KeyValue("BTC", "BTC"),
|
||||
KeyValue("BTC/test", "BTC/test"),
|
||||
KeyValue("ETH", "ETH"),
|
||||
|
|
@ -115,7 +114,7 @@ internal class Helper {
|
|||
|
||||
fun aarList(): List<KeyValue> {
|
||||
return mutableListOf(
|
||||
KeyValue("--- CUSTOM ---", ""),
|
||||
KeyValue(PersonalizationJson.CUSTOM, PersonalizationJson.CUSTOM),
|
||||
KeyValue("Release APP", "com.tangem.wallet"),
|
||||
KeyValue("Debug APP", "com.tangem.wallet.debug"),
|
||||
KeyValue("None", "")
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ class ItemTypes {
|
|||
CommonId.CreateWallet, SigningMethodId.SignTx, SigningMethodId.SignTxRaw, SigningMethodId.SignValidatedTx,
|
||||
SigningMethodId.SignValidatedTxRaw, SigningMethodId.SignValidatedTxIssuer,
|
||||
SigningMethodId.SignValidatedTxRawIssuer, SigningMethodId.SignExternal, SignHashExPropId.RequireTerminalCertSig,
|
||||
SignHashExPropId.RequireTerminalTxSig, SignHashExPropId.CheckPin3, DenominationId.WriteOnPersonalize, TokenId.ItsToken,
|
||||
SignHashExPropId.RequireTerminalTxSig, SignHashExPropId.CheckPin3, TokenId.ItsToken,
|
||||
ProductMaskId.Note, ProductMaskId.Tag, ProductMaskId.IdCard, ProductMaskId.IdIssuerCard, SettingsMaskId.IsReusable, SettingsMaskId.NeedActivation,
|
||||
SettingsMaskId.ForbidPurge, SettingsMaskId.AllowSelectBlockchain, SettingsMaskId.UseBlock, SettingsMaskId.OneApdu,
|
||||
SettingsMaskId.UseCvc, SettingsMaskId.AllowSwapPin, SettingsMaskId.AllowSwapPin2, SettingsMaskId.ForbidDefaultPin,
|
||||
|
|
@ -34,7 +34,7 @@ class ItemTypes {
|
|||
)
|
||||
|
||||
val numberList = mutableListOf(
|
||||
CardNumberId.Number, CommonId.MaxSignatures, SignHashExPropId.PinLessFloorLimit, DenominationId.Denomination, TokenId.Decimal
|
||||
CardNumberId.Number, CommonId.MaxSignatures, SignHashExPropId.PinLessFloorLimit, TokenId.Decimal
|
||||
)
|
||||
|
||||
val hiddenList = mutableListOf<Id>(
|
||||
|
|
|
|||
|
|
@ -52,9 +52,9 @@ class ItemsToPersonalizationConfig : ItemsToModel<PersonalizationConfig> {
|
|||
val export = PersonalizationConfig()
|
||||
export.series = getTyped(CardNumberId.Series)
|
||||
export.startNumber = getTyped(CardNumberId.Number)
|
||||
export.batchId = getTyped(CardNumberId.BatchId)
|
||||
export.cardData.batch = getTyped(CardNumberId.BatchId)
|
||||
export.curveID = getTyped(CommonId.Curve)
|
||||
export.blockchain = getTyped(CommonId.Blockchain)
|
||||
export.cardData.blockchain = getTyped(CommonId.Blockchain)
|
||||
export.blockchainCustom = getTyped(CommonId.BlockchainCustom)
|
||||
export.MaxSignatures = getTyped(CommonId.MaxSignatures)
|
||||
export.createWallet = getTyped(CommonId.CreateWallet)
|
||||
|
|
@ -70,12 +70,10 @@ class ItemsToPersonalizationConfig : ItemsToModel<PersonalizationConfig> {
|
|||
export.requireTerminalCertSignature = getTyped(SignHashExPropId.RequireTerminalCertSig)
|
||||
export.requireTerminalTxSignature = getTyped(SignHashExPropId.RequireTerminalTxSig)
|
||||
export.checkPIN3onCard = getTyped(SignHashExPropId.CheckPin3)
|
||||
export.writeOnPersonalization = getTyped(DenominationId.WriteOnPersonalize)
|
||||
export.denomination = getTyped(DenominationId.Denomination)
|
||||
export.itsToken = getTyped(TokenId.ItsToken)
|
||||
export.symbol = getTyped(TokenId.Symbol)
|
||||
export.contractAddress = getTyped(TokenId.ContractAddress)
|
||||
export.decimal = getTyped(TokenId.Decimal)
|
||||
export.cardData.token_symbol = getTyped(TokenId.Symbol)
|
||||
export.cardData.token_contract_address = getTyped(TokenId.ContractAddress)
|
||||
export.cardData.token_decimal = getTyped(TokenId.Decimal)
|
||||
export.cardData = export.cardData.apply { this.product_note = getTyped(ProductMaskId.Note) }
|
||||
export.cardData = export.cardData.apply { this.product_tag = getTyped(ProductMaskId.Tag) }
|
||||
export.cardData = export.cardData.apply { this.product_id_card = getTyped(ProductMaskId.IdCard) }
|
||||
|
|
@ -85,7 +83,7 @@ class ItemsToPersonalizationConfig : ItemsToModel<PersonalizationConfig> {
|
|||
export.forbidPurgeWallet = getTyped(SettingsMaskId.ForbidPurge)
|
||||
export.allowSelectBlockchain = getTyped(SettingsMaskId.AllowSelectBlockchain)
|
||||
export.useBlock = getTyped(SettingsMaskId.UseBlock)
|
||||
export.oneApdu = getTyped(SettingsMaskId.OneApdu)
|
||||
export.useOneCommandAtTime = getTyped(SettingsMaskId.OneApdu)
|
||||
export.useCVC = getTyped(SettingsMaskId.UseCvc)
|
||||
export.allowSwapPIN = getTyped(SettingsMaskId.AllowSwapPin)
|
||||
export.allowSwapPIN2 = getTyped(SettingsMaskId.AllowSwapPin2)
|
||||
|
|
@ -138,14 +136,13 @@ class PersonalizationConfigToItems : ModelToItems<PersonalizationConfig> {
|
|||
blocList.add(cardNumber())
|
||||
blocList.add(common())
|
||||
blocList.add(signingMethod())
|
||||
// blocList.add(signHashExProperties())
|
||||
// blocList.add(denomination())
|
||||
// blocList.add(token())
|
||||
// blocList.add(productMask())
|
||||
// blocList.add(settingsMask())
|
||||
// blocList.add(settingsMaskProtocolEnc())
|
||||
// blocList.add(settingsMaskNdef())
|
||||
// blocList.add(pins())
|
||||
blocList.add(signHashExProperties())
|
||||
blocList.add(token())
|
||||
blocList.add(productMask())
|
||||
blocList.add(settingsMask())
|
||||
blocList.add(settingsMaskProtocolEnc())
|
||||
blocList.add(settingsMaskNdef())
|
||||
blocList.add(pins())
|
||||
blocList.iterate {
|
||||
if (itemTypes.hiddenList.contains(it.id)) {
|
||||
it.viewModel.viewState.isVisibleState.value = false
|
||||
|
|
@ -203,15 +200,6 @@ class PersonalizationConfigToItems : ModelToItems<PersonalizationConfig> {
|
|||
return block
|
||||
}
|
||||
|
||||
private fun denomination(): ItemGroup {
|
||||
val block = createGroup(BlockId.Denomination)
|
||||
mutableListOf(
|
||||
DenominationId.WriteOnPersonalize,
|
||||
DenominationId.Denomination
|
||||
).forEach { createItem(block, it) }
|
||||
return block
|
||||
}
|
||||
|
||||
private fun token(): ItemGroup {
|
||||
val block = createGroup(BlockId.Token)
|
||||
mutableListOf(
|
||||
|
|
|
|||
|
|
@ -1,125 +0,0 @@
|
|||
package com.tangem.devkit.ucase.variants.personalize.converter
|
||||
|
||||
import com.tangem.commands.*
|
||||
import com.tangem.commands.personalization.entities.CardConfig
|
||||
import com.tangem.commands.personalization.entities.NdefRecord
|
||||
import com.tangem.devkit.ucase.variants.personalize.dto.PersonalizationConfig
|
||||
import ru.dev.gbixahue.eu4d.lib.kotlin.common.Converter
|
||||
import java.util.*
|
||||
|
||||
class PersonalizationConfigToCardConfig : Converter<PersonalizationConfig, CardConfig> {
|
||||
|
||||
override fun convert(from: PersonalizationConfig): CardConfig {
|
||||
val signingMethodMaskBuilder = SigningMethodMaskBuilder()
|
||||
if (from.SigningMethod0) {
|
||||
signingMethodMaskBuilder.add(SigningMethod.SignHash)
|
||||
}
|
||||
if (from.SigningMethod1) {
|
||||
signingMethodMaskBuilder.add(SigningMethod.SignRaw)
|
||||
}
|
||||
if (from.SigningMethod2) {
|
||||
signingMethodMaskBuilder.add(SigningMethod.SignHashValidateByIssuer)
|
||||
}
|
||||
if (from.SigningMethod3) {
|
||||
signingMethodMaskBuilder.add(SigningMethod.SignRawValidateByIssuer)
|
||||
}
|
||||
if (from.SigningMethod4) {
|
||||
signingMethodMaskBuilder.add(SigningMethod.SignHashValidateByIssuerWriteIssuerData)
|
||||
}
|
||||
if (from.SigningMethod5) {
|
||||
signingMethodMaskBuilder.add(SigningMethod.SignRawValidateByIssuerWriteIssuerData)
|
||||
}
|
||||
if (from.SigningMethod6) {
|
||||
signingMethodMaskBuilder.add(SigningMethod.SignHash)
|
||||
}
|
||||
val signingMethod = signingMethodMaskBuilder.build()
|
||||
|
||||
val isNote = from.cardData.product_note
|
||||
val isTag = from.cardData.product_tag
|
||||
val isIdCard = from.cardData.product_id_card
|
||||
val isIdIssuer = from.cardData.product_id_issuer
|
||||
|
||||
val productMaskBuilder = ProductMaskBuilder()
|
||||
if (isNote) productMaskBuilder.add(com.tangem.commands.Product.Note)
|
||||
if (isTag) productMaskBuilder.add(com.tangem.commands.Product.Tag)
|
||||
if (isIdCard) productMaskBuilder.add(com.tangem.commands.Product.IdCard)
|
||||
if (isIdIssuer) productMaskBuilder.add(com.tangem.commands.Product.IdIssuer)
|
||||
val productMask = productMaskBuilder.build()
|
||||
|
||||
var tokenSymbol: String? = null
|
||||
var tokenContractAddress: String? = null
|
||||
var tokenDecimal: Int? = null
|
||||
if (from.itsToken) {
|
||||
tokenSymbol = from.symbol
|
||||
tokenContractAddress = from.contractAddress
|
||||
tokenDecimal = from.decimal.toInt()
|
||||
}
|
||||
|
||||
val blockchain = if (from.blockchain.isNotEmpty()) from.blockchain else from.blockchainCustom
|
||||
val cardData = CardData(
|
||||
blockchainName = blockchain,
|
||||
batchId = from.batchId,
|
||||
productMask = productMask,
|
||||
tokenSymbol = tokenSymbol,
|
||||
tokenContractAddress = tokenContractAddress,
|
||||
tokenDecimal = tokenDecimal,
|
||||
issuerName = null,
|
||||
manufactureDateTime = Calendar.getInstance().time,
|
||||
manufacturerSignature = null)
|
||||
|
||||
|
||||
val ndefs = mutableListOf<NdefRecord>()
|
||||
if (from.uri.isNotEmpty()) {
|
||||
ndefs.add(NdefRecord(NdefRecord.Type.URI, from.uri))
|
||||
}
|
||||
when (from.aar) {
|
||||
"None" -> null
|
||||
"--- CUSTOM ---" -> NdefRecord(NdefRecord.Type.AAR, from.aarCustom)
|
||||
else -> NdefRecord(NdefRecord.Type.AAR, from.aar)
|
||||
}?.let { ndefs.add(it) }
|
||||
|
||||
return CardConfig(
|
||||
"Tangem",
|
||||
"Tangem Test",
|
||||
from.series,
|
||||
from.startNumber,
|
||||
1000,
|
||||
from.PIN,
|
||||
from.PIN2,
|
||||
from.PIN3,
|
||||
from.hexCrExKey,
|
||||
from.CVC,
|
||||
from.pauseBeforePIN2.toInt(),
|
||||
from.smartSecurityDelay,
|
||||
EllipticCurve.byName(from.curveID) ?: EllipticCurve.Secp256k1,
|
||||
signingMethod,
|
||||
from.MaxSignatures.toInt(),
|
||||
from.isReusable,
|
||||
from.allowSwapPIN,
|
||||
from.allowSwapPIN2,
|
||||
from.useActivation,
|
||||
from.useCVC,
|
||||
from.useNDEF,
|
||||
from.useDynamicNDEF,
|
||||
from.oneApdu,
|
||||
from.useBlock,
|
||||
from.allowSelectBlockchain,
|
||||
from.forbidPurgeWallet,
|
||||
from.protocolAllowUnencrypted,
|
||||
from.protocolAllowStaticEncryption,
|
||||
from.protectIssuerDataAgainstReplay,
|
||||
from.forbidDefaultPIN,
|
||||
from.disablePrecomputedNDEF,
|
||||
from.skipSecurityDelayIfValidatedByIssuer,
|
||||
from.skipCheckPIN2andCVCIfValidatedByIssuer,
|
||||
from.skipSecurityDelayIfValidatedByLinkedTerminal,
|
||||
from.restrictOverwriteIssuerDataEx,
|
||||
from.requireTerminalTxSignature,
|
||||
from.requireTerminalCertSignature,
|
||||
from.checkPIN3onCard,
|
||||
from.createWallet,
|
||||
cardData,
|
||||
ndefs
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,203 @@
|
|||
package com.tangem.devkit.ucase.variants.personalize.converter
|
||||
|
||||
import com.tangem.commands.EllipticCurve
|
||||
import com.tangem.commands.SigningMethod
|
||||
import com.tangem.commands.SigningMethodMask
|
||||
import com.tangem.commands.personalization.entities.NdefRecord
|
||||
import com.tangem.devkit._arch.structure.abstraction.TwoWayConverter
|
||||
import com.tangem.devkit.ucase.variants.personalize.dto.PersonalizationConfig
|
||||
import com.tangem.devkit.ucase.variants.personalize.dto.PersonalizationJson
|
||||
import ru.dev.gbixahue.eu4d.lib.kotlin.common.Converter
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
class PersonalizationJsonConverter : TwoWayConverter<PersonalizationJson, PersonalizationConfig> {
|
||||
|
||||
override fun aToB(from: PersonalizationJson): PersonalizationConfig = JsonToConfig().convert(from)
|
||||
|
||||
override fun bToA(from: PersonalizationConfig): PersonalizationJson = ConfigToJson().convert(from)
|
||||
}
|
||||
|
||||
internal class JsonToConfig : Converter<PersonalizationJson, PersonalizationConfig> {
|
||||
|
||||
override fun convert(jsonDto: PersonalizationJson): PersonalizationConfig {
|
||||
val config = PersonalizationConfig.default()
|
||||
config.apply {
|
||||
series = jsonDto.series
|
||||
startNumber = jsonDto.startNumber
|
||||
curveID = EllipticCurve.byName(jsonDto.curveID)?.curve ?: ""
|
||||
blockchainCustom = ""
|
||||
MaxSignatures = jsonDto.MaxSignatures
|
||||
createWallet = jsonDto.createWallet != 0L
|
||||
hexCrExKey = jsonDto.hexCrExKey
|
||||
requireTerminalTxSignature = jsonDto.requireTerminalTxSignature
|
||||
requireTerminalCertSignature = jsonDto.requireTerminalCertSignature
|
||||
checkPIN3onCard = jsonDto.checkPIN3onCard
|
||||
isReusable = jsonDto.isReusable
|
||||
useActivation = jsonDto.useActivation
|
||||
forbidPurgeWallet = jsonDto.forbidPurgeWallet
|
||||
allowSelectBlockchain = jsonDto.allowSelectBlockchain
|
||||
useBlock = jsonDto.useBlock
|
||||
useOneCommandAtTime = jsonDto.useOneCommandAtTime
|
||||
useCVC = jsonDto.useCVC
|
||||
allowSwapPIN = jsonDto.allowSwapPIN
|
||||
allowSwapPIN2 = jsonDto.allowSwapPIN2
|
||||
forbidDefaultPIN = jsonDto.forbidDefaultPIN
|
||||
smartSecurityDelay = jsonDto.smartSecurityDelay
|
||||
protectIssuerDataAgainstReplay = jsonDto.protectIssuerDataAgainstReplay
|
||||
skipSecurityDelayIfValidatedByIssuer = jsonDto.skipSecurityDelayIfValidatedByIssuer
|
||||
skipCheckPIN2andCVCIfValidatedByIssuer = jsonDto.skipCheckPIN2andCVCIfValidatedByIssuer
|
||||
skipSecurityDelayIfValidatedByLinkedTerminal = jsonDto.skipSecurityDelayIfValidatedByLinkedTerminal
|
||||
restrictOverwriteIssuerDataEx = jsonDto.restrictOverwriteIssuerDataEx
|
||||
protocolAllowUnencrypted = jsonDto.protocolAllowUnencrypted
|
||||
protocolAllowStaticEncryption = jsonDto.protocolAllowStaticEncryption
|
||||
useNDEF = jsonDto.useNDEF
|
||||
useDynamicNDEF = jsonDto.useDynamicNDEF
|
||||
disablePrecomputedNDEF = jsonDto.disablePrecomputedNDEF
|
||||
PIN = jsonDto.PIN
|
||||
PIN2 = jsonDto.PIN2
|
||||
PIN3 = jsonDto.PIN3
|
||||
CVC = jsonDto.CVC
|
||||
pauseBeforePIN2 = jsonDto.pauseBeforePIN2
|
||||
count = jsonDto.count
|
||||
issuerName = jsonDto.issuerName
|
||||
issuerData = jsonDto.issuerData
|
||||
// numberFormat = from.numberFormat
|
||||
// pinLessFloorLimit = 100000L
|
||||
}
|
||||
|
||||
fillCardData(jsonDto, config)
|
||||
fillSigningMethod(jsonDto, config)
|
||||
fillNdef(jsonDto, config)
|
||||
|
||||
return config
|
||||
}
|
||||
|
||||
private fun fillCardData(jsonDto: PersonalizationJson, config: PersonalizationConfig) {
|
||||
val jsonCardData = jsonDto.cardData
|
||||
|
||||
// copy whole object and then checking tricky places
|
||||
config.cardData = jsonCardData
|
||||
|
||||
config.itsToken = jsonCardData.token_contract_address.isNotEmpty() || jsonCardData.token_symbol.isNotEmpty()
|
||||
|
||||
val selectedBlockchain = Helper.listOfBlockchain().firstOrNull { it.value == jsonCardData.blockchain }
|
||||
if (selectedBlockchain == null) {
|
||||
config.cardData.blockchain = PersonalizationJson.CUSTOM
|
||||
config.blockchainCustom = jsonCardData.blockchain
|
||||
}
|
||||
}
|
||||
|
||||
private fun fillSigningMethod(jsonDto: PersonalizationJson, config: PersonalizationConfig) {
|
||||
val mask = SigningMethodMask(jsonDto.SigningMethod.toInt())
|
||||
if (mask.contains(SigningMethod.SignHash)) config.SigningMethod0 = true
|
||||
if (mask.contains(SigningMethod.SignRaw)) config.SigningMethod1 = true
|
||||
if (mask.contains(SigningMethod.SignHashValidateByIssuer)) config.SigningMethod2 = true
|
||||
if (mask.contains(SigningMethod.SignRawValidateByIssuer)) config.SigningMethod3 = true
|
||||
if (mask.contains(SigningMethod.SignHashValidateByIssuerWriteIssuerData)) config.SigningMethod4 = true
|
||||
if (mask.contains(SigningMethod.SignRawValidateByIssuerWriteIssuerData)) config.SigningMethod5 = true
|
||||
}
|
||||
|
||||
private fun fillNdef(jsonDto: PersonalizationJson, config: PersonalizationConfig) {
|
||||
jsonDto.ndef.forEach { record ->
|
||||
when (record.type) {
|
||||
NdefRecord.Type.URI -> config.uri = record.value
|
||||
NdefRecord.Type.AAR -> config.aar = record.value
|
||||
NdefRecord.Type.TEXT -> {
|
||||
}
|
||||
}
|
||||
}
|
||||
val selectedAar = Helper.aarList().firstOrNull { it.value == config.aar }
|
||||
if (selectedAar == null) {
|
||||
config.aarCustom = config.aar
|
||||
config.aar = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class ConfigToJson : Converter<PersonalizationConfig, PersonalizationJson> {
|
||||
|
||||
override fun convert(from: PersonalizationConfig): PersonalizationJson {
|
||||
val jsonDto = PersonalizationJson()
|
||||
jsonDto.apply {
|
||||
releaseVersion = false
|
||||
issuerName = from.issuerName
|
||||
series = from.series
|
||||
startNumber = from.startNumber
|
||||
count = from.count
|
||||
PIN = from.PIN
|
||||
PIN2 = from.PIN2
|
||||
PIN3 = from.PIN3
|
||||
hexCrExKey = from.hexCrExKey
|
||||
CVC = from.CVC
|
||||
pauseBeforePIN2 = from.pauseBeforePIN2
|
||||
smartSecurityDelay = from.smartSecurityDelay
|
||||
curveID = from.curveID
|
||||
MaxSignatures = from.MaxSignatures
|
||||
isReusable = from.isReusable
|
||||
allowSwapPIN = from.allowSwapPIN
|
||||
allowSwapPIN2 = from.allowSwapPIN2
|
||||
useActivation = from.useActivation
|
||||
useCVC = from.useCVC
|
||||
useNDEF = from.useNDEF
|
||||
useDynamicNDEF = from.useDynamicNDEF
|
||||
useOneCommandAtTime = from.useOneCommandAtTime
|
||||
useBlock = from.useBlock
|
||||
allowSelectBlockchain = from.allowSelectBlockchain
|
||||
forbidPurgeWallet = from.forbidPurgeWallet
|
||||
protocolAllowUnencrypted = from.protocolAllowUnencrypted
|
||||
protocolAllowStaticEncryption = from.protocolAllowStaticEncryption
|
||||
protectIssuerDataAgainstReplay = from.protectIssuerDataAgainstReplay
|
||||
forbidDefaultPIN = from.forbidDefaultPIN
|
||||
disablePrecomputedNDEF = from.disablePrecomputedNDEF
|
||||
skipSecurityDelayIfValidatedByIssuer = from.skipSecurityDelayIfValidatedByIssuer
|
||||
skipCheckPIN2andCVCIfValidatedByIssuer = from.skipCheckPIN2andCVCIfValidatedByIssuer
|
||||
skipSecurityDelayIfValidatedByLinkedTerminal = from.skipSecurityDelayIfValidatedByLinkedTerminal
|
||||
restrictOverwriteIssuerDataEx = from.restrictOverwriteIssuerDataEx
|
||||
requireTerminalTxSignature = from.requireTerminalTxSignature
|
||||
requireTerminalCertSignature = from.requireTerminalCertSignature
|
||||
checkPIN3onCard = from.checkPIN3onCard
|
||||
createWallet = if (from.createWallet) 1 else 0
|
||||
issuerData = from.issuerData
|
||||
// numberFormat = from.numberFormat
|
||||
// pinLessFloorLimit = 100000L
|
||||
}
|
||||
|
||||
fillCardData(from, jsonDto)
|
||||
fillSigningMethod(from, jsonDto)
|
||||
fillNdef(from, jsonDto)
|
||||
return jsonDto
|
||||
}
|
||||
|
||||
private fun fillCardData(config: PersonalizationConfig, jsonDto: PersonalizationJson) {
|
||||
jsonDto.cardData = config.cardData
|
||||
if (config.cardData.blockchain == PersonalizationJson.CUSTOM) {
|
||||
jsonDto.cardData.blockchain = config.blockchainCustom
|
||||
}
|
||||
if (config.itsToken) {
|
||||
jsonDto.cardData.token_contract_address = config.cardData.token_contract_address
|
||||
jsonDto.cardData.token_symbol = config.cardData.token_symbol
|
||||
jsonDto.cardData.token_decimal = config.cardData.token_decimal
|
||||
} else {
|
||||
jsonDto.cardData.token_contract_address = ""
|
||||
jsonDto.cardData.token_symbol = ""
|
||||
jsonDto.cardData.token_decimal = 0
|
||||
}
|
||||
}
|
||||
|
||||
private fun fillSigningMethod(config: PersonalizationConfig, jsonDto: PersonalizationJson) {
|
||||
jsonDto.SigningMethod = PersonalizationConfig.makeSigningMethodMask(config).rawValue.toLong()
|
||||
}
|
||||
|
||||
private fun fillNdef(config: PersonalizationConfig, jsonDto: PersonalizationJson) {
|
||||
fun add(value: String, type: NdefRecord.Type) {
|
||||
if (value.isNotEmpty()) jsonDto.ndef.add(NdefRecord(type, value))
|
||||
}
|
||||
|
||||
if (config.aarCustom.isNotEmpty()) add(config.aarCustom, NdefRecord.Type.AAR)
|
||||
else add(config.aar, NdefRecord.Type.AAR)
|
||||
|
||||
add(config.uri, NdefRecord.Type.URI)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,9 @@
|
|||
package com.tangem.devkit.ucase.variants.personalize.dto
|
||||
|
||||
import com.tangem.commands.EllipticCurve
|
||||
import com.tangem.commands.SigningMethod
|
||||
import com.tangem.commands.SigningMethodMask
|
||||
import com.tangem.commands.SigningMethodMaskBuilder
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
|
|
@ -10,11 +13,9 @@ class PersonalizationConfig {
|
|||
// Card number
|
||||
var series = ""
|
||||
var startNumber: Long = 0
|
||||
var batchId = ""
|
||||
|
||||
// Common
|
||||
var curveID = ""
|
||||
var blockchain = ""
|
||||
var blockchainCustom = ""
|
||||
var MaxSignatures: Long = 0
|
||||
var createWallet = false
|
||||
|
|
@ -35,16 +36,10 @@ class PersonalizationConfig {
|
|||
var requireTerminalCertSignature = false
|
||||
var checkPIN3onCard = false
|
||||
|
||||
// Denomination
|
||||
var writeOnPersonalization = false
|
||||
var denomination: Long = 0
|
||||
|
||||
// Token
|
||||
var itsToken = false
|
||||
var symbol = ""
|
||||
var contractAddress = ""
|
||||
var decimal: Long = 0
|
||||
|
||||
// Product mask
|
||||
var cardData = CardData()
|
||||
|
||||
// Settings mask
|
||||
|
|
@ -53,7 +48,7 @@ class PersonalizationConfig {
|
|||
var forbidPurgeWallet = false
|
||||
var allowSelectBlockchain = false
|
||||
var useBlock = false
|
||||
var oneApdu = false
|
||||
var useOneCommandAtTime = false
|
||||
var useCVC = false
|
||||
var allowSwapPIN = false
|
||||
var allowSwapPIN2 = false
|
||||
|
|
@ -69,6 +64,7 @@ class PersonalizationConfig {
|
|||
var protocolAllowUnencrypted = false
|
||||
var protocolAllowStaticEncryption = false
|
||||
|
||||
// Settings mask
|
||||
var useNDEF = false
|
||||
var useDynamicNDEF = false
|
||||
var disablePrecomputedNDEF = false
|
||||
|
|
@ -83,17 +79,20 @@ class PersonalizationConfig {
|
|||
var CVC = ""
|
||||
var pauseBeforePIN2: Long = 0
|
||||
|
||||
var count: Long = 0
|
||||
var issuerName = ""
|
||||
var issuerData = null
|
||||
|
||||
companion object {
|
||||
|
||||
fun default(): PersonalizationConfig {
|
||||
return PersonalizationConfig().apply {
|
||||
// Card number
|
||||
series = "BB"
|
||||
startNumber = 300000000000L
|
||||
batchId = "ffff"
|
||||
|
||||
// Common
|
||||
curveID = EllipticCurve.Secp256k1.curve
|
||||
blockchain = "ETH"
|
||||
blockchainCustom = ""
|
||||
MaxSignatures = 999999L
|
||||
createWallet = true
|
||||
|
|
@ -114,17 +113,10 @@ class PersonalizationConfig {
|
|||
requireTerminalCertSignature = false
|
||||
checkPIN3onCard = true
|
||||
|
||||
// Denomination
|
||||
writeOnPersonalization = false
|
||||
denomination = 1000000L
|
||||
|
||||
// Token
|
||||
itsToken = false
|
||||
symbol = ""
|
||||
contractAddress = ""
|
||||
decimal = 0L
|
||||
|
||||
cardData = CardData()
|
||||
cardData = CardData.default()
|
||||
|
||||
// Settings mask
|
||||
isReusable = true
|
||||
|
|
@ -132,7 +124,7 @@ class PersonalizationConfig {
|
|||
forbidPurgeWallet = false
|
||||
allowSelectBlockchain = false
|
||||
useBlock = false
|
||||
oneApdu = false
|
||||
useOneCommandAtTime = false
|
||||
useCVC = false
|
||||
allowSwapPIN = true
|
||||
allowSwapPIN2 = true
|
||||
|
|
@ -161,17 +153,67 @@ class PersonalizationConfig {
|
|||
PIN3 = ""
|
||||
CVC = "000"
|
||||
pauseBeforePIN2 = 5000L
|
||||
|
||||
count = 1050
|
||||
issuerName = "TANGEM"
|
||||
issuerData = null
|
||||
}
|
||||
}
|
||||
|
||||
fun makeSigningMethodMask(from: PersonalizationConfig): SigningMethodMask {
|
||||
val signingMethodMaskBuilder = SigningMethodMaskBuilder()
|
||||
if (from.SigningMethod0) {
|
||||
signingMethodMaskBuilder.add(SigningMethod.SignHash)
|
||||
}
|
||||
if (from.SigningMethod1) {
|
||||
signingMethodMaskBuilder.add(SigningMethod.SignRaw)
|
||||
}
|
||||
if (from.SigningMethod2) {
|
||||
signingMethodMaskBuilder.add(SigningMethod.SignHashValidateByIssuer)
|
||||
}
|
||||
if (from.SigningMethod3) {
|
||||
signingMethodMaskBuilder.add(SigningMethod.SignRawValidateByIssuer)
|
||||
}
|
||||
if (from.SigningMethod4) {
|
||||
signingMethodMaskBuilder.add(SigningMethod.SignHashValidateByIssuerWriteIssuerData)
|
||||
}
|
||||
if (from.SigningMethod5) {
|
||||
signingMethodMaskBuilder.add(SigningMethod.SignRawValidateByIssuerWriteIssuerData)
|
||||
}
|
||||
if (from.SigningMethod6) {
|
||||
signingMethodMaskBuilder.add(SigningMethod.SignHash)
|
||||
}
|
||||
return signingMethodMaskBuilder.build()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class CardData {
|
||||
var date = "2020-02-17"
|
||||
var batch = "FF87"
|
||||
var blockchain = "IROHA"
|
||||
var product_note = true
|
||||
var date = ""
|
||||
var batch = ""
|
||||
var blockchain = ""
|
||||
var token_symbol = ""
|
||||
var token_contract_address = ""
|
||||
var token_decimal: Long = 0
|
||||
var product_note = false
|
||||
var product_tag = false
|
||||
var product_id_card = false
|
||||
var product_id_issuer = false
|
||||
|
||||
companion object {
|
||||
fun default(): CardData {
|
||||
return CardData().apply {
|
||||
date = "2020-02-17"
|
||||
batch = "FFFF"
|
||||
blockchain = "ETH"
|
||||
token_symbol = ""
|
||||
token_contract_address = ""
|
||||
token_decimal = 0
|
||||
product_note = true
|
||||
product_tag = false
|
||||
product_id_card = false
|
||||
product_id_issuer = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,138 @@
|
|||
package com.tangem.devkit.ucase.variants.personalize.dto
|
||||
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.GsonBuilder
|
||||
import com.tangem.commands.EllipticCurve
|
||||
import com.tangem.commands.Product
|
||||
import com.tangem.commands.ProductMaskBuilder
|
||||
import com.tangem.commands.SigningMethodMask
|
||||
import com.tangem.commands.personalization.entities.CardConfig
|
||||
import com.tangem.commands.personalization.entities.NdefRecord
|
||||
import java.util.*
|
||||
|
||||
class PersonalizationJson {
|
||||
|
||||
var issuerName = ""
|
||||
var series = ""
|
||||
var startNumber = 0L
|
||||
var count = 0L
|
||||
var PIN = ""
|
||||
var PIN2 = ""
|
||||
var PIN3 = ""
|
||||
var hexCrExKey = ""
|
||||
var CVC = ""
|
||||
var pauseBeforePIN2 = 0L
|
||||
var smartSecurityDelay = false
|
||||
var curveID = ""
|
||||
var SigningMethod = 0L
|
||||
var MaxSignatures = 0L
|
||||
var isReusable = false
|
||||
var allowSwapPIN = false
|
||||
var allowSwapPIN2 = false
|
||||
var useActivation = false
|
||||
var useCVC = false
|
||||
var useNDEF = false
|
||||
var useDynamicNDEF = false
|
||||
var useOneCommandAtTime = false
|
||||
var useBlock = false
|
||||
var allowSelectBlockchain = false
|
||||
var forbidPurgeWallet = false
|
||||
var protocolAllowUnencrypted = false
|
||||
var protocolAllowStaticEncryption = false
|
||||
var protectIssuerDataAgainstReplay = false
|
||||
var forbidDefaultPIN = false
|
||||
var disablePrecomputedNDEF = false
|
||||
var skipSecurityDelayIfValidatedByIssuer = false
|
||||
var skipCheckPIN2andCVCIfValidatedByIssuer = false
|
||||
var skipSecurityDelayIfValidatedByLinkedTerminal = false
|
||||
var restrictOverwriteIssuerDataEx = false
|
||||
var requireTerminalTxSignature = false
|
||||
var requireTerminalCertSignature = false
|
||||
var checkPIN3onCard = false
|
||||
var createWallet = 0L
|
||||
var issuerData = null
|
||||
|
||||
var ndef = mutableListOf<NdefRecord>()
|
||||
var cardData = CardData()
|
||||
|
||||
var releaseVersion = false
|
||||
var numberFormat = ""
|
||||
|
||||
companion object {
|
||||
const val CUSTOM = "--- CUSTOM ---"
|
||||
|
||||
fun getJsonConverter(): Gson {
|
||||
val builder = GsonBuilder().setPrettyPrinting()
|
||||
return builder.create()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun PersonalizationJson.toCardConfig(): CardConfig {
|
||||
val isNote = cardData.product_note
|
||||
val isTag = cardData.product_tag
|
||||
val isIdCard = cardData.product_id_card
|
||||
val isIdIssuer = cardData.product_id_issuer
|
||||
|
||||
val productMaskBuilder = ProductMaskBuilder()
|
||||
if (isNote) productMaskBuilder.add(Product.Note)
|
||||
if (isTag) productMaskBuilder.add(Product.Tag)
|
||||
if (isIdCard) productMaskBuilder.add(Product.IdCard)
|
||||
if (isIdIssuer) productMaskBuilder.add(Product.IdIssuer)
|
||||
val productMask = productMaskBuilder.build()
|
||||
|
||||
val sdkCardData = com.tangem.commands.CardData(
|
||||
blockchainName = this.cardData.blockchain,
|
||||
batchId = this.cardData.batch,
|
||||
productMask = productMask,
|
||||
tokenSymbol = this.cardData.token_symbol,
|
||||
tokenContractAddress = this.cardData.token_contract_address,
|
||||
tokenDecimal = this.cardData.token_decimal.toInt(),
|
||||
issuerName = this.issuerName,
|
||||
manufactureDateTime = Calendar.getInstance().time,
|
||||
manufacturerSignature = null)
|
||||
|
||||
return CardConfig(
|
||||
this.issuerName,
|
||||
"Tangem Test",
|
||||
this.series,
|
||||
this.startNumber,
|
||||
this.count.toInt(),
|
||||
this.PIN,
|
||||
this.PIN2,
|
||||
this.PIN3,
|
||||
this.hexCrExKey,
|
||||
this.CVC,
|
||||
this.pauseBeforePIN2.toInt(),
|
||||
this.smartSecurityDelay,
|
||||
EllipticCurve.byName(this.curveID) ?: EllipticCurve.Secp256k1,
|
||||
SigningMethodMask(this.SigningMethod.toInt()),
|
||||
this.MaxSignatures.toInt(),
|
||||
this.isReusable,
|
||||
this.allowSwapPIN,
|
||||
this.allowSwapPIN2,
|
||||
this.useActivation,
|
||||
this.useCVC,
|
||||
this.useNDEF,
|
||||
this.useDynamicNDEF,
|
||||
this.useOneCommandAtTime,
|
||||
this.useBlock,
|
||||
this.allowSelectBlockchain,
|
||||
this.forbidPurgeWallet,
|
||||
this.protocolAllowUnencrypted,
|
||||
this.protocolAllowStaticEncryption,
|
||||
this.protectIssuerDataAgainstReplay,
|
||||
this.forbidDefaultPIN,
|
||||
this.disablePrecomputedNDEF,
|
||||
this.skipSecurityDelayIfValidatedByIssuer,
|
||||
this.skipCheckPIN2andCVCIfValidatedByIssuer,
|
||||
this.skipSecurityDelayIfValidatedByLinkedTerminal,
|
||||
this.restrictOverwriteIssuerDataEx,
|
||||
this.requireTerminalTxSignature,
|
||||
this.requireTerminalCertSignature,
|
||||
this.checkPIN3onCard,
|
||||
this.createWallet != 0L,
|
||||
sdkCardData,
|
||||
this.ndef
|
||||
)
|
||||
}
|
||||
|
|
@ -24,6 +24,7 @@ import com.tangem.devkit._arch.widget.WidgetBuilder
|
|||
import com.tangem.devkit.commons.DialogController
|
||||
import com.tangem.devkit.commons.view.MultiActionView
|
||||
import com.tangem.devkit.commons.view.ViewAction
|
||||
import com.tangem.devkit.extensions.copyToClipboard
|
||||
import com.tangem.devkit.extensions.view.beginDelayedTransition
|
||||
import com.tangem.devkit.ucase.domain.paramsManager.ItemsManager
|
||||
import com.tangem.devkit.ucase.domain.paramsManager.PayloadKey
|
||||
|
|
@ -50,7 +51,12 @@ import ru.dev.gbixahue.eu4d.lib.android.global.threading.postWork
|
|||
*/
|
||||
class PersonalizationFragment : BaseCardActionFragment(), PersonalizationPresetView {
|
||||
|
||||
override val itemsManager: ItemsManager by lazy { PersonalizationItemsManager(PersonalizationConfigStore(requireContext())) }
|
||||
private lateinit var personalizationItemsManager: PersonalizationItemsManager
|
||||
|
||||
override val itemsManager: ItemsManager by lazy {
|
||||
personalizationItemsManager = PersonalizationItemsManager(PersonalizationConfigStore(requireContext()))
|
||||
personalizationItemsManager
|
||||
}
|
||||
|
||||
override fun getLayoutId(): Int = R.layout.fg_base_action_layout
|
||||
|
||||
|
|
@ -106,20 +112,47 @@ class PersonalizationFragment : BaseCardActionFragment(), PersonalizationPresetV
|
|||
override fun widgetsWasCreated() {
|
||||
super.widgetsWasCreated()
|
||||
|
||||
val btnContainer = contentContainer.inflate<ViewGroup>(R.layout.view_simple_button)
|
||||
val btn = btnContainer.findViewById<Button>(R.id.button)
|
||||
val footerContainer = contentContainer.inflate<ViewGroup>(R.layout.fg_personalization_footer)
|
||||
|
||||
val show = StringId("show")
|
||||
val hide = StringId("hide")
|
||||
val multiAction = MultiActionView(mutableListOf(
|
||||
ViewAction(show, R.string.show_rare_fields) { actionVM.showFields(ActionType.Personalize) },
|
||||
ViewAction(hide, R.string.hide_rare_fields) { actionVM.hideFields(ActionType.Personalize) }
|
||||
), btn)
|
||||
multiAction.afterAction = {
|
||||
multiAction.state = if (it == show) hide else show
|
||||
fun initShowHideBtn(parent: ViewGroup) {
|
||||
val btn = parent.findViewById<Button>(R.id.btn_show_hide_fields)
|
||||
|
||||
val show = StringId("show")
|
||||
val hide = StringId("hide")
|
||||
val multiAction = MultiActionView(mutableListOf(
|
||||
ViewAction(show, R.string.show_rare_fields) { actionVM.showFields(ActionType.Personalize) },
|
||||
ViewAction(hide, R.string.hide_rare_fields) { actionVM.hideFields(ActionType.Personalize) }
|
||||
), btn)
|
||||
multiAction.afterAction = {
|
||||
multiAction.state = if (it == show) hide else show
|
||||
}
|
||||
multiAction.performAction(hide)
|
||||
}
|
||||
multiAction.performAction(hide)
|
||||
contentContainer.addView(btnContainer)
|
||||
|
||||
fun initImportExportJson(parent: ViewGroup) {
|
||||
val tvJsonExport = parent.findViewById<EditText>(R.id.et_json_export)
|
||||
val btnExportJson = parent.findViewById<Button>(R.id.btn_export_json)
|
||||
|
||||
tvJsonExport.setOnClickListener {
|
||||
val jsonString = tvJsonExport.text
|
||||
Log.w(this, jsonString)
|
||||
requireContext().copyToClipboard(jsonString, "Exported Json")
|
||||
}
|
||||
btnExportJson.setOnClickListener {
|
||||
tvJsonExport.setText(personalizationItemsManager.exportJsonConfig())
|
||||
}
|
||||
|
||||
val tvJsonImport = parent.findViewById<EditText>(R.id.et_json_import)
|
||||
val btnImportJson = parent.findViewById<Button>(R.id.btn_import_json)
|
||||
btnImportJson.setOnClickListener {
|
||||
personalizationItemsManager.importJsonConfig(tvJsonImport.text.toString().trim())
|
||||
}
|
||||
}
|
||||
|
||||
initShowHideBtn(footerContainer)
|
||||
initImportExportJson(footerContainer)
|
||||
|
||||
contentContainer.addView(footerContainer)
|
||||
}
|
||||
|
||||
override fun handleResponseCardData(card: Card) {
|
||||
|
|
|
|||
|
|
@ -1,11 +1,9 @@
|
|||
package com.tangem.devkit.ucase.variants.responses.ui.widget
|
||||
|
||||
import android.content.ClipData
|
||||
import android.content.ClipboardManager
|
||||
import android.content.Context
|
||||
import android.view.ViewGroup
|
||||
import com.tangem.devkit.R
|
||||
import com.tangem.devkit._arch.structure.abstraction.Item
|
||||
import com.tangem.devkit.extensions.copyToClipboard
|
||||
import com.tangem.devkit.ucase.variants.personalize.ui.widgets.DescriptionWidget
|
||||
import ru.dev.gbixahue.eu4d.lib.android._android.views.stringFrom
|
||||
import ru.dev.gbixahue.eu4d.lib.android._android.views.toast
|
||||
|
|
@ -19,12 +17,8 @@ abstract class ResponseWidget(parent: ViewGroup, item: Item) : DescriptionWidget
|
|||
init {
|
||||
view.setOnClickListener {
|
||||
val data = stringOf(item.getData<Any?>())
|
||||
val clipboard = view.context.getSystemService(Context.CLIPBOARD_SERVICE) as? ClipboardManager
|
||||
?: return@setOnClickListener
|
||||
|
||||
val clipboardData = "${getName()} - $data"
|
||||
val clip: ClipData = ClipData.newPlainText("FieldValue", clipboardData)
|
||||
clipboard.setPrimaryClip(clip)
|
||||
view.context.copyToClipboard(clipboardData, "FieldValue")
|
||||
|
||||
val copyMessage = view.stringFrom(R.string.copy_to_clipboard)
|
||||
view.toast("$copyMessage\n$clipboardData")
|
||||
|
|
|
|||
|
|
@ -0,0 +1,18 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/personalization_footer_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<include layout="@layout/v_export_import_json"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/btn_show_hide_fields"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="@dimen/def_indent"
|
||||
tools:text="Show/Hide fields" />
|
||||
|
||||
</LinearLayout>
|
||||
37
tangem-devkit/src/main/res/layout/v_export_import_json.xml
Normal file
37
tangem-devkit/src/main/res/layout/v_export_import_json.xml
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/exim_json_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/def_indent"
|
||||
android:layout_marginEnd="@dimen/def_indent"
|
||||
android:orientation="vertical"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible">
|
||||
|
||||
<EditText
|
||||
android:id="@+id/et_json_import"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
tools:text="@string/stub" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/btn_import_json"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Import json" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/btn_export_json"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Prepare json to export" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/et_json_export"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
tools:text="@string/stub" />
|
||||
|
||||
</LinearLayout>
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<Button
|
||||
android:id="@+id/button"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
|
@ -14,7 +14,6 @@ import com.tangem.common.CompletionResult
|
|||
import com.tangem.tangem_sdk_new.extensions.hide
|
||||
import com.tangem.tangem_sdk_new.extensions.show
|
||||
import com.tangem.tangem_sdk_new.nfc.NfcReader
|
||||
import com.tangem.tangem_sdk_new.ui.NfcEnableDialog
|
||||
import com.tangem.tangem_sdk_new.ui.TouchCardAnimation
|
||||
import kotlinx.android.synthetic.main.layout_touch_card.*
|
||||
import kotlinx.android.synthetic.main.nfc_bottom_sheet.*
|
||||
|
|
@ -35,7 +34,6 @@ class DefaultSessionViewDelegate(private val reader: NfcReader) : SessionViewDel
|
|||
override fun onNfcSessionStarted(cardId: String?, message: Message?) {
|
||||
reader.readingCancelled = false
|
||||
postUI { showReadingDialog(activity, cardId, message) }
|
||||
if (!reader.nfcEnabled) NfcEnableDialog().show(activity)
|
||||
}
|
||||
|
||||
private fun showReadingDialog(activity: Activity, cardId: String?, message: Message?) {
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import android.nfc.tech.IsoDep
|
|||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import com.tangem.Log
|
||||
import com.tangem.tangem_sdk_new.ui.NfcEnableDialog
|
||||
|
||||
/**
|
||||
* Helps use NFC, leveraging Android NFC functionality.
|
||||
|
|
@ -22,6 +23,7 @@ class NfcManager : NfcAdapter.ReaderCallback {
|
|||
val reader = NfcReader()
|
||||
private var activity: Activity? = null
|
||||
private var nfcAdapter: NfcAdapter? = null
|
||||
private var nfcEnableDialog: NfcEnableDialog? = null
|
||||
|
||||
fun setCurrentActivity(activity: Activity) {
|
||||
this.activity = activity
|
||||
|
|
@ -47,16 +49,21 @@ class NfcManager : NfcAdapter.ReaderCallback {
|
|||
fun onResume() {
|
||||
val filter = IntentFilter(NfcAdapter.ACTION_ADAPTER_STATE_CHANGED)
|
||||
activity?.registerReceiver(mBroadcastReceiver, filter)
|
||||
|
||||
if ((nfcAdapter == null || nfcAdapter?.isEnabled != true)) {
|
||||
reader.nfcEnabled = false
|
||||
} else {
|
||||
reader.nfcEnabled = true
|
||||
enableReaderMode()
|
||||
}
|
||||
handleNfcEnabled(nfcAdapter?.isEnabled == true)
|
||||
reader.manager = this
|
||||
}
|
||||
|
||||
private fun handleNfcEnabled(nfcEnabled: Boolean) {
|
||||
reader.nfcEnabled = nfcEnabled
|
||||
if (nfcEnabled) {
|
||||
enableReaderMode()
|
||||
nfcEnableDialog?.cancel()
|
||||
} else {
|
||||
nfcEnableDialog = NfcEnableDialog()
|
||||
activity?.let{ nfcEnableDialog?.show(it) }
|
||||
}
|
||||
}
|
||||
|
||||
fun onPause() {
|
||||
activity?.unregisterReceiver(mBroadcastReceiver)
|
||||
disableReaderMode()
|
||||
|
|
|
|||
|
|
@ -8,16 +8,22 @@ import com.tangem.tangem_sdk_new.R
|
|||
|
||||
class NfcEnableDialog {
|
||||
|
||||
private var dialog: AlertDialog? = null
|
||||
|
||||
fun show(activity: Activity) {
|
||||
val builder = AlertDialog.Builder(activity)
|
||||
builder.setCancelable(false)
|
||||
.setIcon(R.drawable.ic_action_nfc_gray)
|
||||
.setTitle(R.string.dialog_nfc_enable_title)
|
||||
.setMessage(R.string.dialog_nfc_enable_text)
|
||||
.setPositiveButton(R.string.general_ok
|
||||
) { _, _ ->
|
||||
activity.startActivity(Intent(Settings.ACTION_NFC_SETTINGS))
|
||||
}
|
||||
builder.create().show()
|
||||
.setPositiveButton(R.string.general_ok)
|
||||
{ _, _ -> activity.startActivity(Intent(Settings.ACTION_NFC_SETTINGS)) }
|
||||
.setNegativeButton(R.string.dialog_cancel) { dialog, _ -> dialog.cancel() }
|
||||
dialog = builder.create()
|
||||
dialog?.show()
|
||||
}
|
||||
|
||||
fun cancel() {
|
||||
dialog?.cancel()
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
<resources>
|
||||
<string name="app_name">Tangem Sdk</string>
|
||||
<string name="general_ok">OK</string>
|
||||
<string name="dialog_cancel">Cancel</string>
|
||||
<string name="dialog_nfc_enable_title">Enable NFC?</string>
|
||||
<string name="dialog_nfc_enable_text">This app is useless when NFC is disabled. Reader mode depends on it. Hit OK to go to Settings, where you can enable NFC.</string>
|
||||
<string name="dialog_security_delay">Security delay</string>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue