diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/responses/ResponseJsonConverter.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/responses/ResponseJsonConverter.kt index 794f4aa727..5533c3682d 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/responses/ResponseJsonConverter.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/responses/ResponseJsonConverter.kt @@ -20,7 +20,7 @@ class ResponseJsonConverter { private fun init(): Gson { val builder = GsonBuilder().apply { registerTypeAdapter(ByteArray::class.java, ByteTypeAdapter(fieldConverter)) - registerTypeAdapter(SigningMethod::class.java, SigningMethodTypeAdapter(fieldConverter)) + registerTypeAdapter(SigningMethodMask::class.java, SigningMethodTypeAdapter(fieldConverter)) registerTypeAdapter(SettingsMask::class.java, SettingsMaskTypeAdapter(fieldConverter)) registerTypeAdapter(ProductMask::class.java, ProductMaskTypeAdapter(fieldConverter)) registerTypeAdapter(Date::class.java, DateTypeAdapter()) @@ -62,8 +62,8 @@ class ProductMaskTypeAdapter( class SigningMethodTypeAdapter( private val fieldConverter: ResponseFieldConverter -) : JsonSerializer { - override fun serialize(src: SigningMethod, typeOfSrc: Type, context: JsonSerializationContext): JsonElement { +) : JsonSerializer { + override fun serialize(src: SigningMethodMask, typeOfSrc: Type, context: JsonSerializationContext): JsonElement { return JsonArray().apply { fieldConverter.signingMethodList(src).forEach { add(it) } } @@ -86,32 +86,17 @@ class ResponseFieldConverter { fun productMaskList(productMask: ProductMask?): List { val mask = productMask ?: return emptyList() - val signingMap = mutableMapOf( - ProductMask.note to "Note", - ProductMask.tag to "Tag", - ProductMask.idCard to "IdCard", - ProductMask.idIssuer to "IdIssuer" - ) - return signingMap.filter { mask.contains(it.key) }.map { it.value } + return Product.values().filter { mask.contains(it) }.map { it.name } } - fun signingMethod(signingMethod: SigningMethod?): String { - return signingMethodList(signingMethod).print(wrap = false) + fun signingMethod(signingMask: SigningMethodMask?): String { + return signingMethodList(signingMask).print(wrap = false) } - fun signingMethodList(signingMethod: SigningMethod?): List { - val methods = signingMethod ?: return emptyList() + fun signingMethodList(signingMask: SigningMethodMask?): List { + val mask = signingMask ?: return emptyList() - val signingMap = mutableMapOf( - SigningMethod.signHash to "Hash", - SigningMethod.signRaw to "Raw", - SigningMethod.signHashValidatedByIssuer to "HashValidatedByIssuer", - SigningMethod.signRawValidatedByIssuer to "RawValidatedByIssuer", - SigningMethod.signHashValidatedByIssuerAndWriteIssuerData to "HashValidatedByIssuerAndWriteIssuerData", - SigningMethod.signRawValidatedByIssuerAndWriteIssuerData to "RawValidatedByIssuerAndWriteIssuerData", - SigningMethod.signPos to "Pos" - ) - return signingMap.filter { methods.contains(it.key) }.map { it.value } + return SigningMethod.values().filter { mask.contains(it) }.map { it.name } } fun settingsMask(settingsMask: SettingsMask?): String { @@ -124,7 +109,7 @@ class ResponseFieldConverter { return Settings.values().filter { masks.contains(it) }.map { it.name } } - fun byteArray(byteArray: ByteArray?): String { - return byteArray?.toHexString() ?: "" + fun byteArray(byteArray: ByteArray?): String? { + return byteArray?.toHexString() } } \ No newline at end of file diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/resources/initializers/ResponseResources.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/resources/initializers/ResponseResources.kt index 989bdce039..a199a04a26 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/resources/initializers/ResponseResources.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/resources/initializers/ResponseResources.kt @@ -30,7 +30,7 @@ class ResponseResources { holder.register(CardId.issuerPublicKey, Resources(R.string.response_card_issuer_data_public_key, R.string.info_response_card_issuer_data_public_key)) holder.register(CardId.curve, Resources(R.string.response_card_curve, R.string.info_response_card_curve)) holder.register(CardId.maxSignatures, Resources(R.string.response_card_max_signatures, R.string.info_response_card_max_signatures)) - holder.register(CardId.signingMethod, Resources(R.string.response_card_signing_method, R.string.response_card_signing_method)) + holder.register(CardId.signingMethod, Resources(R.string.response_card_signing_method, R.string.info_response_card_signing_method)) holder.register(CardId.pauseBeforePin2, Resources(R.string.response_card_pause_before_pin2, R.string.info_response_card_allow_pin2)) holder.register(CardId.walletPublicKey, Resources(R.string.response_card_wallet_public_key, R.string.info_response_card_wallet_public_key)) holder.register(CardId.walletRemainingSignatures, Resources(R.string.response_card_wallet_remaining_signatures, R.string.info_response_card_wallet_remaining_signatures)) diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/responses/converter/CardConverter.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/responses/converter/CardConverter.kt index cb8d978fbe..f7676fd1b4 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/responses/converter/CardConverter.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/responses/converter/CardConverter.kt @@ -1,33 +1,27 @@ package com.tangem.tangemtest.ucase.variants.responses.converter -import com.tangem.commands.Card -import com.tangem.commands.CardData -import com.tangem.commands.Settings -import com.tangem.commands.SettingsMask +import com.tangem.commands.* import com.tangem.tangemtest.R -import com.tangem.tangemtest._arch.structure.Id import com.tangem.tangemtest._arch.structure.StringId -import com.tangem.tangemtest._arch.structure.abstraction.* +import com.tangem.tangemtest._arch.structure.abstraction.Item +import com.tangem.tangemtest._arch.structure.abstraction.ItemGroup +import com.tangem.tangemtest._arch.structure.abstraction.iterate import com.tangem.tangemtest._arch.structure.impl.BoolItem import com.tangem.tangemtest._arch.structure.impl.TextItem -import com.tangem.tangemtest.ucase.domain.responses.ResponseFieldConverter -import com.tangem.tangemtest.ucase.variants.personalize.BlockId import com.tangem.tangemtest.ucase.variants.responses.CardDataId import com.tangem.tangemtest.ucase.variants.responses.CardId -import ru.dev.gbixahue.eu4d.lib.kotlin.stringOf +import com.tangem.tangemtest.ucase.variants.responses.item.TextHeaderItem /** [REDACTED_AUTHOR] */ -class CardConverter : ModelToItems { - - private val fieldConverter = ResponseFieldConverter() +class CardConverter : BaseResponseConverter() { override fun convert(from: Card): List { val itemList = mutableListOf() - itemList.add(simpleFields(from)) - itemList.add(cardData(from.cardData)) - itemList.add(settingsMask(from.settingsMask)) + commonGroup(itemList, from) + cardDataGroup(itemList, from.cardData) + settingsMaskGroup(itemList, from.settingsMask) hideEmptyNullFields(itemList) return itemList @@ -35,70 +29,66 @@ class CardConverter : ModelToItems { private fun hideEmptyNullFields(itemList: MutableList) { itemList.iterate { - val data = it.getData() - val isHidden = if (data == null) true - else when (data) { - is String -> data.isEmpty() - else -> false - } + if (it is ItemGroup) return@iterate - if (isHidden) it.viewModel.viewState.isVisibleState.value = false + if (it.getData() == null) it.viewModel.viewState.isVisibleState.value = false } } - private fun simpleFields(from: Card): Item { - val group = createGroup(BlockId.Common) + private fun commonGroup(itemList: MutableList, from: Card) { + val group = createGroup(CardId.empty, addHeaderItem = false) + itemList.add(group) + group.addItem(TextItem(CardId.cardId, from.cardId)) group.addItem(TextItem(CardId.manufacturerName, from.manufacturerName)) - group.addItem(TextItem(CardId.status, stringOf(from.status))) + group.addItem(TextItem(CardId.status, valueToString(from.status))) group.addItem(TextItem(CardId.firmwareVersion, from.firmwareVersion)) group.addItem(TextItem(CardId.cardPublicKey, fieldConverter.byteArray(from.cardPublicKey))) group.addItem(TextItem(CardId.issuerPublicKey, fieldConverter.byteArray(from.issuerPublicKey))) - group.addItem(TextItem(CardId.curve, stringOf(from.curve))) - group.addItem(TextItem(CardId.maxSignatures, stringOf(from.maxSignatures))) - group.addItem(TextItem(CardId.signingMethod, fieldConverter.signingMethod(from.signingMethod))) - group.addItem(TextItem(CardId.pauseBeforePin2, stringOf(from.pauseBeforePin2))) + group.addItem(TextItem(CardId.curve, valueToString(from.curve))) + group.addItem(TextItem(CardId.maxSignatures, valueToString(from.maxSignatures))) + group.addItem(TextItem(CardId.pauseBeforePin2, valueToString(from.pauseBeforePin2))) group.addItem(TextItem(CardId.walletPublicKey, fieldConverter.byteArray(from.walletPublicKey))) - group.addItem(TextItem(CardId.walletRemainingSignatures, stringOf(from.walletRemainingSignatures))) - group.addItem(TextItem(CardId.walletSignedHashes, stringOf(from.walletSignedHashes))) - group.addItem(TextItem(CardId.health, stringOf(from.health))) - group.addItem(TextItem(CardId.isActivated, stringOf(from.isActivated))) - group.addItem(TextItem(CardId.activationSeed, stringOf(from.activationSeed))) - group.addItem(TextItem(CardId.paymentFlowVersion, stringOf(from.paymentFlowVersion))) - group.addItem(TextItem(CardId.userCounter, stringOf(from.userCounter))) -// block.addItem(TextItem(CardId.UserProtectedCounter, stringOf(from.userProtectedCounter))) - return group + group.addItem(TextItem(CardId.walletRemainingSignatures, valueToString(from.walletRemainingSignatures))) + group.addItem(TextItem(CardId.walletSignedHashes, valueToString(from.walletSignedHashes))) + group.addItem(TextItem(CardId.health, valueToString(from.health))) + group.addItem(TextItem(CardId.isActivated, valueToString(from.isActivated))) + group.addItem(TextItem(CardId.activationSeed, valueToString(from.activationSeed))) + group.addItem(TextItem(CardId.paymentFlowVersion, valueToString(from.paymentFlowVersion))) + group.addItem(TextItem(CardId.userCounter, valueToString(from.userCounter))) + + val settingsMask = from.settingsMask ?: return + + group.addItem(TextHeaderItem(CardId.signingMethod, "")) + Settings.values().forEach { group.addItem(BoolItem(StringId(it.name), settingsMask.contains(it))) } } - private fun cardData(from: CardData?): Item { - val group = createGroup(BlockId.Common, R.color.group_card_data) - val data = from ?: return group + private fun cardDataGroup(itemList: MutableList, cardData: CardData?) { + val data = cardData ?: return -// group.addItem(TextItem(StringResId(R.string.response_card_card_data))) + val group = createGroup(CardId.cardData, R.color.group_card_data) + itemList.add(group) group.addItem(TextItem(CardDataId.batchId, data.batchId)) -// Format: Year (2 bytes) | Month (1 byte) | Day (1 byte) - group.addItem(TextItem(CardDataId.manufactureDateTime, stringOf(data.manufactureDateTime))) + group.addItem(TextItem(CardDataId.manufactureDateTime, valueToString(data.manufactureDateTime))) group.addItem(TextItem(CardDataId.issuerName, data.issuerName)) group.addItem(TextItem(CardDataId.blockchainName, data.blockchainName)) group.addItem(TextItem(CardDataId.manufacturerSignature, fieldConverter.byteArray(data.manufacturerSignature))) - group.addItem(TextItem(CardDataId.productMask, fieldConverter.productMask(data.productMask))) group.addItem(TextItem(CardDataId.tokenSymbol, data.tokenSymbol)) group.addItem(TextItem(CardDataId.tokenContractAddress, data.tokenContractAddress)) group.addItem(TextItem(CardDataId.tokenDecimal, data.tokenSymbol)) - return group + val productMask = data.productMask ?: return + + group.addItem(TextHeaderItem(CardDataId.productMask, "")) + Product.values().forEach { group.addItem(BoolItem(StringId(it.name), productMask.contains(it))) } } - private fun settingsMask(from: SettingsMask?): Item { + private fun settingsMaskGroup(itemList: MutableList, from: SettingsMask?) { + val data = from ?: return + val group = createGroup(CardId.settingsMask, R.color.group_settings_mask) - val data = from ?: return group + itemList.add(group) Settings.values().forEach { group.addItem(BoolItem(StringId(it.name), data.contains(it))) } - return group - } - - private fun createGroup(id: Id, colorId: Int? = null): ItemGroup { - return if (colorId == null) SimpleItemGroup(id) - else SimpleItemGroup(id, BaseItemViewModel(viewState = ViewState(bgColor = colorId))) } } \ No newline at end of file diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/responses/item/TextItem.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/responses/item/TextItem.kt new file mode 100644 index 0000000000..452b1e47bc --- /dev/null +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/responses/item/TextItem.kt @@ -0,0 +1,12 @@ +package com.tangem.tangemtest.ucase.variants.responses.item + +import com.tangem.tangemtest._arch.structure.Id +import com.tangem.tangemtest._arch.structure.abstraction.BaseItemViewModel +import com.tangem.tangemtest._arch.structure.abstraction.ItemViewModel +import com.tangem.tangemtest._arch.structure.abstraction.ViewState +import com.tangem.tangemtest._arch.structure.impl.TypedItem + +open class TextHeaderItem(id: Id, viewModel: ItemViewModel) : TypedItem(id, viewModel) { + constructor(id: Id, value: String? = null, viewState: ViewState = ViewState()) + : this(id, BaseItemViewModel(value, viewState)) +} \ No newline at end of file diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/responses/ui/widget/ResponseItemBuilder.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/responses/ui/widget/ResponseItemBuilder.kt index 051974e674..ec39879032 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/responses/ui/widget/ResponseItemBuilder.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/responses/ui/widget/ResponseItemBuilder.kt @@ -6,6 +6,7 @@ import com.tangem.tangemtest._arch.structure.impl.BoolItem import com.tangem.tangemtest._arch.structure.impl.TextItem import com.tangem.tangemtest._arch.widget.ItemWidgetBuilder import com.tangem.tangemtest._arch.widget.abstraction.ViewWidget +import com.tangem.tangemtest.ucase.variants.responses.item.TextHeaderItem /** [REDACTED_AUTHOR] @@ -13,6 +14,7 @@ import com.tangem.tangemtest._arch.widget.abstraction.ViewWidget class ResponseItemBuilder : ItemWidgetBuilder { override fun build(item: BaseItem, parent: ViewGroup): ViewWidget? { return when (item) { + is TextHeaderItem -> ResponseHeaderWidget(parent, item) is TextItem -> ResponseTextWidget(parent, item) is BoolItem -> CheckBoxWidget(parent, item) else -> null diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/responses/ui/widget/ResponseTextWidget.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/responses/ui/widget/ResponseTextWidget.kt index 8859d78527..a221c03124 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/responses/ui/widget/ResponseTextWidget.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/responses/ui/widget/ResponseTextWidget.kt @@ -4,6 +4,7 @@ import android.view.ViewGroup import android.widget.TextView import com.tangem.tangemtest.R import com.tangem.tangemtest._arch.structure.impl.TextItem +import com.tangem.tangemtest.ucase.variants.responses.item.TextHeaderItem /** [REDACTED_AUTHOR] @@ -27,4 +28,18 @@ class ResponseTextWidget( tvName.text = getName() tvValue.text = data } +} + +class ResponseHeaderWidget( + parent: ViewGroup, + private val typedItem: TextHeaderItem +) : ResponseWidget(parent, typedItem) { + override fun getLayoutId(): Int = R.layout.w_response_item_header + + private val tvName: TextView = view.findViewById(R.id.tv_name) + + init { + tvName.text = getName() + + } } \ No newline at end of file diff --git a/tangem-demo/src/main/res/layout/w_response_item_header.xml b/tangem-demo/src/main/res/layout/w_response_item_header.xml new file mode 100644 index 0000000000..626c8ba735 --- /dev/null +++ b/tangem-demo/src/main/res/layout/w_response_item_header.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tangem-demo/src/main/res/values/strings_response.xml b/tangem-demo/src/main/res/values/strings_response.xml index 825ce94f41..489e0a7bd5 100644 --- a/tangem-demo/src/main/res/values/strings_response.xml +++ b/tangem-demo/src/main/res/values/strings_response.xml @@ -57,7 +57,7 @@ Current status of the card Version of Tangem COS Public key that is used to authenticate the card against manufacturer’s database. It is generated one time during card manufacturing. See Security section for more details. - Card settings defined by personalization (bit mask: 0 – Enabled, 1 – Disabled): + Card settings defined by personalization. Defines what happens when user calls PURGE_WALLET command: \n0 - Card will switch to Purged state \n1 - Card will switch to Empty state and let create a new wallet again @@ -72,29 +72,29 @@ Card will execute only one command during one communication session, thus requiring user to physically take the card away from the host after each action (all commands except for READ_CARD). Whether the card should emulate NDEF. In default configuration, two NDEF records are loaded during personalization: (1) Tangem web site address, (2) name of Android App package in Google Play Store. 0 – Disable dynamic generation of NDEF for iOS. See Dynamic NDEF section for more details. - /n1 – Enable dynamic NDEF for iOS. + \n1 – Enable dynamic NDEF for iOS. Security delay Pause_Before_PIN2 will not be applied if PIN2 is not default. Whether the card supports unencrypted NFC communication. See NFC communication section for more details. Whether the card supports fast encrypted NFC communication. See NFC communication section for more details. 0 – No replay protection on write issuer data - /n1 – Enable replay protection on write issuer data (card will require additional Issuer_Data_Counter incremented on each write) + \n1 – Enable replay protection on write issuer data (card will require additional Issuer_Data_Counter incremented on each write) 0 – Wallet elliptic curve and blockchain information stored during PERSONALIZE command and never change - /n1 – Wallet elliptic curve and blockchain information can be changed on CREATE_WALLET command + \n1 – Wallet elliptic curve and blockchain information can be changed on CREATE_WALLET command 0 – Enable precomputed dynamic NDEF to work around iPhone 7+ NFC bug. - /n1 – Disable precomputed dynamic NDEF. See Dynamic NDEF section for more details. + \n1 – Disable precomputed dynamic NDEF. See Dynamic NDEF section for more details. 0 – Enforce security delay in SIGN command if the issuer validates the transaction (for signing methods 2, 3, 4 and 5). - /n1 – Skip security delay in SIGN command if the issuer validates the transaction (for signing methods 2, 3, 4 and 5). + \n1 – Skip security delay in SIGN command if the issuer validates the transaction (for signing methods 2, 3, 4 and 5). 0 – Require and check PIN2 and CVC in SIGN command if the issuer validates the transaction (for signing method 2, 3, 4 and 5). - /n1 – Skip checking PIN2 and CVC in SIGN command if the issuer validates the transaction (for signing method 2, 3, 4 and 5). + \n1 – Skip checking PIN2 and CVC in SIGN command if the issuer validates the transaction (for signing method 2, 3, 4 and 5). 1 - Store Terminal_PublicKey public key of linked terminal no each SIGN command, skip security delay if valid signature of transaction is made with Terminal_PrivateKey is provided in SIGN command 0 – Skip checking terminal’s signature when signing POS transaction - /n1 – Check terminal’s signature when signing POS transaction + \n1 – Check terminal’s signature when signing POS transaction 0 – Skip checking acquirer’s signature of terminal certificate when signing POS transaction - /n1 – Check acquirer’s signature of terminal certificate when signing POS transaction + \n1 – Check acquirer’s signature of terminal certificate when signing POS transaction 0 – Additionally encrypt POS transaction signature with key derived from PIN3 when the transaction amount exceeds PIN3 floor limit - /n1 – Require terminal to send PIN3 to card when the POS transaction amount exceeds PIN3_Floor_Limit + \n1 – Require terminal to send PIN3 to card when the POS transaction amount exceeds PIN3_Floor_Limit Detailed information about card contents. Format is defined by the card issuer. Cards complaint with Tangem Wallet application should have TLV format described in Personalization section. Public key that is used by the card issuer to sign Issuer_Data field. See Security section for more details. Explicit text name of the elliptic curve used for all wallet key operations. @@ -106,12 +106,12 @@ Total number of signed single hashes returned by the card in SIGN command responses since card personalization. Sums up array elements within all SIGN commands. Any non-zero value indicates that the card experiences some hardware problems. User should withdraw the value to other blockchain wallet as soon as possible. Non-zero Health tag will also appear in responses of all other commands. Whether the card requires issuer’s confirmation of activation. - /n0 – card will require issuer’s confirmation of activation, - /notherwise this field will not be returned (card is activated and operational). + \n0 – card will require issuer’s confirmation of activation, + \notherwise this field will not be returned (card is activated and operational). A random challenge generated by PERSONALIZE command that should be signed and returned to COS by the issuer to confirm the card has been activated. See ACTIVATE_CARD command for more details. - /nThis field will not be returned if the card is activated. + \nThis field will not be returned if the card is activated. Version of POS payment scheme supported by COS ([0x02,0x01] for version 2.30) - /nReturned only if SigningMethod ‘6’ enabling POS transactions is supported by card. + \nReturned only if SigningMethod ‘6’ enabling POS transactions is supported by card. This value can be initialized by App and will be increased by COS with the execution of each SIGN command. For example, this field can store blockchain “nonce” for a quick one-touch transaction on POS terminals. Returned only if SigningMethod =6. This value can be initialized by App (with PIN2 confirmation) and will be increased by COS with the execution of each SIGN command. For example, this field can store blockchain “nonce” for a quick one-touch transaction on POS terminals. Returned only if SigningMethod =6.