Updated on 2026-08-14

This commit is contained in:
Tangem 2020-04-06 17:10:40 +03:00
commit 769944959e
48 changed files with 24963 additions and 587 deletions

View file

@ -138,7 +138,7 @@ dependencies {
implementation 'com.fasterxml.jackson.core:jackson-databind:2.9.8'
//dependencies for binance
implementation 'com.google.protobuf:protobuf-java:3.6.1'
implementation 'com.google.protobuf:protobuf-java:3.11.4'
implementation 'io.grpc:grpc-protobuf:1.17.1'
implementation 'io.grpc:grpc-stub:1.17.1'
implementation 'commons-codec:commons-codec:1.10'
@ -154,4 +154,8 @@ dependencies {
implementation 'io.fotoapparat:fotoapparat:2.6.1'
implementation 'com.otaliastudios:cameraview:2.6.1'
//dependencies for flow demo
implementation 'io.grpc:grpc-okhttp:1.28.0'
implementation 'io.grpc:grpc-stub:1.28.0'
}

View file

@ -31,7 +31,8 @@ public enum Blockchain {
StellarTag("XLM-Tag", "XLM", 1000000.0, R.drawable.ic_logo_stellar, "Stellar"),
Eos("EOS", "EOS", 10000.0, R.drawable.tangem2, "EOS"),
Ducatus("DUC", "DUC", 100000000.0, R.drawable.tangem2, "Ducatus"),
Tezos("TEZOS", "XTZ", 10000000.0, R.drawable.ic_logo_tezos, "Tezos");
Tezos("TEZOS", "XTZ", 10000000.0, R.drawable.ic_logo_tezos, "Tezos"),
FlowDemo("FLOW/demo", "", 1.0, R.drawable.tangem2, "Flow demo");
Blockchain(String ID, String currency, double multiplier, int imageResource, String officialName) {
mID = ID;

View file

@ -58,11 +58,17 @@ abstract class BaseFragment : Fragment() {
Log.d("LIFECYCLE", "onDestroy: ${this::class.java.simpleName}")
}
protected fun navigateUp(@IdRes destination: Int = DESTINATION_NOT_SET): Boolean {
return if (destination == DESTINATION_NOT_SET) {
findNavController(this).popBackStack()
} else {
findNavController(this).popBackStack(destination, false)
protected fun navigateUp(@IdRes destination: Int = DESTINATION_NOT_SET) {
try {
if (destination == DESTINATION_NOT_SET) {
findNavController(this).popBackStack()
} else {
findNavController(this).popBackStack(destination, false)
}
} catch (e: IllegalArgumentException) {
Log.w(this::class.java.simpleName, e.message)
} catch (e: IllegalStateException) {
Log.w(this::class.java.simpleName, e.message)
}
}
@ -84,7 +90,7 @@ abstract class BaseFragment : Fragment() {
}
protected fun navigateBackWithResult(resultCode: Int, data: Bundle? = null,
@IdRes destination: Int = DESTINATION_NOT_SET): Boolean {
@IdRes destination: Int = DESTINATION_NOT_SET) {
(requireActivity() as MainActivity).viewModel.navigationResult =
NavigationResult(requestCode, resultCode, data)
return navigateUp(destination)

View file

@ -9,7 +9,6 @@ import android.nfc.Tag
import android.nfc.tech.IsoDep
import android.os.Bundle
import android.view.View
import android.widget.Toast
import androidx.activity.OnBackPressedCallback
import com.google.firebase.crashlytics.FirebaseCrashlytics
import com.tangem.App
@ -89,11 +88,6 @@ class ValidateIdFragment : BaseFragment(), NavigationResultListener,
super.onPause()
}
override fun onStop() {
signTransactionTask?.cancel(true)
super.onStop()
}
override fun onNavigationResult(requestCode: String, resultCode: Int, data: Bundle?) {
if (requestCode == Constant.REQUEST_CODE_SEND_TRANSACTION_) {
navigateBackWithResult(resultCode, data)
@ -262,7 +256,10 @@ class ValidateIdFragment : BaseFragment(), NavigationResultListener,
NoExtendedLengthSupportDialog().show(requireFragmentManager(), NoExtendedLengthSupportDialog.TAG)
}
} else {
Toast.makeText(context, R.string.general_notification_scan_again, Toast.LENGTH_LONG).show()
(activity as MainActivity).toastHelper.showSingleToast(
context,
getString(R.string.general_notification_scan_again)
)
}
progressBar?.progress = 100
progressBar?.progressTintList = ColorStateList.valueOf(Color.RED)

View file

@ -9,7 +9,6 @@ import android.nfc.Tag
import android.nfc.tech.IsoDep
import android.os.Bundle
import android.view.View
import android.widget.Toast
import androidx.activity.OnBackPressedCallback
import com.google.firebase.crashlytics.FirebaseCrashlytics
import com.tangem.App
@ -49,7 +48,6 @@ class WriteIdFragment : BaseFragment(), NavigationResultListener,
private lateinit var ctx: TangemContext
private lateinit var tx: ByteArray
private lateinit var mpFinishSignSound: MediaPlayer
private var toast: Toast? = null
private var idWasWritten = false
@ -94,7 +92,6 @@ class WriteIdFragment : BaseFragment(), NavigationResultListener,
override fun onStop() {
writeIDCardTask?.cancel(true)
toast?.cancel()
super.onStop()
}
@ -174,7 +171,6 @@ class WriteIdFragment : BaseFragment(), NavigationResultListener,
if (success) {
navigateUp(R.id.main)
} else {
toast?.cancel()
navigateUp(R.id.issueNewIdFragment)
}
}
@ -231,8 +227,10 @@ class WriteIdFragment : BaseFragment(), NavigationResultListener,
}
} else {
if (!idWasWritten) {
toast = Toast.makeText(context, R.string.general_notification_scan_again, Toast.LENGTH_SHORT)
toast?.show()
(activity as MainActivity).toastHelper.showSingleToast(
context,
getString(R.string.general_notification_scan_again)
)
}
}
progressBar?.progress = 100

View file

@ -12,6 +12,7 @@ import com.tangem.wallet.ducatus.DucatusEngine
import com.tangem.wallet.eos.EosEngine
import com.tangem.wallet.eth.EthEngine
import com.tangem.wallet.ethID.EthIdEngine
import com.tangem.wallet.flowDemo.FlowDemoEngine
import com.tangem.wallet.ltc.LtcEngine
import com.tangem.wallet.matic.MaticTokenEngine
import com.tangem.wallet.nftToken.NftTokenEngine
@ -58,6 +59,7 @@ object CoinEngineFactory {
Blockchain.Ducatus -> DucatusEngine()
Blockchain.Tezos -> TezosEngine()
Blockchain.BitcoinDual -> BtcMultisigEngine()
Blockchain.FlowDemo -> FlowDemoEngine()
else -> null
}
}
@ -105,6 +107,8 @@ object CoinEngineFactory {
TezosEngine(context)
else if (Blockchain.BitcoinDual == context.blockchain)
BtcMultisigEngine(context)
else if (Blockchain.FlowDemo == context.blockchain)
FlowDemoEngine(context)
else
return null
} catch (e: Exception) {

View file

@ -0,0 +1,29 @@
package com.tangem.wallet.flowDemo
import android.os.Bundle
import android.util.Log
import com.tangem.wallet.CoinData
import java.lang.Exception
class FlowDemoData : CoinData() {
var description: String? = null
override fun clearInfo() {
super.clearInfo()
description = null
}
override fun loadFromBundle(B: Bundle) {
super.loadFromBundle(B)
if (B.containsKey("Description")) description = B.getString("Description")
}
override fun saveToBundle(B: Bundle) {
super.saveToBundle(B)
try {
if (description != null) B.putString("Description", description!!)
} catch (e: Exception) {
Log.e("Can't save to bundle ", e.message)
}
}
}

View file

@ -0,0 +1,250 @@
package com.tangem.wallet.flowDemo
import android.net.Uri
import android.text.InputFilter
import com.google.protobuf.ByteString
import com.tangem.tangem_card.reader.CardProtocol.TangemException
import com.tangem.tangem_card.tasks.SignTask
import com.tangem.tangem_card.util.Util
import com.tangem.wallet.*
import com.tangem.wallet.binance.client.domain.Account
import com.tangem.wallet.flowDemo.proto.Observation
import com.tangem.wallet.flowDemo.proto.ObserveServiceGrpc
import io.grpc.ManagedChannelBuilder
import io.reactivex.Observable
import io.reactivex.Single
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.functions.Function
import io.reactivex.observers.DisposableSingleObserver
import io.reactivex.schedulers.Schedulers
import java.util.concurrent.TimeUnit
class FlowDemoEngine : CoinEngine {
constructor()
constructor(context: TangemContext) : super(context) {
if (context.coinData == null) {
coinData = FlowDemoData()
context.coinData = coinData
} else if (context.coinData is FlowDemoData) {
coinData = context.coinData as FlowDemoData
} else {
throw Exception("Invalid type of Blockchain data for XlmEngine")
}
}
private val TAG = FlowDemoEngine::class.java.simpleName
var coinData: FlowDemoData? = null
override fun checkNewTransactionAmount(amount: Amount?): Boolean {
return true
}
override fun requestBalanceAndUnspentTransactions(blockchainRequestsCallbacks: BlockchainRequestsCallbacks?) {
val channel = ManagedChannelBuilder.forAddress("ec2-3-134-137-165.us-east-2.compute.amazonaws.com", 3569).usePlaintext().build();
val flowApi = ObserveServiceGrpc.newBlockingStub(channel)
val script = ByteString.copyFrom(
"""import TangemTest from 0x02
pub fun main(): String? {
let contract = getAccount(0x02)
let collectionRef = contract.published[&TangemTest.TagCollection] ?? panic("missing reference!")
let desc = collectionRef.tags["%s"]?.desc
return desc
}""".format(coinData!!.wallet).toByteArray()
)
val scriptRequest = Observation.ExecuteScriptRequest.newBuilder().setScript(script).build()
val scriptResponseObserver = object : DisposableSingleObserver<Observation.ExecuteScriptResponse>() {
override fun onSuccess(response: Observation.ExecuteScriptResponse) {
coinData!!.isBalanceReceived = true
val byteResult = response.value.toByteArray()
if (byteResult.size > 8) {
coinData!!.description = String(byteResult.copyOfRange(8, byteResult.size)).toUpperCase()
}
blockchainRequestsCallbacks!!.onComplete(true)
}
override fun onError(e: Throwable) {
ctx.error = e.message
blockchainRequestsCallbacks!!.onComplete(false)
}
}
try {
Single.just(Observation.ExecuteScriptResponse.getDefaultInstance())
.map { flowApi.executeScript(scriptRequest) }
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.timeout(10, TimeUnit.SECONDS)
.subscribe(scriptResponseObserver)
} catch (e: Exception) {
ctx.error = e.message
blockchainRequestsCallbacks!!.onComplete(false)
}
}
override fun getBalance(): Amount {
return Amount()
}
override fun isNeedCheckNode(): Boolean {
return false
}
override fun getAmountInputFilters(): Array<InputFilter> {
TODO("Not yet implemented")
}
override fun getWalletExplorerUri(): Uri {
return Uri.parse("https://www.onflow.org/")
}
override fun getBalanceHTML(): String {
val balanceString = if(hasBalanceInfo()) {
if(coinData!!.description != null) {
coinData!!.description!! + " TANGEM CARD"
} else {
"UNKNOWN CARD"
}
} else {
""
}
if (coinData!!.description == "COMMON") {
balanceString == "$balanceString<br><small><small>your usual tangem card</small></small>"
} else if (coinData!!.description == "RARE") {
balanceString == "$balanceString<br><small><small>wow, this one is RARE</small></small>"
}
return balanceString
}
override fun awaitingConfirmation(): Boolean {
return false
}
override fun getFeeCurrency(): String {
return ""
}
override fun convertToByteArray(internalAmount: InternalAmount?): ByteArray {
TODO("Not yet implemented")
}
override fun requestSendTransaction(blockchainRequestsCallbacks: BlockchainRequestsCallbacks?, txForSend: ByteArray?) {
TODO("Not yet implemented")
}
override fun hasBalanceInfo(): Boolean {
return coinData!!.isBalanceReceived
}
override fun getBalanceEquivalent(): String {
return ""
}
// public String getOfflineBalanceHTML() {
// InternalAmount offlineInternalAmount = convertToInternalAmount(ctx.getCard().getOfflineBalance());
// Amount offlineAmount = convertToAmount(offlineInternalAmount);
// return offlineAmount.toDescriptionString(getDecimals());
// }
@Throws(TangemException::class)
override fun defineWallet() {
try {
val wallet = calculateAddress(ctx.card.walletPublicKeyRar)
ctx.coinData.wallet = wallet
} catch (e: java.lang.Exception) {
ctx.coinData.wallet = "ERROR"
throw TangemException("Can't define wallet address")
}
}
override fun calculateAddress(pkUncompressed: ByteArray?): String {
return Util.byteArrayToHexString(pkUncompressed)
}
override fun getShareWalletUri(): Uri {
return Uri.parse(ctx.coinData.wallet)
}
override fun getUnspentInputsDescription(): String {
return ""
}
override fun checkNewTransactionAmountAndFee(amount: Amount?, fee: Amount?, isFeeIncluded: Boolean?): Boolean {
TODO("Not yet implemented")
}
override fun isBalanceNotZero(): Boolean {
return true
}
override fun getBalanceCurrency(): String {
return ""
}
override fun evaluateFeeEquivalent(fee: String?): String {
return ""
}
override fun convertToAmount(internalAmount: InternalAmount?): Amount {
TODO("Not yet implemented")
}
override fun convertToAmount(strAmount: String?, currency: String?): Amount {
TODO("Not yet implemented")
}
override fun isExtractPossible(): Boolean {
return false
}
override fun convertToInternalAmount(amount: Amount?): InternalAmount {
TODO("Not yet implemented")
}
override fun convertToInternalAmount(bytes: ByteArray?): InternalAmount {
TODO("Not yet implemented")
}
override fun requestFee(blockchainRequestsCallbacks: BlockchainRequestsCallbacks?, targetAddress: String?, amount: Amount?) {
TODO("Not yet implemented")
}
override fun validateAddress(address: String?): Boolean {
TODO("Not yet implemented")
}
override fun constructTransaction(amountValue: Amount?, feeValue: Amount?, IncFee: Boolean, targetAddress: String?): SignTask.TransactionToSign {
TODO("Not yet implemented")
}
override fun validateBalance(balanceValidator: BalanceValidator?): Boolean {
if (hasBalanceInfo()) {
if (coinData!!.description != null) {
balanceValidator!!.setScore(100)
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)
}
} else {
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
}
return true
}
override fun createCoinData(): CoinData {
return FlowDemoData()
}
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -228,6 +228,12 @@ class LocalStorage
fun getCardArtworkBitmap(card: TangemCard): Bitmap {
// special cases (first series of cards, hardcode CID->artwork), on new series batch<->artwork
if (card.isIDIssuer) {
val artworkId = context.resources.getResourceEntryName(R.drawable.card_id_issuer)
return getArtworkBitmap(artworkId) ?: return getDefaultArtworkBitmap(card)
}
val hexCID = Util.bytesToHex(card.cid)
val artworkResourceId: Int? = when {
hexCID in "AA01000000000000".."AA01000000004999" -> R.drawable.card_ru006

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

View file

@ -634,6 +634,15 @@ public class TangemCard {
}
}
public boolean isIDIssuer() {
try {
return (productMask & ProductMask.IdIssuer) != 0;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
public TLVList tlvIDCardData = null;
public void setTlvIdCardData(TLVList tlvIDCardData) {

View file

@ -8,6 +8,7 @@ public class ProductMask {
public static final int Note = 0x0001;
public static final int Tag = 0x0002;
public static final int IdCard = 0x0004;
public static final int IdIssuer = 0x0008;
public static String getDescription(int iValue) {
StringBuilder sb = new StringBuilder();
@ -17,6 +18,8 @@ public class ProductMask {
sb.append("Tag, ");
if ((iValue & ProductMask.IdCard) != 0)
sb.append("IdCard, ");
if ((iValue & ProductMask.IdIssuer) != 0)
sb.append("IdIssuer, ");
if (sb.length() > 1) sb.delete(sb.length() - 2, sb.length());
sb.append("]");

View file

@ -119,6 +119,7 @@ data class ProductMask(val rawValue: Int) {
const val note = 0x01
const val tag = 0x02
const val idCard = 0x04
const val idIssuer = 0x08
}
}

View file

@ -0,0 +1,26 @@
package com.tangem.tangemtest._arch.structure.abstraction
import com.tangem.tangemtest._arch.structure.Id
/**
[REDACTED_AUTHOR]
*/
fun List<Item>.findDataItem(id: Id): BaseItem<Any?>? {
var foundItem: BaseItem<Any?>? = null
iterate {
if (it.id == id) {
foundItem = it as? BaseItem<Any?>
return@iterate
}
}
return foundItem
}
fun List<Item>.iterate(func: (Item) -> Unit) {
forEach {
when (it) {
is BaseItem<*> -> func(it)
is Block -> it.itemList.iterate(func)
}
}
}

View file

@ -0,0 +1,15 @@
package com.tangem.tangemtest._arch.structure.abstraction
import ru.dev.gbixahue.eu4d.lib.kotlin.common.Converter
/**
[REDACTED_AUTHOR]
*/
interface DefaultConverter<A, B> {
fun convert(from: A, default: B): B
}
interface ItemsToModel<M> : DefaultConverter<List<Item>, M>
interface ModelToItems<M> : Converter<M, List<Item>>
interface ModelConverter<M> : DefaultConverter<List<Item>, M>, Converter<M, List<Item>>

View file

@ -61,6 +61,8 @@ open class BaseItemViewModel<D> : ItemViewModel<D> {
}
class ViewState {
var isHiddenField = false
var descriptionVisibility: Int = 0x00000008
set(value) {
field = value

View file

@ -2,8 +2,8 @@ package com.tangem.tangemtest.ucase.domain.actions
import com.tangem.tangemtest._arch.structure.Id
import com.tangem.tangemtest._arch.structure.PayloadHolder
import com.tangem.tangemtest._arch.structure.abstraction.findDataItem
import com.tangem.tangemtest.ucase.domain.paramsManager.ActionCallback
import com.tangem.tangemtest.ucase.domain.paramsManager.findDataItem
import com.tangem.tangemtest.ucase.variants.TlvId
/**

View file

@ -1,25 +1,65 @@
package com.tangem.tangemtest.ucase.domain.actions
import com.tangem.commands.personalization.entities.CardConfig
import com.tangem.tangemtest._arch.structure.PayloadHolder
import com.tangem.tangemtest._arch.structure.abstraction.Item
import com.tangem.tangemtest._arch.structure.abstraction.findDataItem
import com.tangem.tangemtest._arch.structure.impl.EditTextItem
import com.tangem.tangemtest._arch.structure.impl.NumberItem
import com.tangem.tangemtest.ucase.domain.paramsManager.ActionCallback
import com.tangem.tangemtest.ucase.domain.paramsManager.PayloadKey
import com.tangem.tangemtest.ucase.tunnel.ActionView
import com.tangem.tangemtest.ucase.tunnel.ItemError
import com.tangem.tangemtest.ucase.variants.personalize.CardNumber
import com.tangem.tangemtest.ucase.variants.personalize.converter.PersonalizeConfigConverter
import com.tangem.tangemtest.ucase.variants.personalize.converter.fromTo.PersonalizeConfigToCardConfig
import com.tangem.tangemtest.ucase.variants.personalize.dto.DefaultPersonalizeParams
import com.tangem.tangemtest.ucase.variants.personalize.dto.PersonalizeConfig
import ru.dev.gbixahue.eu4d.lib.kotlin.stringOf
/**
[REDACTED_AUTHOR]
*/
class PersonalizeAction : BaseAction() {
override fun executeMainAction(payload: PayloadHolder, attrs: AttrForAction, callback: ActionCallback) {
val cardConfig = attrs.payload.remove(PayloadKey.cardConfig) as? CardConfig
?: throw IllegalArgumentException("CardConfig must be in the payloads of the ParamsManager")
val itemList = attrs.payload[PayloadKey.itemList] as? List<Item> ?: return
val actionView = attrs.payload[PayloadKey.actionView] as? ActionView ?: return
if (!checkSeries(itemList)) {
actionView.showSnackbar(ItemError.BadSeries)
return
}
if (!checkNumber(itemList)) {
actionView.showSnackbar(ItemError.BadCardNumber)
return
}
val issuer = DefaultPersonalizeParams.issuer()
val acquirer = DefaultPersonalizeParams.acquirer()
val manufacturer = DefaultPersonalizeParams.manufacturer()
val personalizeConfig = PersonalizeConfigConverter().convert(itemList, PersonalizeConfig())
val cardConfig = PersonalizeConfigToCardConfig().convert(personalizeConfig)
attrs.cardManager.personalize(cardConfig, issuer, manufacturer, acquirer) {
handleResult(payload, it, null, attrs, callback)
}
}
private fun checkSeries(itemList: List<Item>): Boolean {
val seriesItem = itemList.findDataItem(CardNumber.Series) as? EditTextItem ?: return false
val data = seriesItem.getData() ?: return false
seriesItem.setData(data.toUpperCase())
return data.length == 2 || data.length == 4
}
private fun checkNumber(itemList: List<Item>): Boolean {
val seriesItem = itemList.findDataItem(CardNumber.Series) as? EditTextItem ?: return false
val numberItem = itemList.findDataItem(CardNumber.Number) as? NumberItem ?: return false
val seriesData = seriesItem.getData() ?: return false
val numberData = stringOf(numberItem.getData())
return if (seriesData.length == 2 && numberData.length > 13) false
else !(seriesData.length == 4 && numberData.length > 11)
}
}

View file

@ -2,8 +2,8 @@ package com.tangem.tangemtest.ucase.domain.actions
import com.tangem.tangemtest._arch.structure.Id
import com.tangem.tangemtest._arch.structure.PayloadHolder
import com.tangem.tangemtest._arch.structure.abstraction.findDataItem
import com.tangem.tangemtest.ucase.domain.paramsManager.ActionCallback
import com.tangem.tangemtest.ucase.domain.paramsManager.findDataItem
import com.tangem.tangemtest.ucase.variants.TlvId
import ru.dev.gbixahue.eu4d.lib.kotlin.stringOf

View file

@ -4,7 +4,6 @@ import com.tangem.CardManager
import com.tangem.tangemtest._arch.structure.Id
import com.tangem.tangemtest._arch.structure.Payload
import com.tangem.tangemtest._arch.structure.PayloadHolder
import com.tangem.tangemtest._arch.structure.abstraction.BaseItem
import com.tangem.tangemtest._arch.structure.abstraction.Item
import com.tangem.tangemtest.ucase.domain.paramsManager.triggers.changeConsequence.ItemsChangeConsequence
import com.tangem.tasks.TaskEvent
@ -34,15 +33,6 @@ interface PayloadKey {
val incomingJson: String = "incomingJson"
val card = "card"
val actionView = "actionView"
val itemList = "itemList"
}
}
fun List<Item>.findItem(id: Id): Item? {
return firstOrNull { it.id == id }
}
fun List<Item>.findDataItem(id: Id): BaseItem<Any?>? {
val item = firstOrNull { it.id == id }
return if (item is BaseItem<*>) item as BaseItem<Any?>
else null
}

View file

@ -4,12 +4,12 @@ import com.tangem.CardManager
import com.tangem.tangemtest._arch.structure.Id
import com.tangem.tangemtest._arch.structure.Payload
import com.tangem.tangemtest._arch.structure.abstraction.Item
import com.tangem.tangemtest._arch.structure.abstraction.findDataItem
import com.tangem.tangemtest.ucase.domain.actions.Action
import com.tangem.tangemtest.ucase.domain.actions.AttrForAction
import com.tangem.tangemtest.ucase.domain.paramsManager.ActionCallback
import com.tangem.tangemtest.ucase.domain.paramsManager.AffectedItemsCallback
import com.tangem.tangemtest.ucase.domain.paramsManager.ItemsManager
import com.tangem.tangemtest.ucase.domain.paramsManager.findDataItem
import com.tangem.tangemtest.ucase.domain.paramsManager.triggers.changeConsequence.ItemsChangeConsequence
/**

View file

@ -2,18 +2,14 @@ package com.tangem.tangemtest.ucase.domain.paramsManager.managers
import com.tangem.CardManager
import com.tangem.tangemtest._arch.structure.abstraction.Item
import com.tangem.tangemtest._arch.structure.impl.EditTextItem
import com.tangem.tangemtest.ucase.domain.actions.PersonalizeAction
import com.tangem.tangemtest.ucase.domain.paramsManager.ActionCallback
import com.tangem.tangemtest.ucase.variants.TlvId
/**
[REDACTED_AUTHOR]
*/
class PersonalizeItemsManager : BaseItemsManager(PersonalizeAction()) {
override fun createItemsList(): List<Item> {
return listOf(EditTextItem(TlvId.CardId))
}
override fun createItemsList(): List<Item> = listOf()
override fun invokeMainAction(cardManager: CardManager, callback: ActionCallback) {
action.executeMainAction(this, getAttrsForAction(cardManager), callback)

View file

@ -4,8 +4,8 @@ import com.tangem.commands.Card
import com.tangem.commands.CardStatus
import com.tangem.tangemtest._arch.structure.PayloadHolder
import com.tangem.tangemtest._arch.structure.abstraction.Item
import com.tangem.tangemtest._arch.structure.abstraction.findDataItem
import com.tangem.tangemtest.ucase.domain.paramsManager.PayloadKey
import com.tangem.tangemtest.ucase.domain.paramsManager.findDataItem
import com.tangem.tangemtest.ucase.tunnel.ActionView
import com.tangem.tangemtest.ucase.tunnel.CardError
import com.tangem.tangemtest.ucase.variants.TlvId

View file

@ -6,8 +6,8 @@ import com.tangem.common.extensions.calculateSha256
import com.tangem.common.extensions.calculateSha512
import com.tangem.tangemtest._arch.structure.PayloadHolder
import com.tangem.tangemtest._arch.structure.abstraction.Item
import com.tangem.tangemtest._arch.structure.abstraction.findDataItem
import com.tangem.tangemtest.ucase.domain.paramsManager.PayloadKey
import com.tangem.tangemtest.ucase.domain.paramsManager.findDataItem
import com.tangem.tangemtest.ucase.tunnel.ActionView
import com.tangem.tangemtest.ucase.variants.TlvId
import ru.dev.gbixahue.eu4d.lib.android.global.threading.postUI

View file

@ -7,6 +7,8 @@ import com.tangem.commands.SettingsMask
import com.tangem.commands.SigningMethod
import com.tangem.common.extensions.toHexString
import java.lang.reflect.Type
import java.text.DateFormat
import java.util.*
/**
[REDACTED_AUTHOR]
@ -20,6 +22,7 @@ class GsonInitializer {
registerTypeAdapter(SigningMethod::class.java, SigningMethodTypeAdapter())
registerTypeAdapter(SettingsMask::class.java, SettingsMaskTypeAdapter())
registerTypeAdapter(ProductMask::class.java, ProductMaskTypeAdapter())
registerTypeAdapter(Date::class.java, DateTypeAdapter())
}
builder.setPrettyPrinting()
return builder.create()
@ -52,4 +55,11 @@ class SigningMethodTypeAdapter : JsonSerializer<SigningMethod> {
override fun serialize(src: SigningMethod, typeOfSrc: Type, context: JsonSerializationContext): JsonElement {
return JsonPrimitive(src.rawValue.toString())
}
}
class DateTypeAdapter: JsonSerializer<Date> {
override fun serialize(src: Date, typeOfSrc: Type, context: JsonSerializationContext): JsonElement {
val formatter = DateFormat.getDateInstance(DateFormat.DEFAULT, Locale("en_US"))
return JsonPrimitive(formatter.format(src).toString())
}
}

View file

@ -43,6 +43,7 @@ class PersonalizeResources() {
private fun initCardNumber(holder: TypedHolder<Id, Resources>) {
holder.register(CardNumber.Series, Resources(R.string.pers_item_series, R.string.info_pers_item_series))
holder.register(CardNumber.Number, Resources(R.string.pers_item_number, R.string.info_pers_item_number))
holder.register(CardNumber.BatchId, Resources(R.string.pers_item_batch_id, R.string.info_pers_item_batch_id))
}
private fun initCommon(holder: TypedHolder<Id, Resources>) {
@ -86,7 +87,8 @@ class PersonalizeResources() {
private fun initProductMask(holder: TypedHolder<Id, Resources>) {
holder.register(ProductMask.Note, Resources(R.string.pers_item_note, R.string.info_pers_item_note))
holder.register(ProductMask.Tag, Resources(R.string.pers_item_tag, R.string.info_pers_item_tag))
holder.register(ProductMask.CardId, Resources(R.string.pers_item_id_card, R.string.info_pers_item_id_card))
holder.register(ProductMask.IdCard, Resources(R.string.pers_item_id_card, R.string.info_pers_item_id_card))
holder.register(ProductMask.IdIssuerCard, Resources(R.string.pers_item_id_issuer_card, R.string.info_pers_item_id_issuer_card))
}
private fun initSettingsMask(holder: TypedHolder<Id, Resources>) {
@ -110,7 +112,7 @@ class PersonalizeResources() {
private fun initSettingsMaskProtocolEnc(holder: TypedHolder<Id, Resources>) {
holder.register(SettingsMaskProtocolEnc.AllowUnencrypted, Resources(R.string.pers_item_allow_unencrypted, R.string.info_pers_item_allow_unencrypted))
holder.register(SettingsMaskProtocolEnc.AlloFastEncryption, Resources(R.string.pers_item_allow_fast_encryption, R.string.info_pers_item_allow_fast_encryption))
holder.register(SettingsMaskProtocolEnc.AllowStaticEncryption, Resources(R.string.pers_item_allow_fast_encryption, R.string.info_pers_item_allow_fast_encryption))
}
private fun initSettingsMaskNde(holder: TypedHolder<Id, Resources>) {

View file

@ -5,6 +5,13 @@ import com.tangem.tangemtest._arch.structure.Id
/**
[REDACTED_AUTHOR]
*/
enum class CardError : Id {
NotPersonalized
interface Errors: Id
enum class CardError : Errors {
NotPersonalized,
}
enum class ItemError: Errors {
BadSeries,
BadCardNumber,
}

View file

@ -6,6 +6,7 @@ interface ViewScreen
interface SnackbarHolder {
fun showSnackbar(message: String)
fun showSnackbar(id: Int)
fun showSnackbar(id: Id)
}

View file

@ -133,11 +133,15 @@ abstract class BaseCardActionFragment : Fragment(), LayoutHolder, ActionView {
override fun showSnackbar(id: Id) {
// MainResourceHolder.safeGet<>()
when (id) {
CardError.NotPersonalized -> showSnackbar(requireContext().getString(R.string.card_error_not_personalized))
CardError.NotPersonalized -> showSnackbar(R.string.card_error_not_personalized)
else -> showSnackbar(requireContext().getString(R.string.unknown))
}
}
override fun showSnackbar(id: Int) {
showSnackbar(requireContext().getString(id))
}
override fun showSnackbar(message: String) {
Snackbar.make(mainView, message, BaseTransientBottomBar.LENGTH_SHORT).show()
}

View file

@ -27,6 +27,7 @@ enum class BlockId : PersonalizeId {
enum class CardNumber : BlockItem {
Series,
Number,
BatchId,
}
enum class Common : BlockItem {
@ -70,7 +71,8 @@ enum class Token : BlockItem {
enum class ProductMask : BlockItem {
Note,
Tag,
CardId
IdCard,
IdIssuerCard
}
enum class SettingsMask : BlockItem {
@ -94,7 +96,7 @@ enum class SettingsMask : BlockItem {
enum class SettingsMaskProtocolEnc : BlockItem {
AllowUnencrypted,
AlloFastEncryption
AllowStaticEncryption
}
enum class SettingsMaskNdef : BlockItem {

View file

@ -0,0 +1,136 @@
package com.tangem.tangemtest.ucase.variants.personalize.converter
import com.tangem.commands.EllipticCurve
import com.tangem.tangemtest._arch.structure.Id
import com.tangem.tangemtest._arch.structure.impl.KeyValue
import com.tangem.tangemtest.ucase.variants.personalize.*
import com.tangem.tangemtest.ucase.variants.personalize.dto.PersonalizeConfig
import ru.dev.gbixahue.eu4d.lib.kotlin.common.BaseTypedHolder
/**
[REDACTED_AUTHOR]
*/
class PersonalizeConfigValuesHolder : BaseTypedHolder<Id, Value>() {
// All registered association values can't be objects
fun init(default: PersonalizeConfig) {
register(CardNumber.Series, Value(default.series))
register(CardNumber.Number, Value(default.startNumber))
register(CardNumber.BatchId, Value(default.batchId))
register(Common.Curve, Value(default.curveID, Helper.listOfCurves()))
register(Common.Blockchain, Value(default.blockchain, Helper.listOfBlockchain()))
register(Common.BlockchainCustom, Value(""))
register(Common.MaxSignatures, Value(default.MaxSignatures))
register(Common.CreateWallet, Value(default.createWallet))
register(SigningMethod.SignTx, Value(default.SigningMethod0))
register(SigningMethod.SignTxRaw, Value(default.SigningMethod1))
register(SigningMethod.SignValidatedTx, Value(default.SigningMethod2))
register(SigningMethod.SignValidatedTxRaw, Value(default.SigningMethod3))
register(SigningMethod.SignValidatedTxIssuer, Value(default.SigningMethod4))
register(SigningMethod.SignValidatedTxRawIssuer, Value(default.SigningMethod5))
register(SigningMethod.SignExternal, Value(default.SigningMethod6))
register(SignHashExProp.PinLessFloorLimit, Value(default.pinLessFloorLimit))
register(SignHashExProp.CryptoExKey, Value(default.hexCrExKey))
register(SignHashExProp.RequireTerminalCertSig, Value(default.requireTerminalCertSignature))
register(SignHashExProp.RequireTerminalTxSig, Value(default.requireTerminalTxSignature))
register(SignHashExProp.CheckPin3, Value(default.checkPIN3onCard))
register(Denomination.WriteOnPersonalize, Value(default.writeOnPersonalization))
register(Denomination.Denomination, Value(default.denomination))
register(Token.ItsToken, Value(default.itsToken))
register(Token.Symbol, Value(default.symbol))
register(Token.ContractAddress, Value(default.contractAddress))
register(Token.Decimal, Value(default.decimal))
register(ProductMask.Note, Value(default.cardData.product_note))
register(ProductMask.Tag, Value(default.cardData.product_tag))
register(ProductMask.IdCard, Value(default.cardData.product_id_card))
register(ProductMask.IdIssuerCard, Value(default.cardData.product_id_issuer))
register(SettingsMask.IsReusable, Value(default.isReusable))
register(SettingsMask.NeedActivation, Value(default.useActivation))
register(SettingsMask.ForbidPurge, Value(default.forbidPurgeWallet))
register(SettingsMask.AllowSelectBlockchain, Value(default.allowSelectBlockchain))
register(SettingsMask.UseBlock, Value(default.useBlock))
register(SettingsMask.OneApdu, Value(default.oneApdu))
register(SettingsMask.UseCvc, Value(default.useCVC))
register(SettingsMask.AllowSwapPin, Value(default.allowSwapPIN))
register(SettingsMask.AllowSwapPin2, Value(default.allowSwapPIN2))
register(SettingsMask.ForbidDefaultPin, Value(default.forbidDefaultPIN))
register(SettingsMask.SmartSecurityDelay, Value(default.smartSecurityDelay))
register(SettingsMask.ProtectIssuerDataAgainstReplay, Value(default.protectIssuerDataAgainstReplay))
register(SettingsMask.SkipSecurityDelayIfValidated, Value(default.skipSecurityDelayIfValidatedByIssuer))
register(SettingsMask.SkipPin2CvcIfValidated, Value(default.skipCheckPIN2andCVCIfValidatedByIssuer))
register(SettingsMask.SkipSecurityDelayOnLinkedTerminal, Value(default.skipSecurityDelayIfValidatedByLinkedTerminal))
register(SettingsMask.RestrictOverwriteExtraIssuerData, Value(default.restrictOverwriteIssuerDataEx))
register(SettingsMaskProtocolEnc.AllowUnencrypted, Value(default.protocolAllowUnencrypted))
register(SettingsMaskProtocolEnc.AllowStaticEncryption, Value(default.protocolAllowStaticEncryption))
register(SettingsMaskNdef.UseNdef, Value(default.useNDEF))
register(SettingsMaskNdef.DynamicNdef, Value(default.useDynamicNDEF))
register(SettingsMaskNdef.DisablePrecomputedNdef, Value(default.disablePrecomputedNDEF))
register(SettingsMaskNdef.Aar, Value(default.aar, Helper.aarList()))
register(SettingsMaskNdef.AarCustom, Value(default.aar))
register(Pins.Pin, Value(default.PIN))
register(Pins.Pin2, Value(default.PIN2))
register(Pins.Pin3, Value(default.PIN3))
register(Pins.Cvc, Value(default.CVC))
register(Pins.PauseBeforePin2, Value(default.pauseBeforePIN2, Helper.pauseBeforePin()))
}
}
class Value(
private var value: Any? = null,
val list: List<KeyValue>? = mutableListOf()
) {
fun get(): Any? = value
fun set(newValue: Any?) {
value = newValue
}
}
internal class Helper {
companion object {
fun listOfCurves(): List<KeyValue> {
return EllipticCurve.values().map { KeyValue(it.name, it.curve) }
}
fun listOfBlockchain(): List<KeyValue> {
return mutableListOf(
KeyValue("--- CUSTOM ---", ""),
KeyValue("BTC", "BTC"),
KeyValue("BTC/test", "BTC/test"),
KeyValue("ETH", "ETH"),
KeyValue("ETH/test", "ETH/test"),
KeyValue("BCH", "BCH"),
KeyValue("BCH/test", "BCH/test"),
KeyValue("LTC", "LTC"),
KeyValue("XLM", "XLM"),
KeyValue("XLM/test", "XLM/test"),
KeyValue("RSK", "RSK"),
KeyValue("XPR", "XPR"),
KeyValue("CARDANO", "CARDANO"),
KeyValue("BNB", "BNB"),
KeyValue("XTZ", "XTZ"),
KeyValue("DUC", "DUC")
)
}
fun aarList(): List<KeyValue> {
return mutableListOf(
KeyValue("--- CUSTOM ---", ""),
KeyValue("Release APP", "com.tangem.wallet"),
KeyValue("Debug APP", "com.tangem.wallet.debug"),
KeyValue("None", "")
)
}
fun pauseBeforePin(): List<KeyValue> {
return mutableListOf(
KeyValue("immediately", 0L),
KeyValue("2 seconds", 2000L),
KeyValue("5 seconds", 5000L),
KeyValue("15 seconds", 15000L),
KeyValue("30 seconds", 30000L),
KeyValue("1 minute", 60000L),
KeyValue("2 minute", 120000L)
)
}
}
}

View file

@ -1,131 +0,0 @@
package com.tangem.tangemtest.ucase.variants.personalize.converter
import com.tangem.commands.EllipticCurve
import com.tangem.tangemtest._arch.structure.Id
import com.tangem.tangemtest._arch.structure.impl.KeyValue
import com.tangem.tangemtest.ucase.variants.personalize.*
import com.tangem.tangemtest.ucase.variants.personalize.dto.PersonalizeConfig
import ru.dev.gbixahue.eu4d.lib.kotlin.common.BaseTypedHolder
/**
[REDACTED_AUTHOR]
*/
class Value(
private var value: Any? = null,
val list: List<KeyValue>? = mutableListOf()
) {
fun get(): Any? = value
fun set(newValue: Any?) {
value = newValue
}
}
class IdToValueAssociations : BaseTypedHolder<Id, Value>() {
// All registered association values can't be objects
fun init(from: PersonalizeConfig) {
register(CardNumber.Series, Value(from.series))
register(CardNumber.Number, Value(from.startNumber))
register(Common.Curve, Value(from.curveID, Helper.listOfCurves()))
register(Common.Blockchain, Value(from.blockchain, Helper.listOfBlockchain()))
register(Common.BlockchainCustom, Value(""))
register(Common.MaxSignatures, Value(from.MaxSignatures))
register(Common.CreateWallet, Value(from.createWallet))
register(SigningMethod.SignTx, Value(from.SigningMethod0))
register(SigningMethod.SignTxRaw, Value(from.SigningMethod1))
register(SigningMethod.SignValidatedTx, Value(from.SigningMethod2))
register(SigningMethod.SignValidatedTxRaw, Value(from.SigningMethod3))
register(SigningMethod.SignValidatedTxIssuer, Value(from.SigningMethod4))
register(SigningMethod.SignValidatedTxRawIssuer, Value(from.SigningMethod5))
register(SigningMethod.SignExternal, Value(from.SigningMethod6))
register(SignHashExProp.PinLessFloorLimit, Value(from.pinLessFloorLimit))
register(SignHashExProp.CryptoExKey, Value(from.hexCrExKey))
register(SignHashExProp.RequireTerminalCertSig, Value(from.requireTerminalCertSignature))
register(SignHashExProp.RequireTerminalTxSig, Value(from.requireTerminalTxSignature))
register(SignHashExProp.CheckPin3, Value(from.checkPIN3onCard))
register(Denomination.WriteOnPersonalize, Value(from.writeOnPersonalization))
register(Denomination.Denomination, Value(from.denomination))
register(Token.ItsToken, Value(from.itsToken))
register(Token.Symbol, Value(from.symbol))
register(Token.ContractAddress, Value(from.contractAddress))
register(Token.Decimal, Value(from.decimal))
register(ProductMask.Note, Value(from.cardData.product_note))
register(ProductMask.Tag, Value(from.cardData.product_tag))
register(ProductMask.CardId, Value(from.cardData.product_id_card))
register(SettingsMask.IsReusable, Value(from.isReusable))
register(SettingsMask.NeedActivation, Value(from.useActivation))
register(SettingsMask.ForbidPurge, Value(from.forbidPurgeWallet))
register(SettingsMask.AllowSelectBlockchain, Value(from.allowSelectBlockchain))
register(SettingsMask.UseBlock, Value(from.useBlock))
register(SettingsMask.OneApdu, Value(from.oneApdu))
register(SettingsMask.UseCvc, Value(from.useCVC))
register(SettingsMask.AllowSwapPin, Value(from.allowSwapPIN))
register(SettingsMask.AllowSwapPin2, Value(from.allowSwapPIN2))
register(SettingsMask.ForbidDefaultPin, Value(from.forbidDefaultPIN))
register(SettingsMask.SmartSecurityDelay, Value(from.smartSecurityDelay))
register(SettingsMask.ProtectIssuerDataAgainstReplay, Value(from.protectIssuerDataAgainstReplay))
register(SettingsMask.SkipSecurityDelayIfValidated, Value(from.skipSecurityDelayIfValidatedByIssuer))
register(SettingsMask.SkipPin2CvcIfValidated, Value(from.skipCheckPIN2andCVCIfValidatedByIssuer))
register(SettingsMask.SkipSecurityDelayOnLinkedTerminal, Value(from.skipSecurityDelayIfValidatedByLinkedTerminal))
register(SettingsMask.RestrictOverwriteExtraIssuerData, Value(from.restrictOverwriteIssuerDataEx))
register(SettingsMaskProtocolEnc.AllowUnencrypted, Value(from.protocolAllowUnencrypted))
register(SettingsMaskProtocolEnc.AlloFastEncryption, Value(false))
register(SettingsMaskNdef.UseNdef, Value(from.useNDEF))
register(SettingsMaskNdef.DynamicNdef, Value(from.useDynamicNDEF))
register(SettingsMaskNdef.DisablePrecomputedNdef, Value(from.disablePrecomputedNDEF))
register(SettingsMaskNdef.Aar, Value(from.aar, Helper.aarList()))
register(SettingsMaskNdef.AarCustom, Value(from.aar))
register(Pins.Pin, Value(from.PIN))
register(Pins.Pin2, Value(from.PIN2))
register(Pins.Pin3, Value(from.PIN3))
register(Pins.Cvc, Value(from.CVC))
register(Pins.PauseBeforePin2, Value(from.pauseBeforePIN2, Helper.pauseBeforePin()))
}
}
internal class Helper {
companion object {
fun listOfCurves(): List<KeyValue> {
return EllipticCurve.values().map { KeyValue(it.name, it.curve) }
}
fun listOfBlockchain(): List<KeyValue> {
return mutableListOf(
KeyValue("--- CUSTOM ---", "BTC/test"),
KeyValue("BTC", "BTC"),
KeyValue("BTC/test", "BTC/test"),
KeyValue("ETH", "ETH"),
KeyValue("ETH/test", "ETH/test"),
KeyValue("BCH", "BCH"),
KeyValue("BCH/test", "BCH/test"),
KeyValue("LTC", "LTC"),
KeyValue("XLM", "XLM"),
KeyValue("XLM/test", "XLM/test"),
KeyValue("RSK", "RSK"),
KeyValue("XPR", "XPR"),
KeyValue("CARDANO", "CARDANO")
)
}
fun aarList(): List<KeyValue> {
return mutableListOf(
KeyValue("--- CUSTOM ---", ""),
KeyValue("Release APP", "com.tangem.wallet"),
KeyValue("Debug APP", "com.tangem.wallet.debug"),
KeyValue("None", "")
)
}
fun pauseBeforePin(): List<KeyValue> {
return mutableListOf(
KeyValue("immediately", 0L),
KeyValue("2 seconds", 2000L),
KeyValue("5 seconds", 5000L),
KeyValue("15 seconds", 15000L),
KeyValue("30 seconds", 30000L),
KeyValue("1 minute", 60000L),
KeyValue("2 minute", 120000L)
)
}
}
}

View file

@ -1,335 +1,26 @@
package com.tangem.tangemtest.ucase.variants.personalize.converter
import com.tangem.commands.CardData
import com.tangem.commands.EllipticCurve
import com.tangem.commands.ProductMaskBuilder
import com.tangem.commands.personalization.entities.CardConfig
import com.tangem.commands.personalization.entities.NdefRecord
import com.tangem.tangemtest._arch.structure.Additional
import com.tangem.tangemtest._arch.structure.Id
import com.tangem.tangemtest._arch.structure.abstraction.Block
import com.tangem.tangemtest._arch.structure.abstraction.ListItemBlock
import com.tangem.tangemtest._arch.structure.impl.*
import com.tangem.tangemtest.ucase.domain.paramsManager.PayloadKey
import com.tangem.tangemtest.ucase.variants.personalize.*
import com.tangem.tangemtest._arch.structure.abstraction.Item
import com.tangem.tangemtest._arch.structure.abstraction.ItemsToModel
import com.tangem.tangemtest._arch.structure.abstraction.ModelConverter
import com.tangem.tangemtest._arch.structure.abstraction.ModelToItems
import com.tangem.tangemtest.ucase.variants.personalize.converter.fromTo.ItemsToPersonalizeConfig
import com.tangem.tangemtest.ucase.variants.personalize.converter.fromTo.PersonalizeConfigToItem
import com.tangem.tangemtest.ucase.variants.personalize.dto.PersonalizeConfig
import java.util.*
/**
[REDACTED_AUTHOR]
*/
class PersonalizeConfigConverter {
class PersonalizeConfigConverter : ModelConverter<PersonalizeConfig> {
private val associations: IdToValueAssociations = IdToValueAssociations()
private val valueMapper = ValueMapper()
private val toModel: ItemsToModel<PersonalizeConfig> = ItemsToPersonalizeConfig()
private val toItems: ModelToItems<PersonalizeConfig> = PersonalizeConfigToItem()
fun toBlock(from: PersonalizeConfig): List<Block> {
associations.init(from)
val blocList = mutableListOf<Block>()
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())
val payloadBlock = ListItemBlock(Additional.JSON_TAILS)
addPayload(payloadBlock, from)
blocList.add(payloadBlock)
return blocList
override fun convert(from: List<Item>, default: PersonalizeConfig): PersonalizeConfig {
return toModel.convert(from, default)
}
private fun cardNumber(): Block {
val block = createBlock(BlockId.CardNumber)
mutableListOf(
CardNumber.Series,
CardNumber.Number
).forEach { createItem(block, it as Id) }
return block
override fun convert(from: PersonalizeConfig): List<Item> {
return toItems.convert(from)
}
private fun common(): Block {
val block = createBlock(BlockId.Common)
mutableListOf(
Common.Curve,
Common.Blockchain,
Common.BlockchainCustom,
Common.MaxSignatures,
Common.CreateWallet
).forEach { createItem(block, it) }
return block
}
private fun signingMethod(): Block {
val block = createBlock(BlockId.SigningMethod)
mutableListOf(
SigningMethod.SignTx,
SigningMethod.SignTxRaw,
SigningMethod.SignValidatedTx,
SigningMethod.SignValidatedTxRaw,
SigningMethod.SignValidatedTxIssuer,
SigningMethod.SignValidatedTxRawIssuer,
SigningMethod.SignExternal
).forEach { createItem(block, it) }
return block
}
private fun signHashExProperties(): Block {
val block = createBlock(BlockId.SignHashExProp)
mutableListOf(
SignHashExProp.PinLessFloorLimit,
SignHashExProp.CryptoExKey,
SignHashExProp.RequireTerminalCertSig,
SignHashExProp.RequireTerminalTxSig,
SignHashExProp.CheckPin3
).forEach { createItem(block, it) }
return block
}
private fun denomination(): Block {
val block = createBlock(BlockId.Denomination)
mutableListOf(
Denomination.WriteOnPersonalize,
Denomination.Denomination
).forEach { createItem(block, it) }
return block
}
private fun token(): Block {
val block = createBlock(BlockId.Token)
mutableListOf(
Token.ItsToken,
Token.Symbol,
Token.ContractAddress,
Token.Decimal
).forEach { createItem(block, it) }
return block
}
private fun productMask(): Block {
val block = createBlock(BlockId.ProdMask)
mutableListOf(
ProductMask.Note,
ProductMask.Tag,
ProductMask.CardId
).forEach { createItem(block, it) }
return block
}
private fun settingsMask(): Block {
val block = createBlock(BlockId.SettingsMask)
mutableListOf(
SettingsMask.IsReusable,
SettingsMask.NeedActivation,
SettingsMask.ForbidPurge,
SettingsMask.AllowSelectBlockchain,
SettingsMask.UseBlock,
SettingsMask.OneApdu,
SettingsMask.UseCvc,
SettingsMask.AllowSwapPin,
SettingsMask.AllowSwapPin2,
SettingsMask.ForbidDefaultPin,
SettingsMask.SmartSecurityDelay,
SettingsMask.ProtectIssuerDataAgainstReplay,
SettingsMask.SkipSecurityDelayIfValidated,
SettingsMask.SkipPin2CvcIfValidated,
SettingsMask.SkipSecurityDelayOnLinkedTerminal,
SettingsMask.RestrictOverwriteExtraIssuerData
).forEach { createItem(block, it) }
return block
}
private fun settingsMaskProtocolEnc(): Block {
val block = createBlock(BlockId.SettingsMaskProtocolEnc)
mutableListOf(
SettingsMaskProtocolEnc.AllowUnencrypted,
SettingsMaskProtocolEnc.AlloFastEncryption).forEach { createItem(block, it) }
return block
}
private fun settingsMaskNdef(): Block {
val block = createBlock(BlockId.SettingsMaskNdef)
mutableListOf(
SettingsMaskNdef.UseNdef,
SettingsMaskNdef.DynamicNdef,
SettingsMaskNdef.DisablePrecomputedNdef,
SettingsMaskNdef.Aar,
SettingsMaskNdef.AarCustom
).forEach { createItem(block, it) }
return block
}
private fun pins(): Block {
val block = createBlock(BlockId.Pins)
mutableListOf(Pins.Pin, Pins.Pin2, Pins.Pin3, Pins.Cvc, Pins.PauseBeforePin2)
.forEach { createItem(block, it) }
return block
}
private fun createBlock(id: Id): ListItemBlock {
return ListItemBlock(id).apply { addItem(TextItem(id)) }
}
private fun addPayload(block: Block, from: PersonalizeConfig) {
block.payload[PayloadKey.incomingJson] = from
}
private fun createItem(block: ListItemBlock, id: Id) {
val holder = associations.get(id) ?: return
val item = when {
IdItemHelper.blockIdList.contains(id) -> TextItem(id, holder.get() as? String)
IdItemHelper.listItemList.contains(id) -> ListItem(id, holder.list as List<KeyValue>, holder.get())
IdItemHelper.boolList.contains(id) -> BoolItem(id, holder.get() as? Boolean)
IdItemHelper.editTextList.contains(id) -> EditTextItem(id, holder.get() as? String)
IdItemHelper.numberList.contains(id) -> NumberItem(id, holder.get() as? Number)
else -> ListItemBlock(Additional.UNDEFINED)
}
block.addItem(item)
}
fun toConfig(blockList: List<Block>, defaultConfig: PersonalizeConfig): PersonalizeConfig {
return valueMapper.mapOnObject(blockList, defaultConfig)
}
fun createCardConfig(applyConfig: PersonalizeConfig): CardConfig {
val signingMethod = com.tangem.commands.SigningMethod.build(
signHash = applyConfig.SigningMethod0,
signRaw = applyConfig.SigningMethod1,
signHashValidatedByIssuer = applyConfig.SigningMethod2,
signRawValidatedByIssuer = applyConfig.SigningMethod3,
signHashValidatedByIssuerAndWriteIssuerData = applyConfig.SigningMethod4,
signRawValidatedByIssuerAndWriteIssuerData = applyConfig.SigningMethod5,
signPos = applyConfig.SigningMethod6
)
val isNote = applyConfig.cardData.product_note
val isTag = applyConfig.cardData.product_tag
val isIdCard = applyConfig.cardData.product_id_card
val productMaskBuilder = ProductMaskBuilder()
if (isNote) productMaskBuilder.add(com.tangem.commands.ProductMask.note)
if (isTag) productMaskBuilder.add(com.tangem.commands.ProductMask.tag)
if (isIdCard) productMaskBuilder.add(com.tangem.commands.ProductMask.idCard)
val productMask = productMaskBuilder.build()
var tokenSymbol: String? = null
var tokenContractAddress: String? = null
var tokenDecimal: Int? = null
if (applyConfig.itsToken) {
tokenSymbol = applyConfig.symbol
tokenContractAddress = applyConfig.contractAddress
tokenDecimal = applyConfig.decimal.toInt()
}
val cardData = CardData(
blockchainName = applyConfig.blockchain,
batchId = applyConfig.cardData.batch,
productMask = productMask,
tokenSymbol = tokenSymbol,
tokenContractAddress = tokenContractAddress,
tokenDecimal = tokenDecimal,
issuerName = null,
manufactureDateTime = Calendar.getInstance().time,
manufacturerSignature = null)
val ndefs = mutableListOf<NdefRecord>()
when (applyConfig.aar) {
"None" -> null
"--- CUSTOM ---" -> NdefRecord(NdefRecord.Type.AAR, applyConfig.aarCustom)
else -> NdefRecord(NdefRecord.Type.AAR, applyConfig.aar)
}?.let { ndefs.add(it) }
return CardConfig(
"Tangem",
"Tangem Test",
applyConfig.series,
applyConfig.startNumber,
1000,
applyConfig.PIN,
applyConfig.PIN2,
applyConfig.PIN3,
applyConfig.hexCrExKey,
applyConfig.CVC,
applyConfig.pauseBeforePIN2.toInt(),
applyConfig.smartSecurityDelay,
EllipticCurve.byName(applyConfig.curveID) ?: EllipticCurve.Secp256k1,
signingMethod,
applyConfig.MaxSignatures.toInt(),
applyConfig.isReusable,
applyConfig.allowSwapPIN,
applyConfig.allowSwapPIN2,
applyConfig.useActivation,
applyConfig.useCVC,
applyConfig.useNDEF,
applyConfig.useDynamicNDEF,
applyConfig.oneApdu,
applyConfig.useBlock,
applyConfig.allowSelectBlockchain,
applyConfig.forbidPurgeWallet,
applyConfig.protocolAllowUnencrypted,
applyConfig.protocolAllowStaticEncryption,
applyConfig.protectIssuerDataAgainstReplay,
applyConfig.forbidDefaultPIN,
applyConfig.disablePrecomputedNDEF,
applyConfig.skipSecurityDelayIfValidatedByIssuer,
applyConfig.skipCheckPIN2andCVCIfValidatedByIssuer,
applyConfig.skipSecurityDelayIfValidatedByLinkedTerminal,
applyConfig.restrictOverwriteIssuerDataEx,
applyConfig.requireTerminalTxSignature,
applyConfig.requireTerminalCertSignature,
applyConfig.checkPIN3onCard,
applyConfig.createWallet,
cardData,
ndefs
)
}
}
class IdItemHelper {
companion object {
val blockIdList = mutableListOf<com.tangem.tangemtest.ucase.variants.personalize.BlockId>(
BlockId.CardNumber, BlockId.Common, BlockId.SigningMethod, BlockId.SignHashExProp, BlockId.Denomination,
BlockId.Token, BlockId.ProdMask, BlockId.SettingsMask, BlockId.SettingsMaskProtocolEnc,
BlockId.SettingsMaskNdef, BlockId.Pins
)
val listItemList = mutableListOf(Common.Curve, Common.Blockchain, SettingsMaskNdef.Aar, Pins.PauseBeforePin2)
val boolList = mutableListOf(
Common.CreateWallet, SigningMethod.SignTx, SigningMethod.SignTxRaw, SigningMethod.SignValidatedTx,
SigningMethod.SignValidatedTxRaw, SigningMethod.SignValidatedTxIssuer,
SigningMethod.SignValidatedTxRawIssuer, SigningMethod.SignExternal, SignHashExProp.RequireTerminalCertSig,
SignHashExProp.RequireTerminalTxSig, SignHashExProp.CheckPin3, Denomination.WriteOnPersonalize, Token.ItsToken,
ProductMask.Note, ProductMask.Tag, ProductMask.CardId, SettingsMask.IsReusable, SettingsMask.NeedActivation,
SettingsMask.ForbidPurge, SettingsMask.AllowSelectBlockchain, SettingsMask.UseBlock, SettingsMask.OneApdu,
SettingsMask.UseCvc, SettingsMask.AllowSwapPin, SettingsMask.AllowSwapPin2, SettingsMask.ForbidDefaultPin,
SettingsMask.SmartSecurityDelay, SettingsMask.ProtectIssuerDataAgainstReplay,
SettingsMask.SkipSecurityDelayIfValidated, SettingsMask.SkipPin2CvcIfValidated,
SettingsMask.SkipSecurityDelayOnLinkedTerminal, SettingsMask.RestrictOverwriteExtraIssuerData,
SettingsMaskProtocolEnc.AllowUnencrypted, SettingsMaskProtocolEnc.AlloFastEncryption,
SettingsMaskNdef.UseNdef, SettingsMaskNdef.DynamicNdef, SettingsMaskNdef.DisablePrecomputedNdef
)
val editTextList = mutableListOf(
CardNumber.Series, Common.BlockchainCustom, SignHashExProp.CryptoExKey, Token.Symbol,
Token.ContractAddress, Pins.Pin, Pins.Pin2, Pins.Pin3, Pins.Cvc, SettingsMaskNdef.AarCustom
)
val numberList = mutableListOf(
CardNumber.Number, Common.MaxSignatures, SignHashExProp.PinLessFloorLimit, Denomination.Denomination, Token.Decimal
)
}
}
class JsonTails(
val count: Int,
val numberFormat: String,
val issuerData: Any?,
val releaseVersion: Boolean,
val issuerName: String
)
}

View file

@ -0,0 +1,46 @@
package com.tangem.tangemtest.ucase.variants.personalize.converter.fromTo
import com.tangem.tangemtest.ucase.variants.personalize.*
class ItemTypes {
val blockIdList = mutableListOf(
BlockId.CardNumber, BlockId.Common, BlockId.SigningMethod, BlockId.SignHashExProp, BlockId.Denomination,
BlockId.Token, BlockId.ProdMask, BlockId.SettingsMask, BlockId.SettingsMaskProtocolEnc,
BlockId.SettingsMaskNdef, BlockId.Pins
)
val listItemList = mutableListOf(Common.Curve, Common.Blockchain, SettingsMaskNdef.Aar, Pins.PauseBeforePin2)
val boolList = mutableListOf(
Common.CreateWallet, SigningMethod.SignTx, SigningMethod.SignTxRaw, SigningMethod.SignValidatedTx,
SigningMethod.SignValidatedTxRaw, SigningMethod.SignValidatedTxIssuer,
SigningMethod.SignValidatedTxRawIssuer, SigningMethod.SignExternal, SignHashExProp.RequireTerminalCertSig,
SignHashExProp.RequireTerminalTxSig, SignHashExProp.CheckPin3, Denomination.WriteOnPersonalize, Token.ItsToken,
ProductMask.Note, ProductMask.Tag, ProductMask.IdCard, ProductMask.IdIssuerCard,SettingsMask.IsReusable, SettingsMask.NeedActivation,
SettingsMask.ForbidPurge, SettingsMask.AllowSelectBlockchain, SettingsMask.UseBlock, SettingsMask.OneApdu,
SettingsMask.UseCvc, SettingsMask.AllowSwapPin, SettingsMask.AllowSwapPin2, SettingsMask.ForbidDefaultPin,
SettingsMask.SmartSecurityDelay, SettingsMask.ProtectIssuerDataAgainstReplay,
SettingsMask.SkipSecurityDelayIfValidated, SettingsMask.SkipPin2CvcIfValidated,
SettingsMask.SkipSecurityDelayOnLinkedTerminal, SettingsMask.RestrictOverwriteExtraIssuerData,
SettingsMaskProtocolEnc.AllowUnencrypted, SettingsMaskProtocolEnc.AllowStaticEncryption,
SettingsMaskNdef.UseNdef, SettingsMaskNdef.DynamicNdef, SettingsMaskNdef.DisablePrecomputedNdef
)
val editTextList = mutableListOf(
CardNumber.Series, CardNumber.BatchId, Common.BlockchainCustom, SignHashExProp.CryptoExKey, Token.Symbol,
Token.ContractAddress, Pins.Pin, Pins.Pin2, Pins.Pin3, Pins.Cvc, SettingsMaskNdef.AarCustom
)
val numberList = mutableListOf(
CardNumber.Number, Common.MaxSignatures, SignHashExProp.PinLessFloorLimit, Denomination.Denomination, Token.Decimal
)
val hiddenList = mutableListOf(
CardNumber.Series, CardNumber.BatchId, Pins.Pin3, SigningMethod.SignExternal,
SignHashExProp.CryptoExKey, SignHashExProp.CheckPin3, SettingsMask.OneApdu,
SettingsMask.UseBlock, SettingsMask.ProtectIssuerDataAgainstReplay,
SignHashExProp.RequireTerminalCertSig, SignHashExProp.RequireTerminalTxSig
)
}

View file

@ -1,45 +1,44 @@
package com.tangem.tangemtest.ucase.variants.personalize.converter
package com.tangem.tangemtest.ucase.variants.personalize.converter.fromTo
import com.tangem.tangemtest._arch.structure.Id
import com.tangem.tangemtest._arch.structure.abstraction.BaseItem
import com.tangem.tangemtest._arch.structure.abstraction.Block
import com.tangem.tangemtest._arch.structure.abstraction.Item
import com.tangem.tangemtest._arch.structure.abstraction.ItemsToModel
import com.tangem.tangemtest._arch.structure.impl.KeyValue
import com.tangem.tangemtest._arch.structure.impl.ListValueWrapper
import com.tangem.tangemtest.ucase.variants.personalize.*
import com.tangem.tangemtest.ucase.variants.personalize.converter.PersonalizeConfigValuesHolder
import com.tangem.tangemtest.ucase.variants.personalize.dto.PersonalizeConfig
import ru.dev.gbixahue.eu4d.lib.android.global.log.Log
/**
[REDACTED_AUTHOR]
*/
class ValueMapper {
private val default: IdToValueAssociations = IdToValueAssociations()
private val updatedByItemList: IdToValueAssociations = IdToValueAssociations()
class ItemsToPersonalizeConfig : ItemsToModel<PersonalizeConfig> {
protected val valuesHolder = PersonalizeConfigValuesHolder()
fun mapOnObject(itemList: List<Item>, defaultConfig: PersonalizeConfig): PersonalizeConfig {
default.init(defaultConfig)
updatedByItemList.init(defaultConfig)
startMapping(itemList)
return createConfig()
override fun convert(from: List<Item>, default: PersonalizeConfig): PersonalizeConfig {
valuesHolder.init(default)
mapListItems(from)
return createModel()
}
private fun startMapping(itemList: List<Item>) {
itemList.forEach { item -> mapItem(item) }
private fun mapListItems(itemList: List<Item>) {
itemList.forEach { item -> mapItemToHolder(item) }
}
private fun mapItem(item: Item) {
private fun mapItemToHolder(item: Item) {
when (item) {
is Block -> startMapping(item.itemList)
is Block -> mapListItems(item.itemList)
is BaseItem<*> -> {
val value = updatedByItemList.get(item.id) ?: return
value.set(item.viewModel.data)
val defValue = valuesHolder.get(item.id) ?: return
defValue.set(item.viewModel.data)
}
}
}
private fun createConfig(): PersonalizeConfig {
private fun createModel(): PersonalizeConfig {
val export = PersonalizeConfig()
export.series = getTyped(CardNumber.Series)
export.startNumber = getTyped(CardNumber.Number)
@ -68,7 +67,8 @@ class ValueMapper {
export.decimal = getTyped(Token.Decimal)
export.cardData = export.cardData.apply { this.product_note = getTyped(ProductMask.Note) }
export.cardData = export.cardData.apply { this.product_tag = getTyped(ProductMask.Tag) }
export.cardData = export.cardData.apply { this.product_id_card = getTyped(ProductMask.CardId) }
export.cardData = export.cardData.apply { this.product_id_card = getTyped(ProductMask.IdCard) }
export.cardData = export.cardData.apply { this.product_id_issuer = getTyped(ProductMask.IdIssuerCard) }
export.isReusable = getTyped(SettingsMask.IsReusable)
export.useActivation = getTyped(SettingsMask.NeedActivation)
export.forbidPurgeWallet = getTyped(SettingsMask.ForbidPurge)
@ -86,7 +86,7 @@ class ValueMapper {
export.skipSecurityDelayIfValidatedByLinkedTerminal = getTyped(SettingsMask.SkipSecurityDelayOnLinkedTerminal)
export.restrictOverwriteIssuerDataEx = getTyped(SettingsMask.RestrictOverwriteExtraIssuerData)
export.protocolAllowUnencrypted = getTyped(SettingsMaskProtocolEnc.AllowUnencrypted)
export.allowFastEncryption = getTyped(SettingsMaskProtocolEnc.AlloFastEncryption)
export.protocolAllowStaticEncryption = getTyped(SettingsMaskProtocolEnc.AllowStaticEncryption)
export.useNDEF = getTyped(SettingsMaskNdef.UseNdef)
export.useDynamicNDEF = getTyped(SettingsMaskNdef.DynamicNdef)
export.disablePrecomputedNDEF = getTyped(SettingsMaskNdef.DisablePrecomputedNdef)
@ -101,12 +101,12 @@ class ValueMapper {
}
private inline fun <reified Type> getTyped(id: Id): Type {
return getTypedBy<Type>(updatedByItemList, id) ?: getTypedBy<Type>(default, id)!!
return getTypedBy<Type>(valuesHolder, id)!!
}
private inline fun <reified Type> getTypedBy(associations: IdToValueAssociations, id: Id): Type? {
private inline fun <reified Type> getTypedBy(holder: PersonalizeConfigValuesHolder, id: Id): Type? {
Log.d(this, "getTyped for id: $id")
var typedValue = associations.get(id)?.get()
var typedValue = holder.get(id)?.get()
typedValue = when (typedValue) {
is ListValueWrapper -> (typedValue.selectedItem as KeyValue).value as Type

View file

@ -0,0 +1,301 @@
package com.tangem.tangemtest.ucase.variants.personalize.converter.fromTo
import com.tangem.commands.CardData
import com.tangem.commands.EllipticCurve
import com.tangem.commands.ProductMaskBuilder
import com.tangem.commands.personalization.entities.CardConfig
import com.tangem.commands.personalization.entities.NdefRecord
import com.tangem.tangemtest._arch.structure.Additional
import com.tangem.tangemtest._arch.structure.Id
import com.tangem.tangemtest._arch.structure.abstraction.*
import com.tangem.tangemtest._arch.structure.impl.*
import com.tangem.tangemtest.ucase.domain.paramsManager.PayloadKey
import com.tangem.tangemtest.ucase.variants.personalize.*
import com.tangem.tangemtest.ucase.variants.personalize.converter.PersonalizeConfigValuesHolder
import com.tangem.tangemtest.ucase.variants.personalize.dto.PersonalizeConfig
import ru.dev.gbixahue.eu4d.lib.kotlin.common.Converter
import java.util.*
/**
[REDACTED_AUTHOR]
*/
class PersonalizeConfigToItem : ModelToItems<PersonalizeConfig> {
private val valuesHolder = PersonalizeConfigValuesHolder()
private val itemTypes = ItemTypes()
override fun convert(from: PersonalizeConfig): List<Item> {
valuesHolder.init(from)
val blocList = mutableListOf<Item>()
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())
val payloadBlock = ListItemBlock(Additional.JSON_TAILS)
addPayload(payloadBlock, from)
blocList.add(payloadBlock)
blocList.iterate {
if (itemTypes.hiddenList.contains(it.id)) {
(it as BaseItem<*>)?.viewModel.viewState.isHiddenField = true
}
}
return blocList
}
private fun cardNumber(): Block {
val block = createBlock(BlockId.CardNumber)
mutableListOf(
CardNumber.Series,
CardNumber.Number,
CardNumber.BatchId
).forEach { createItem(block, it as Id) }
return block
}
private fun common(): Block {
val block = createBlock(BlockId.Common)
mutableListOf(
Common.Curve,
Common.Blockchain,
Common.BlockchainCustom,
Common.MaxSignatures,
Common.CreateWallet
).forEach { createItem(block, it) }
return block
}
private fun signingMethod(): Block {
val block = createBlock(BlockId.SigningMethod)
mutableListOf(
SigningMethod.SignTx,
SigningMethod.SignTxRaw,
SigningMethod.SignValidatedTx,
SigningMethod.SignValidatedTxRaw,
SigningMethod.SignValidatedTxIssuer,
SigningMethod.SignValidatedTxRawIssuer,
SigningMethod.SignExternal
).forEach { createItem(block, it) }
return block
}
private fun signHashExProperties(): Block {
val block = createBlock(BlockId.SignHashExProp)
mutableListOf(
SignHashExProp.PinLessFloorLimit,
SignHashExProp.CryptoExKey,
SignHashExProp.RequireTerminalCertSig,
SignHashExProp.RequireTerminalTxSig,
SignHashExProp.CheckPin3
).forEach { createItem(block, it) }
return block
}
private fun denomination(): Block {
val block = createBlock(BlockId.Denomination)
mutableListOf(
Denomination.WriteOnPersonalize,
Denomination.Denomination
).forEach { createItem(block, it) }
return block
}
private fun token(): Block {
val block = createBlock(BlockId.Token)
mutableListOf(
Token.ItsToken,
Token.Symbol,
Token.ContractAddress,
Token.Decimal
).forEach { createItem(block, it) }
return block
}
private fun productMask(): Block {
val block = createBlock(BlockId.ProdMask)
mutableListOf(
ProductMask.Note,
ProductMask.Tag,
ProductMask.IdCard,
ProductMask.IdIssuerCard
).forEach { createItem(block, it) }
return block
}
private fun settingsMask(): Block {
val block = createBlock(BlockId.SettingsMask)
mutableListOf(
SettingsMask.IsReusable,
SettingsMask.NeedActivation,
SettingsMask.ForbidPurge,
SettingsMask.AllowSelectBlockchain,
SettingsMask.UseBlock,
SettingsMask.OneApdu,
SettingsMask.UseCvc,
SettingsMask.AllowSwapPin,
SettingsMask.AllowSwapPin2,
SettingsMask.ForbidDefaultPin,
SettingsMask.SmartSecurityDelay,
SettingsMask.ProtectIssuerDataAgainstReplay,
SettingsMask.SkipSecurityDelayIfValidated,
SettingsMask.SkipPin2CvcIfValidated,
SettingsMask.SkipSecurityDelayOnLinkedTerminal,
SettingsMask.RestrictOverwriteExtraIssuerData
).forEach { createItem(block, it) }
return block
}
private fun settingsMaskProtocolEnc(): Block {
val block = createBlock(BlockId.SettingsMaskProtocolEnc)
mutableListOf(
SettingsMaskProtocolEnc.AllowUnencrypted,
SettingsMaskProtocolEnc.AllowStaticEncryption
).forEach { createItem(block, it) }
return block
}
private fun settingsMaskNdef(): Block {
val block = createBlock(BlockId.SettingsMaskNdef)
mutableListOf(
SettingsMaskNdef.UseNdef,
SettingsMaskNdef.DynamicNdef,
SettingsMaskNdef.DisablePrecomputedNdef,
SettingsMaskNdef.Aar,
SettingsMaskNdef.AarCustom
).forEach { createItem(block, it) }
return block
}
private fun pins(): Block {
val block = createBlock(BlockId.Pins)
mutableListOf(Pins.Pin, Pins.Pin2, Pins.Pin3, Pins.Cvc, Pins.PauseBeforePin2)
.forEach { createItem(block, it) }
return block
}
private fun createBlock(id: Id): ListItemBlock {
return ListItemBlock(id).apply { addItem(TextItem(id)) }
}
private fun addPayload(block: Block, from: PersonalizeConfig) {
block.payload[PayloadKey.incomingJson] = from
}
private fun createItem(block: ListItemBlock, id: Id) {
val holder = valuesHolder.get(id) ?: return
val item = when {
itemTypes.blockIdList.contains(id) -> TextItem(id, holder.get() as? String)
itemTypes.listItemList.contains(id) -> ListItem(id, holder.list as List<KeyValue>, holder.get())
itemTypes.boolList.contains(id) -> BoolItem(id, holder.get() as? Boolean)
itemTypes.editTextList.contains(id) -> EditTextItem(id, holder.get() as? String)
itemTypes.numberList.contains(id) -> NumberItem(id, holder.get() as? Number)
else -> ListItemBlock(Additional.UNDEFINED)
}
block.addItem(item)
}
}
class PersonalizeConfigToCardConfig : Converter<PersonalizeConfig, CardConfig> {
override fun convert(from: PersonalizeConfig): CardConfig {
val signingMethod = com.tangem.commands.SigningMethod.build(
signHash = from.SigningMethod0,
signRaw = from.SigningMethod1,
signHashValidatedByIssuer = from.SigningMethod2,
signRawValidatedByIssuer = from.SigningMethod3,
signHashValidatedByIssuerAndWriteIssuerData = from.SigningMethod4,
signRawValidatedByIssuerAndWriteIssuerData = from.SigningMethod5,
signPos = from.SigningMethod6
)
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.ProductMask.note)
if (isTag) productMaskBuilder.add(com.tangem.commands.ProductMask.tag)
if (isIdCard) productMaskBuilder.add(com.tangem.commands.ProductMask.idCard)
if (isIdIssuer) productMaskBuilder.add(com.tangem.commands.ProductMask.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>()
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
)
}
}

View file

@ -8,11 +8,12 @@ class PersonalizeConfig {
// Card number
var series = "BB"
var startNumber: Long = 300000000000
var batchId = "ffff"
// Common
var curveID = "ed25519"
var blockchain = "BTC"
var blockchain = "BTC/test"
var blockchainCustom = ""
var MaxSignatures: Long = 100
var createWallet = false
@ -71,7 +72,6 @@ class PersonalizeConfig {
// Settings mask - protocol encryption
var protocolAllowUnencrypted = true
var allowFastEncryption = false
var protocolAllowStaticEncryption = true
@ -105,4 +105,5 @@ class CardData {
var product_note = true
var product_tag = false
var product_id_card = false
var product_id_issuer = false
}

View file

@ -9,28 +9,32 @@ import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.fragment.app.viewModels
import androidx.lifecycle.Observer
import com.google.android.material.floatingactionbutton.FloatingActionButton
import com.google.android.material.snackbar.BaseTransientBottomBar
import com.google.android.material.snackbar.Snackbar
import com.tangem.CardManager
import com.tangem.tangem_sdk_new.extensions.init
import com.tangem.tangemtest.R
import com.tangem.tangemtest._arch.structure.abstraction.Block
import com.tangem.tangemtest._arch.structure.Id
import com.tangem.tangemtest._arch.structure.abstraction.Item
import com.tangem.tangemtest._main.MainViewModel
import com.tangem.tangemtest.ucase.domain.paramsManager.ItemsManager
import com.tangem.tangemtest.ucase.domain.paramsManager.ParamsManagerFactory
import com.tangem.tangemtest.ucase.domain.paramsManager.PayloadKey
import com.tangem.tangemtest.ucase.resources.ActionType
import com.tangem.tangemtest.ucase.tunnel.ActionView
import com.tangem.tangemtest.ucase.tunnel.CardError
import com.tangem.tangemtest.ucase.tunnel.ItemError
import com.tangem.tangemtest.ucase.ui.ActionViewModelFactory
import com.tangem.tangemtest.ucase.ui.ParamsViewModel
import com.tangem.tangemtest.ucase.variants.personalize.dto.PersonalizeConfig
import com.tangem.tangemtest.ucase.variants.personalize.ui.widgets.WidgetBuilder
import kotlinx.android.synthetic.main.fg_personalize.*
import ru.dev.gbixahue.eu4d.lib.android.global.log.Log
/**
[REDACTED_AUTHOR]
*/
class PersonalizeFragment : Fragment() {
class PersonalizeFragment : Fragment(), ActionView {
private val mainActivityVM: MainViewModel by activityViewModels()
private val personalizeVM: PersonalizeViewModel by viewModels { PersonalizeViewModelFactory(PersonalizeConfig()) }
@ -38,10 +42,9 @@ class PersonalizeFragment : Fragment() {
private val itemsManager: ItemsManager by lazy { ParamsManagerFactory.createFactory().get(ActionType.Personalize)!! }
private val paramsVM: ParamsViewModel by viewModels() { ActionViewModelFactory(itemsManager) }
private val blockContainer: ViewGroup by lazy {
mainView.findViewById<LinearLayout>(R.id.ll_container)
}
private lateinit var mainView: View
private val blockContainer: ViewGroup by lazy { mainView.findViewById<LinearLayout>(R.id.ll_container) }
private val actionFab: FloatingActionButton by lazy { mainView.findViewById<FloatingActionButton>(R.id.fab_action) }
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
Log.d(this, "onCreateView")
@ -52,11 +55,16 @@ class PersonalizeFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val blockList = mutableListOf<Block>()
val blockList = mutableListOf<Item>()
personalizeVM.ldBlockList.observe(viewLifecycleOwner, Observer { list ->
blockList.clear()
blockList.addAll(list)
blockList.forEach { WidgetBuilder().build(it, blockContainer) }
paramsVM.attachToPayload(mutableMapOf(
PayloadKey.actionView to this as ActionView,
PayloadKey.itemList to blockList
))
})
mainActivityVM.ldDescriptionSwitch.observe(viewLifecycleOwner, Observer {
personalizeVM.toggleDescriptionVisibility(it)
@ -70,16 +78,34 @@ class PersonalizeFragment : Fragment() {
})
paramsVM.seError.observe(viewLifecycleOwner, Observer { showSnackbarMessage(it) })
fab_action?.setOnClickListener {
val cardConfig = personalizeVM.createCardConfig(personalizeVM.createConfig(blockList))
paramsVM.attachToPayload(mutableMapOf(PayloadKey.cardConfig to cardConfig))
paramsVM.invokeMainAction()
}
actionFab.setOnClickListener { paramsVM.invokeMainAction() }
}
protected fun showSnackbarMessage(message: String) {
Snackbar.make(mainView, message, BaseTransientBottomBar.LENGTH_SHORT).show()
}
override fun showActionFab(show: Boolean) {
if (show) actionFab.show() else actionFab.hide()
}
override fun showSnackbar(id: Id) {
// MainResourceHolder.safeGet<>()
when (id) {
CardError.NotPersonalized -> showSnackbar(R.string.card_error_not_personalized)
ItemError.BadSeries -> showSnackbar(R.string.card_error_bad_series)
ItemError.BadCardNumber -> showSnackbar(R.string.card_error_bad_series_number)
else -> showSnackbar(requireContext().getString(R.string.unknown))
}
}
override fun showSnackbar(id: Int) {
showSnackbar(requireContext().getString(id))
}
override fun showSnackbar(message: String) {
Snackbar.make(mainView, message, BaseTransientBottomBar.LENGTH_SHORT).show()
}
}

View file

@ -4,10 +4,9 @@ import android.view.View
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import com.google.gson.Gson
import com.tangem.commands.personalization.entities.CardConfig
import com.tangem.tangemtest._arch.structure.abstraction.BaseItem
import com.tangem.tangemtest._arch.structure.abstraction.Block
import com.tangem.tangemtest._arch.structure.abstraction.Item
import com.tangem.tangemtest._arch.structure.abstraction.iterate
import com.tangem.tangemtest.ucase.variants.personalize.converter.PersonalizeConfigConverter
import com.tangem.tangemtest.ucase.variants.personalize.dto.PersonalizeConfig
@ -20,32 +19,14 @@ class PersonalizeViewModelFactory(private val config: PersonalizeConfig) : ViewM
class PersonalizeViewModel(private val config: PersonalizeConfig) : ViewModel() {
val ldBlockList: MutableLiveData<List<Block>> by lazy { MutableLiveData(initBlockList()) }
val ldBlockList: MutableLiveData<List<Item>> by lazy { MutableLiveData(initBlockList()) }
private fun initBlockList(): List<Block> = createBlocksFromConfig(config)
fun createBlocksFromConfig(config: PersonalizeConfig): List<Block> {
return PersonalizeConfigConverter().toBlock(config)
}
fun createConfig(blocList: List<Block>): PersonalizeConfig {
return PersonalizeConfigConverter().toConfig(blocList, PersonalizeConfig())
}
fun createCardConfig(config: PersonalizeConfig): CardConfig {
return PersonalizeConfigConverter().createCardConfig(config)
}
fun convertToJson(config: PersonalizeConfig): String {
return Gson().toJson(config)
}
private fun initBlockList(): List<Item> = PersonalizeConfigConverter().convert(config)
fun toggleDescriptionVisibility(state: Boolean) {
ldBlockList.value?.forEach { block ->
block.itemList.forEach { item ->
val vm = item as? BaseItem<*> ?: return@forEach
vm.viewModel.viewState.descriptionVisibility = if (state) View.VISIBLE else View.GONE
}
ldBlockList.value?.iterate {
val baseItem = it as? BaseItem<*> ?: return@iterate
baseItem.viewModel.viewState.descriptionVisibility = if (state) View.VISIBLE else View.GONE
}
}
}

View file

@ -30,6 +30,12 @@ abstract class BaseWidget<D>(
) : ViewWidget, DataWidget<D> {
override val view: View = inflate(getLayoutId(), parent)
init {
if (dataItem.viewModel.viewState.isHiddenField) {
view.visibility = View.GONE
}
}
}
abstract class BaseBlockWidget(parent: ViewGroup) : BlockViewWidget {

View file

@ -8,6 +8,7 @@ import com.google.android.material.textfield.TextInputLayout
import com.tangem.tangemtest.R
import com.tangem.tangemtest._arch.structure.impl.EditTextItem
import com.tangem.tangemtest.ucase.variants.personalize.ui.widgets.abstraction.getResNameId
import ru.dev.gbixahue.eu4d.lib.android._android.views.moveCursorToEnd
import ru.dev.gbixahue.eu4d.lib.kotlin.stringOf
/**
@ -32,10 +33,13 @@ class EditTextWidget(parent: ViewGroup, data: EditTextItem) : DescriptionWidget<
tilItem.hint = tilItem.context.getString(getResNameId())
etItem.setText(dataItem.viewModel.data)
etItem.addTextChangedListener(watcher)
dataItem.viewModel.onDataUpdated = {
etItem.removeTextChangedListener(watcher)
etItem.setText(it)
etItem.addTextChangedListener(watcher)
}
dataItem.viewModel.onDataUpdated = { silentUpdate(it) }
}
private fun silentUpdate(value: String?) {
etItem.removeTextChangedListener(watcher)
etItem.setText(value)
etItem.moveCursorToEnd()
etItem.addTextChangedListener(watcher)
}
}

View file

@ -1,13 +1,17 @@
package com.tangem.tangemtest.ucase.variants.personalize.ui.widgets.impl.item
import android.text.Editable
import android.text.InputFilter
import android.text.TextWatcher
import android.view.ViewGroup
import com.google.android.material.textfield.TextInputEditText
import com.google.android.material.textfield.TextInputLayout
import com.tangem.tangemtest.R
import com.tangem.tangemtest._arch.structure.impl.NumberItem
import com.tangem.tangemtest.ucase.variants.personalize.CardNumber
import com.tangem.tangemtest.ucase.variants.personalize.ui.widgets.abstraction.getResNameId
import ru.dev.gbixahue.eu4d.lib.android._android.views.addInputFilter
import ru.dev.gbixahue.eu4d.lib.android._android.views.moveCursorToEnd
import ru.dev.gbixahue.eu4d.lib.kotlin.stringOf
/**
@ -21,7 +25,7 @@ class NumberWidget(parent: ViewGroup, data: NumberItem) : DescriptionWidget<Numb
private val watcher = object : TextWatcher {
override fun afterTextChanged(s: Editable?) {
dataItem.viewModel.updateDataByView(getIntValue(stringOf(s)))
dataItem.viewModel.updateDataByView(getValue(stringOf(s)))
}
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
@ -30,16 +34,23 @@ class NumberWidget(parent: ViewGroup, data: NumberItem) : DescriptionWidget<Numb
init {
tilItem.hint = tilItem.context.getString(getResNameId())
//TODO: remove from Widget
if (dataItem.id == CardNumber.Number) etItem.addInputFilter(InputFilter.LengthFilter(13))
etItem.setText(stringOf(dataItem.viewModel.data))
etItem.addTextChangedListener(watcher)
dataItem.viewModel.onDataUpdated = {
etItem.removeTextChangedListener(watcher)
etItem.setText(stringOf(it))
etItem.addTextChangedListener(watcher)
}
dataItem.viewModel.onDataUpdated = { silentUpdate(it) }
}
private fun getIntValue(value: String): Long {
private fun silentUpdate(value: Number?) {
etItem.removeTextChangedListener(watcher)
etItem.setText(stringOf(value))
etItem.moveCursorToEnd()
etItem.addTextChangedListener(watcher)
}
private fun getValue(value: String): Long {
return if (value.isEmpty()) 0L else value.toLong()
}

View file

@ -17,20 +17,20 @@ import ru.dev.gbixahue.eu4d.lib.kotlin.stringOf
/**
[REDACTED_AUTHOR]
*/
class SpinnerWidget(parent: ViewGroup, data: ListItem) : DescriptionWidget<ListValueWrapper>(parent, data) {
class SpinnerWidget(parent: ViewGroup, listItem: ListItem) : DescriptionWidget<ListValueWrapper>(parent, listItem) {
override fun getLayoutId(): Int = R.layout.w_personalize_item_spinner
private val data: ListValueWrapper = listItem.getData()!!
private val spItem = view.findViewById<Spinner>(R.id.sp_item)
private val spAdapter = SpItemAdapter(dataItem.viewModel.data?.itemList)
private val spAdapter = SpItemAdapter(data.itemList)
private val onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onNothingSelected(parent: AdapterView<*>?) {}
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
val vmData = dataItem.viewModel.data ?: return
vmData.selectedItem = vmData.itemList[position]
dataItem.viewModel.updateDataByView(vmData)
data.selectedItem = data.itemList[position]
dataItem.viewModel.updateDataByView(data)
}
}
@ -38,6 +38,9 @@ class SpinnerWidget(parent: ViewGroup, data: ListItem) : DescriptionWidget<ListV
val name = view.findViewById<TextView>(R.id.tv_name)
name.setText(getResNameId())
spItem.adapter = spAdapter
spAdapter.getItemPosition(stringOf(data.selectedItem))?.let {
spItem.setSelection(it)
}
spItem.onItemSelectedListener = onItemSelectedListener
dataItem.viewModel.onDataUpdated = {
it?.apply {
@ -81,4 +84,9 @@ class SpItemAdapter(list: List<KeyValue>?) : BaseAdapter() {
override fun getItemId(position: Int): Long = position.toLong()
override fun getCount(): Int = itemList.size
fun getItemPosition(item: String): Int? {
val kv = itemList.firstOrNull { it.value == item } ?: return null
return itemList.indexOf(kv)
}
}

View file

@ -8,6 +8,8 @@
<string name="switch_description">Docs</string>
<string name="card_error_not_personalized">Your card hasn\'t been personalized yet. You need to run personalize command first.</string>
<string name="card_error_bad_series">Wrong Series</string>
<string name="card_error_bad_series_number">Wrong Series and Number</string>
<string name="stub">Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit</string>
<!-- <string name="stub">Sed ut perspiciatis unde omnis iste natus</string>-->

View file

@ -17,6 +17,7 @@
<string name="pers_item_series">Series</string>
<string name="pers_item_number">Number</string>
<string name="pers_item_batch_id">Batch ID</string>
<string name="pers_item_curve">Curve</string>
<string name="pers_item_blockchain">Blockchain</string>
<string name="pers_item_custom_blockchain">Custom blockchain</string>
@ -43,6 +44,7 @@
<string name="pers_item_note">Note</string>
<string name="pers_item_tag">Tag</string>
<string name="pers_item_id_card">ID card</string>
<string name="pers_item_id_issuer_card">ID Issuer</string>
<string name="pers_item_is_reusable">Is reusable</string>
<string name="pers_item_need_activation">Need activation</string>
<string name="pers_item_forbid_purge">Forbid purge</string>
@ -55,7 +57,7 @@
<string name="pers_item_forbid_default_pin">Forbid default PIN</string>
<string name="pers_item_smart_security_delay">Smart Security Delay</string>
<string name="pers_item_protect_issuer_data_against_replay">Protect issuer data against replay</string>
<string name="pers_item_skip_security_delay_if_validated">Security delay if validated</string>
<string name="pers_item_skip_security_delay_if_validated">Skip security delay if validated</string>
<string name="pers_item_skip_pin2_and_cvc_if_validated">Skip PIN2 and CVC if validated</string>
<string name="pers_item_skip_security_delay_on_linked_terminal">Skip security delay on linked terminal</string>
<string name="pers_item_restrict_overwrite_ex_issuer_data">Restrict overwrite extra issuer data</string>
@ -86,6 +88,7 @@
<string name="info_pers_item_series"></string>
<string name="info_pers_item_number"></string>
<string name="info_pers_item_batch_id"></string>
<string name="info_pers_item_curve">Elliptic curve used for all wallet key operations. Supported curves: secp256k1, ed25519.</string>
<string name="info_pers_item_blockchain">Name of the blockchain. It\'s just a textfield in the card. It\'s needed for host app to tell what blockchain should it use. </string>
<string name="info_pers_item_custom_blockchain">You can use this field, to create something special</string>
@ -113,6 +116,7 @@
<string name="info_pers_item_note">Self-custodial wallet or note</string>
<string name="info_pers_item_tag">Anti-counterfeit tag</string>
<string name="info_pers_item_id_card">Personal ID card</string>
<string name="info_pers_item_id_issuer_card"></string>
<string name="info_pers_item_is_reusable">Defines what happens when user calls PURGE_WALLET command:
\nOff - Card will switch to Purged state
\nOn - Card will switch to Empty state and let create a new wallet again