From a4c7731f708210c35d4b688697c74c5ff5a65b2b Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 6 Apr 2020 17:25:38 +0300 Subject: [PATCH 01/39] Updated on 2026-08-14 --- .../ucase/resources/initializers/PersonalizeResources.kt | 6 ++++-- .../com/tangem/tangemtest/ucase/variants/personalize/Ids.kt | 3 ++- .../ucase/variants/personalize/converter/DefaultValues.kt | 1 + .../variants/personalize/converter/fromTo/ItemTypes.kt | 4 ++-- .../converter/fromTo/ItemsToPersonalizeConfig.kt | 1 + .../personalize/converter/fromTo/PersonalizeConfigTo.kt | 6 +++++- .../ucase/variants/personalize/dto/PersonalizeConfig.kt | 1 + tangem-demo/src/main/res/values/strings_personalization.xml | 2 ++ 8 files changed, 18 insertions(+), 6 deletions(-) diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/resources/initializers/PersonalizeResources.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/resources/initializers/PersonalizeResources.kt index 1413ede425..e182c60e38 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/resources/initializers/PersonalizeResources.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/resources/initializers/PersonalizeResources.kt @@ -22,7 +22,7 @@ class PersonalizeResources() { initProductMask(holder) initSettingsMask(holder) initSettingsMaskProtocolEnc(holder) - initSettingsMaskNde(holder) + initSettingsMaskNdef(holder) initPins(holder) } @@ -115,11 +115,13 @@ class PersonalizeResources() { 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) { + private fun initSettingsMaskNdef(holder: TypedHolder) { holder.register(SettingsMaskNdef.UseNdef, Resources(R.string.pers_item_use_ndef, R.string.info_pers_item_use_ndef)) holder.register(SettingsMaskNdef.DynamicNdef, Resources(R.string.pers_item_dynamic_ndef, R.string.info_pers_item_dynamic_ndef)) holder.register(SettingsMaskNdef.DisablePrecomputedNdef, Resources(R.string.pers_item_disable_precomputed_ndef, R.string.info_pers_item_disable_precomputed_ndef)) holder.register(SettingsMaskNdef.Aar, Resources(R.string.pers_item_aar, R.string.info_pers_item_aar)) + holder.register(SettingsMaskNdef.AarCustom, Resources(R.string.pers_item_custom_aar_package_name, R.string.info_pers_item_custom_aar_package_name)) + holder.register(SettingsMaskNdef.Uri, Resources(R.string.pers_item_uri, R.string.info_pers_item_uri)) } private fun initPins(holder: TypedHolder) { diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/Ids.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/Ids.kt index b0cf3f78de..176af027b3 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/Ids.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/Ids.kt @@ -104,7 +104,8 @@ enum class SettingsMaskNdef : BlockItem { DynamicNdef, DisablePrecomputedNdef, Aar, - AarCustom + AarCustom, + Uri, } enum class Pins : BlockItem { diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/converter/DefaultValues.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/converter/DefaultValues.kt index 9fac214af2..d40e50b17e 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/converter/DefaultValues.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/converter/DefaultValues.kt @@ -67,6 +67,7 @@ class PersonalizeConfigValuesHolder : BaseTypedHolder() { register(SettingsMaskNdef.DisablePrecomputedNdef, Value(default.disablePrecomputedNDEF)) register(SettingsMaskNdef.Aar, Value(default.aar, Helper.aarList())) register(SettingsMaskNdef.AarCustom, Value(default.aar)) + register(SettingsMaskNdef.Uri, Value(default.uri)) register(Pins.Pin, Value(default.PIN)) register(Pins.Pin2, Value(default.PIN2)) register(Pins.Pin3, Value(default.PIN3)) diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/converter/fromTo/ItemTypes.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/converter/fromTo/ItemTypes.kt index 4c6b277667..87454c9244 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/converter/fromTo/ItemTypes.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/converter/fromTo/ItemTypes.kt @@ -17,7 +17,7 @@ class ItemTypes { SigningMethod.SignValidatedTxRaw, SigningMethod.SignValidatedTxIssuer, SigningMethod.SignValidatedTxRawIssuer, SigningMethod.SignExternal, SignHashExProp.RequireTerminalCertSig, SignHashExProp.RequireTerminalTxSig, SignHashExProp.CheckPin3, Denomination.WriteOnPersonalize, Token.ItsToken, - ProductMask.Note, ProductMask.Tag, ProductMask.CardId, ProductMask.IssuerId,SettingsMask.IsReusable, SettingsMask.NeedActivation, + ProductMask.Note, ProductMask.Tag, ProductMask.CardId, ProductMask.IssuerId, SettingsMask.IsReusable, SettingsMask.NeedActivation, SettingsMask.ForbidPurge, SettingsMask.AllowSelectBlockchain, SettingsMask.UseBlock, SettingsMask.OneApdu, SettingsMask.UseCvc, SettingsMask.AllowSwapPin, SettingsMask.AllowSwapPin2, SettingsMask.ForbidDefaultPin, SettingsMask.SmartSecurityDelay, SettingsMask.ProtectIssuerDataAgainstReplay, @@ -29,7 +29,7 @@ class ItemTypes { 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 + Token.ContractAddress, Pins.Pin, Pins.Pin2, Pins.Pin3, Pins.Cvc, SettingsMaskNdef.AarCustom, SettingsMaskNdef.Uri ) val numberList = mutableListOf( diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/converter/fromTo/ItemsToPersonalizeConfig.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/converter/fromTo/ItemsToPersonalizeConfig.kt index d6ea20bf06..8ba83342ce 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/converter/fromTo/ItemsToPersonalizeConfig.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/converter/fromTo/ItemsToPersonalizeConfig.kt @@ -92,6 +92,7 @@ class ItemsToPersonalizeConfig : ItemsToModel { export.disablePrecomputedNDEF = getTyped(SettingsMaskNdef.DisablePrecomputedNdef) export.aar = getTyped(SettingsMaskNdef.Aar) export.aarCustom = getTyped(SettingsMaskNdef.AarCustom) + export.uri = getTyped(SettingsMaskNdef.Uri) export.PIN = getTyped(Pins.Pin) export.PIN2 = getTyped(Pins.Pin2) export.PIN3 = getTyped(Pins.Pin3) diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/converter/fromTo/PersonalizeConfigTo.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/converter/fromTo/PersonalizeConfigTo.kt index 73baa0b078..64b26930ef 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/converter/fromTo/PersonalizeConfigTo.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/converter/fromTo/PersonalizeConfigTo.kt @@ -166,7 +166,8 @@ class PersonalizeConfigToItem : ModelToItems { SettingsMaskNdef.DynamicNdef, SettingsMaskNdef.DisablePrecomputedNdef, SettingsMaskNdef.Aar, - SettingsMaskNdef.AarCustom + SettingsMaskNdef.AarCustom, + SettingsMaskNdef.Uri ).forEach { createItem(block, it) } return block } @@ -248,6 +249,9 @@ class PersonalizeConfigToCardConfig : Converter { val ndefs = mutableListOf() + if (from.uri.isNotEmpty()) { + ndefs.add(NdefRecord(NdefRecord.Type.URI, from.uri)) + } when (from.aar) { "None" -> null "--- CUSTOM ---" -> NdefRecord(NdefRecord.Type.AAR, from.aarCustom) diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/dto/PersonalizeConfig.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/dto/PersonalizeConfig.kt index 0ebc993776..bc855da8b1 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/dto/PersonalizeConfig.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/dto/PersonalizeConfig.kt @@ -80,6 +80,7 @@ class PersonalizeConfig { var disablePrecomputedNDEF = false var aar = "com.tangem.wallet" var aarCustom = "" + var uri = "https://tangem.com" // Pins diff --git a/tangem-demo/src/main/res/values/strings_personalization.xml b/tangem-demo/src/main/res/values/strings_personalization.xml index 6ae2c26d5b..c17a65c936 100644 --- a/tangem-demo/src/main/res/values/strings_personalization.xml +++ b/tangem-demo/src/main/res/values/strings_personalization.xml @@ -68,6 +68,7 @@ Disable precomputed NDEF AAR Custom AAR package name + Url PIN PIN2 PIN3 @@ -155,6 +156,7 @@ Android Application Record. Android Application ID, Package name of Android apllication. Android OS will open this app or send user to Google Play page if it\'s not installed. If this field is empty, Android OS will try to open URI from URI field. + PIN1 code to access the card. Default unhashed value: ‘000000’. PIN2 code to confirm transaction signatures and swap PIN operations. Default unhashed value: ‘000’. PIN3 code to confirm POS transaction with amount that exceed pin-less floor limit. From 72616738fe50c6250d5350efc7fda743d4a11245 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 7 Apr 2020 16:13:02 +0300 Subject: [PATCH 02/39] Updated on 2026-08-14 --- .../_arch/widget/ItemWidgetBuilder.kt | 12 +++ .../tangemtest/_arch/widget/WidgetBuilder.kt | 37 +++++++ .../widget}/abstraction/ViewWidget.kt | 2 +- .../widget/impl}/LinearBlockWidget.kt | 14 ++- .../widget}/impl/StubWidget.kt | 4 +- .../_main/entryPoint/ActionListFragment.kt | 20 ++-- .../triggers/afterAction/AfterScanModifier.kt | 4 +- .../ParamChangeConsequence.kt | 2 +- .../tangemtest/ucase/tunnel/ViewScreen.kt | 15 +-- .../ucase/ui/BaseCardActionFragment.kt | 42 +++----- .../ucase/ui/BaseCardResponseFragment.kt | 58 +++++++++++ .../tangemtest/ucase/ui/BaseFragment.kt | 38 ++++++++ .../tangemtest/ucase/ui/ParamsViewModel.kt | 17 ++-- .../ucase/ui/ResponseItemBuilder.kt | 20 ++++ .../ucase/ui/widgets/ResponseTextWidget.kt | 34 +++++++ .../personalize/ui/PersonalizeFragment.kt | 96 +++++++++++-------- .../item/TextWidget.kt => BlockHeadWidget.kt} | 6 +- .../{impl/item => }/DescriptionWidget.kt | 6 +- .../widgets/{impl/item => }/EditTextWidget.kt | 4 +- .../widgets/{impl/item => }/NumberWidget.kt | 4 +- .../ui/widgets/PersonalizeItemBuilder.kt | 23 +++++ .../widgets/{impl/item => }/SpinnerWidget.kt | 4 +- .../widgets/{impl/item => }/SwitchWidget.kt | 4 +- .../personalize/ui/widgets/WidgetBuilder.kt | 48 ---------- .../src/main/res/layout/fg_card_response.xml | 22 +++++ .../src/main/res/layout/w_response_item.xml | 34 +++++++ .../src/main/res/navigation/navigation.xml | 10 ++ 27 files changed, 406 insertions(+), 174 deletions(-) create mode 100644 tangem-demo/src/main/java/com/tangem/tangemtest/_arch/widget/ItemWidgetBuilder.kt create mode 100644 tangem-demo/src/main/java/com/tangem/tangemtest/_arch/widget/WidgetBuilder.kt rename tangem-demo/src/main/java/com/tangem/tangemtest/{ucase/variants/personalize/ui/widgets => _arch/widget}/abstraction/ViewWidget.kt (95%) rename tangem-demo/src/main/java/com/tangem/tangemtest/{ucase/variants/personalize/ui/widgets/impl/block => _arch/widget/impl}/LinearBlockWidget.kt (52%) rename tangem-demo/src/main/java/com/tangem/tangemtest/{ucase/variants/personalize/ui/widgets => _arch/widget}/impl/StubWidget.kt (67%) create mode 100644 tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/BaseCardResponseFragment.kt create mode 100644 tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/BaseFragment.kt create mode 100644 tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/ResponseItemBuilder.kt create mode 100644 tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/widgets/ResponseTextWidget.kt rename tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/{impl/item/TextWidget.kt => BlockHeadWidget.kt} (68%) rename tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/{impl/item => }/DescriptionWidget.kt (87%) rename tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/{impl/item => }/EditTextWidget.kt (94%) rename tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/{impl/item => }/NumberWidget.kt (95%) create mode 100644 tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/PersonalizeItemBuilder.kt rename tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/{impl/item => }/SpinnerWidget.kt (96%) rename tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/{impl/item => }/SwitchWidget.kt (91%) delete mode 100644 tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/WidgetBuilder.kt create mode 100644 tangem-demo/src/main/res/layout/fg_card_response.xml create mode 100644 tangem-demo/src/main/res/layout/w_response_item.xml diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/widget/ItemWidgetBuilder.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/widget/ItemWidgetBuilder.kt new file mode 100644 index 0000000000..b507573e99 --- /dev/null +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/widget/ItemWidgetBuilder.kt @@ -0,0 +1,12 @@ +package com.tangem.tangemtest._arch.widget + +import android.view.ViewGroup +import com.tangem.tangemtest._arch.structure.abstraction.BaseItem +import com.tangem.tangemtest._arch.widget.abstraction.ViewWidget + +/** +[REDACTED_AUTHOR] + */ +interface ItemWidgetBuilder { + fun build(item: BaseItem<*>, parent: ViewGroup): ViewWidget? +} \ No newline at end of file diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/widget/WidgetBuilder.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/widget/WidgetBuilder.kt new file mode 100644 index 0000000000..a524006f74 --- /dev/null +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/widget/WidgetBuilder.kt @@ -0,0 +1,37 @@ +package com.tangem.tangemtest._arch.widget + +import android.view.ViewGroup +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.ListItemBlock +import com.tangem.tangemtest._arch.widget.abstraction.ViewWidget +import com.tangem.tangemtest._arch.widget.impl.LinearBlockWidget +import com.tangem.tangemtest._arch.widget.impl.StubWidget + +/** +[REDACTED_AUTHOR] + */ +class WidgetBuilder( + private val itemBuilder: ItemWidgetBuilder +) { + + fun build(item: Item, parent: ViewGroup): ViewWidget? { + return when (item) { + is Block -> buildBlock(item, parent) + is BaseItem<*> -> itemBuilder.build(item, parent) + else -> StubWidget(parent) + } + } + + private fun buildBlock(block: Block, parent: ViewGroup): ViewWidget { + return when (block) { + is ListItemBlock -> { + val linearBlock = LinearBlockWidget(parent, block) + block.getItems().forEach { build(it, linearBlock.view as ViewGroup) } + linearBlock + } + else -> StubWidget(parent) + } + } +} \ No newline at end of file diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/abstraction/ViewWidget.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/widget/abstraction/ViewWidget.kt similarity index 95% rename from tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/abstraction/ViewWidget.kt rename to tangem-demo/src/main/java/com/tangem/tangemtest/_arch/widget/abstraction/ViewWidget.kt index 7926c4a0cf..b3ca53c3a0 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/abstraction/ViewWidget.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/widget/abstraction/ViewWidget.kt @@ -1,4 +1,4 @@ -package com.tangem.tangemtest.ucase.variants.personalize.ui.widgets.abstraction +package com.tangem.tangemtest._arch.widget.abstraction import android.view.LayoutInflater import android.view.View diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/impl/block/LinearBlockWidget.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/widget/impl/LinearBlockWidget.kt similarity index 52% rename from tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/impl/block/LinearBlockWidget.kt rename to tangem-demo/src/main/java/com/tangem/tangemtest/_arch/widget/impl/LinearBlockWidget.kt index fb4be30444..5f1d268c6f 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/impl/block/LinearBlockWidget.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/widget/impl/LinearBlockWidget.kt @@ -1,11 +1,11 @@ -package com.tangem.tangemtest.ucase.variants.personalize.ui.widgets.impl.block +package com.tangem.tangemtest._arch.widget.impl import android.view.ViewGroup import com.tangem.tangemtest.R -import com.tangem.tangemtest._arch.structure.abstraction.BaseItem import com.tangem.tangemtest._arch.structure.abstraction.ItemViewModel import com.tangem.tangemtest._arch.structure.abstraction.ListItemBlock -import com.tangem.tangemtest.ucase.variants.personalize.ui.widgets.abstraction.BaseBlockWidget +import com.tangem.tangemtest._arch.structure.abstraction.iterate +import com.tangem.tangemtest._arch.widget.abstraction.BaseBlockWidget /** [REDACTED_AUTHOR] @@ -17,5 +17,11 @@ class LinearBlockWidget( override fun getLayoutId(): Int = R.layout.w_personilize_block override var viewModel: List> = listOf() - get() = children.itemList.map { it as BaseItem<*> }.map { it.viewModel } + get() { + val vmList = mutableListOf>() + children.itemList.iterate { item -> + (item as? ItemViewModel<*>)?.let { vmList.add(it) } + } + return vmList.toList() + } } \ No newline at end of file diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/impl/StubWidget.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/widget/impl/StubWidget.kt similarity index 67% rename from tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/impl/StubWidget.kt rename to tangem-demo/src/main/java/com/tangem/tangemtest/_arch/widget/impl/StubWidget.kt index a087c6e0cb..98e2bcd57a 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/impl/StubWidget.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/widget/impl/StubWidget.kt @@ -1,9 +1,9 @@ -package com.tangem.tangemtest.ucase.variants.personalize.ui.widgets.impl +package com.tangem.tangemtest._arch.widget.impl import android.view.ViewGroup import com.tangem.tangemtest.R import com.tangem.tangemtest._arch.structure.abstraction.ItemViewModel -import com.tangem.tangemtest.ucase.variants.personalize.ui.widgets.abstraction.BaseBlockWidget +import com.tangem.tangemtest._arch.widget.abstraction.BaseBlockWidget /** [REDACTED_AUTHOR] diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/_main/entryPoint/ActionListFragment.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/_main/entryPoint/ActionListFragment.kt index 313c4aaffe..ea17194439 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/_main/entryPoint/ActionListFragment.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/_main/entryPoint/ActionListFragment.kt @@ -1,14 +1,10 @@ package com.tangem.tangemtest._main.entryPoint import android.os.Bundle -import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels import androidx.lifecycle.Observer -import androidx.navigation.NavController -import androidx.navigation.fragment.findNavController import androidx.recyclerview.widget.DividerItemDecoration import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView @@ -19,21 +15,19 @@ import com.tangem.tangemtest._main.MainViewModel import com.tangem.tangemtest.ucase.getDefaultNavigationOptions import com.tangem.tangemtest.ucase.resources.ActionType import com.tangem.tangemtest.ucase.resources.MainResourceHolder +import com.tangem.tangemtest.ucase.ui.BaseFragment import kotlinx.android.synthetic.main.fg_entry_point.* /** [REDACTED_AUTHOR] */ -class ActionListFragment : Fragment() { +class ActionListFragment : BaseFragment() { - private val navController: NavController by lazy { findNavController() } private lateinit var rvActions: RecyclerView private val mainActivityVM: MainViewModel by activityViewModels() - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - return inflater.inflate(R.layout.fg_entry_point, container, false) - } + override fun getLayoutId(): Int = R.layout.fg_entry_point override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) @@ -48,7 +42,9 @@ class ActionListFragment : Fragment() { val vhDataWrapper = VhExDataWrapper(MainResourceHolder, false) - rvActions.adapter = RvActionsAdapter(vhDataWrapper) { type, position, data -> navigate(data) } + rvActions.adapter = RvActionsAdapter(vhDataWrapper) { type, position, data -> + navigateTo(data, options = getDefaultNavigationOptions()) + } mainActivityVM.ldDescriptionSwitch.observe(viewLifecycleOwner, Observer { vhDataWrapper.descriptionIsVisible = it TransitionManager.beginDelayedTransition(rvActions as ViewGroup, AutoTransition()) @@ -64,10 +60,6 @@ class ActionListFragment : Fragment() { adapter.notifyDataSetChanged() } - private fun navigate(destinationId: Int) { - navController.navigate(destinationId, null, getDefaultNavigationOptions()) - } - private fun getNavigateOptions(): MutableList { return mutableListOf( ActionType.Scan, diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/triggers/afterAction/AfterScanModifier.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/triggers/afterAction/AfterScanModifier.kt index b75c721424..990f2a78f4 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/triggers/afterAction/AfterScanModifier.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/triggers/afterAction/AfterScanModifier.kt @@ -24,12 +24,12 @@ class AfterScanModifier : AfterActionModification { return if (isNotPersonalized(card)) { actionView.showSnackbar(CardError.NotPersonalized) - postUI { actionView.showActionFab(false) } + postUI { actionView.enableActionFab(false) } listOf() } else { payload.set(PayloadKey.card, card) foundItem.setData(card.cardId) - postUI { actionView.showActionFab(true) } + postUI { actionView.enableActionFab(true) } listOf(foundItem) } } diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/triggers/changeConsequence/ParamChangeConsequence.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/triggers/changeConsequence/ParamChangeConsequence.kt index 2957d43a45..5f61c31c34 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/triggers/changeConsequence/ParamChangeConsequence.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/triggers/changeConsequence/ParamChangeConsequence.kt @@ -32,7 +32,7 @@ class SignScanConsequence: ItemsChangeConsequence { val card = payload.remove(PayloadKey.card) as Card? if (card == null) { hashItem.restoreDefaultData() - postUI { (payload.get(PayloadKey.actionView) as? ActionView)?.showActionFab(false) } + postUI { (payload.get(PayloadKey.actionView) as? ActionView)?.enableActionFab(false) } } else { val dataForHashing = hashItem.getData() as? String ?: "Any data mother...s" val hashedData = when(card.curve) { diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/tunnel/ViewScreen.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/tunnel/ViewScreen.kt index ec84ed97ba..a22a6b14ae 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/tunnel/ViewScreen.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/tunnel/ViewScreen.kt @@ -1,15 +1,16 @@ package com.tangem.tangemtest.ucase.tunnel +import com.google.android.material.snackbar.Snackbar import com.tangem.tangemtest._arch.structure.Id +interface SnackbarHolder { + fun showSnackbar(message: String, length: Int = Snackbar.LENGTH_SHORT) + fun showSnackbar(id: Int, length: Int = Snackbar.LENGTH_SHORT) +} + interface ViewScreen -interface SnackbarHolder { - fun showSnackbar(message: String) - fun showSnackbar(id: Int) - fun showSnackbar(id: Id) -} - interface ActionView : ViewScreen, SnackbarHolder { - fun showActionFab(show: Boolean) + fun enableActionFab(enable: Boolean) + fun showSnackbar(id: Id) } \ No newline at end of file diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/BaseCardActionFragment.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/BaseCardActionFragment.kt index d44d2c998d..6871b38bbb 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/BaseCardActionFragment.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/BaseCardActionFragment.kt @@ -5,14 +5,11 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.LinearLayout -import android.widget.TextView -import androidx.fragment.app.Fragment +import androidx.core.os.bundleOf 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 @@ -26,18 +23,18 @@ 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.ui.widgets.ParameterWidget +import ru.dev.gbixahue.eu4d.lib.android._android.views.enable import ru.dev.gbixahue.eu4d.lib.android.global.log.Log import ru.dev.gbixahue.eu4d.lib.kotlin.common.LayoutHolder /** [REDACTED_AUTHOR] */ -abstract class BaseCardActionFragment : Fragment(), LayoutHolder, ActionView { +abstract class BaseCardActionFragment : BaseFragment(), LayoutHolder, ActionView { protected val incomingParamsContainer: ViewGroup by lazy { mainView.findViewById(R.id.ll_incoming_params_container) } - protected lateinit var mainView: View protected val actionFab: FloatingActionButton by lazy { mainView.findViewById(R.id.fab_action) } protected val paramsVM: ParamsViewModel by viewModels { ActionViewModelFactory(itemsManager) } @@ -45,12 +42,6 @@ abstract class BaseCardActionFragment : Fragment(), LayoutHolder, ActionView { private val itemsManager: ItemsManager by lazy { ParamsManagerFactory.createFactory().get(getAction())!! } - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - Log.d(this, "onCreateView") - mainView = inflater.inflate(getLayoutId(), container, false) - return mainView - } - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) Log.d(this, "onViewCreated") @@ -59,18 +50,17 @@ abstract class BaseCardActionFragment : Fragment(), LayoutHolder, ActionView { paramsVM.setCardManager(CardManager.init(requireActivity())) paramsVM.attachToPayload(mutableMapOf(PayloadKey.actionView to this as ActionView)) initFab() - showActionFab(false) createWidgets { subscribeToViewModelChanges() } } private fun initFab() { - Log.d(this, "initFab") + enableActionFab(false) actionFab.setOnClickListener { paramsVM.invokeMainAction() } } private fun createWidgets(widgetCreatedCallback: () -> Unit) { Log.d(this, "createWidgets") - paramsVM.ldParams.observe(viewLifecycleOwner, Observer { itemList -> + paramsVM.ldItemList.observe(viewLifecycleOwner, Observer { itemList -> itemList.forEach { param -> val widget = ParameterWidget(inflateParamView(incomingParamsContainer), param) widget.onValueChanged = { id, value -> paramsVM.userChangedItem(id, value) } @@ -83,17 +73,19 @@ abstract class BaseCardActionFragment : Fragment(), LayoutHolder, ActionView { private fun subscribeToViewModelChanges() { Log.d(this, "subscribeToViewModelChanges") + listenReadResponse() listenResponse() listenError() listenChangedItems() listenDescriptionSwitchChanges() } + protected open fun listenReadResponse() {} + protected open fun listenResponse() { - val tvResponse by lazy { mainView.findViewById(R.id.tv_action_response_json) } - paramsVM.ldResponse.observe(viewLifecycleOwner, Observer { - Log.d(this, "action response: ${if (it.length > 50) it.substring(0..50) else it}") - tvResponse.text = it + paramsVM.seResponse.observe(viewLifecycleOwner, Observer { + navigateTo(R.id.action_nav_card_action_to_response_screen, + bundleOf(Pair(BaseCardResponseFragment.response, it))) }) } @@ -126,8 +118,8 @@ abstract class BaseCardActionFragment : Fragment(), LayoutHolder, ActionView { return view as ViewGroup } - override fun showActionFab(show: Boolean) { - if (show) actionFab.show() else actionFab.hide() + override fun enableActionFab(enable: Boolean) { + actionFab.enable(enable) } override fun showSnackbar(id: Id) { @@ -138,13 +130,5 @@ abstract class BaseCardActionFragment : Fragment(), LayoutHolder, ActionView { } } - override fun showSnackbar(id: Int) { - showSnackbar(requireContext().getString(id)) - } - - override fun showSnackbar(message: String) { - Snackbar.make(mainView, message, BaseTransientBottomBar.LENGTH_SHORT).show() - } - abstract fun getAction(): ActionType } \ No newline at end of file diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/BaseCardResponseFragment.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/BaseCardResponseFragment.kt new file mode 100644 index 0000000000..51d5e0866f --- /dev/null +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/BaseCardResponseFragment.kt @@ -0,0 +1,58 @@ +package com.tangem.tangemtest.ucase.ui + +import android.os.Bundle +import android.view.View +import android.view.ViewGroup +import android.widget.LinearLayout +import androidx.fragment.app.activityViewModels +import androidx.lifecycle.Observer +import com.tangem.tangemtest.R +import com.tangem.tangemtest._arch.structure.abstraction.BaseItem +import com.tangem.tangemtest._arch.structure.abstraction.Item +import com.tangem.tangemtest._arch.structure.abstraction.ListItemBlock +import com.tangem.tangemtest._arch.structure.abstraction.iterate +import com.tangem.tangemtest._arch.structure.impl.TextItem +import com.tangem.tangemtest._arch.widget.WidgetBuilder +import com.tangem.tangemtest._main.MainViewModel +import com.tangem.tangemtest.ucase.resources.StringId + +/** +[REDACTED_AUTHOR] + */ +class BaseCardResponseFragment : BaseFragment() { + + companion object Arg { + val response = "response" + } + + private val mainActivityVM: MainViewModel by activityViewModels() + private val blockContainer: ViewGroup by lazy { mainView.findViewById(R.id.ll_container) } + + override fun getLayoutId(): Int = R.layout.fg_card_response + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + val response = requireArguments().getString(Arg.response) ?: return + if (response.isEmpty()) return + + val itemList = createItemList(response) + itemList.forEach { WidgetBuilder(ResponseItemBuilder()).build(it, blockContainer) } + + mainActivityVM.ldDescriptionSwitch.observe(viewLifecycleOwner, Observer { + itemList.iterate { item -> + val baseItem = item as? BaseItem<*> ?: return@iterate + baseItem.viewModel.viewState.descriptionVisibility = if (it) View.VISIBLE else View.GONE + } + }) + + } + + protected open fun createItemList(response: String): List { + return listOf( + ListItemBlock(StringId("")).apply { + addItem(TextItem(StringId("resp.fieldName"), response)) + } + ) + } +} diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/BaseFragment.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/BaseFragment.kt new file mode 100644 index 0000000000..29dfeec6be --- /dev/null +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/BaseFragment.kt @@ -0,0 +1,38 @@ +package com.tangem.tangemtest.ucase.ui + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.Fragment +import androidx.navigation.NavOptions +import androidx.navigation.fragment.NavHostFragment.findNavController +import com.google.android.material.snackbar.Snackbar +import com.tangem.tangemtest.ucase.getDefaultNavigationOptions +import com.tangem.tangemtest.ucase.tunnel.SnackbarHolder +import ru.dev.gbixahue.eu4d.lib.kotlin.common.LayoutHolder + +/** +[REDACTED_AUTHOR] + */ +abstract class BaseFragment : Fragment(), LayoutHolder, SnackbarHolder { + + protected lateinit var mainView: View + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + mainView = inflater.inflate(getLayoutId(), container, false) + return mainView + } + + protected fun navigateTo(navigationId: Int, bundle: Bundle? = null, options: NavOptions? = getDefaultNavigationOptions()) { + findNavController(this).navigate(navigationId, bundle, options) + } + + override fun showSnackbar(id: Int, length: Int) { + showSnackbar(requireContext().getString(id), length) + } + + override fun showSnackbar(message: String, length: Int) { + Snackbar.make(mainView, message, length).show() + } +} \ No newline at end of file diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/ParamsViewModel.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/ParamsViewModel.kt index 2df743786e..94b4d47a3f 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/ParamsViewModel.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/ParamsViewModel.kt @@ -4,7 +4,6 @@ import androidx.annotation.UiThread import androidx.lifecycle.* import com.google.gson.Gson import com.tangem.CardManager -import com.tangem.commands.Card import com.tangem.tangemtest._arch.SingleLiveEvent import com.tangem.tangemtest._arch.structure.Id import com.tangem.tangemtest._arch.structure.Payload @@ -27,12 +26,10 @@ class ActionViewModelFactory(private val manager: ItemsManager) : ViewModelProvi class ParamsViewModel(private val itemsManager: ItemsManager) : ViewModel(), LifecycleObserver { - val ldCard = MutableLiveData() - val ldIsVerified = MutableLiveData() - val ldResponse = MutableLiveData() - val ldReadResponse = MutableLiveData() - val ldParams = MutableLiveData(itemsManager.getItems()) + val seReadResponse = SingleLiveEvent() + val seResponse = SingleLiveEvent() + val ldItemList = MutableLiveData(itemsManager.getItems()) val seError: MutableLiveData = SingleLiveEvent() val seChangedItems: MutableLiveData> = SingleLiveEvent() @@ -109,13 +106,13 @@ internal class Notifier(private val vm: ParamsViewModel) { private fun handleDataEvent(event: Any?) { when (event) { is ScanEvent.OnReadEvent -> { - vm.ldCard.postValue(event.card) - vm.ldReadResponse.postValue(gson.toJson(event)) +// vm.ldCard.postValue(event.card) + vm.seReadResponse.postValue(gson.toJson(event)) } is ScanEvent.OnVerifyEvent -> { - vm.ldIsVerified.postValue(true) +// vm.ldIsVerified.postValue(true) } - else -> vm.ldResponse.postValue(gson.toJson(event)) + else -> vm.seResponse.postValue(gson.toJson(event)) } } diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/ResponseItemBuilder.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/ResponseItemBuilder.kt new file mode 100644 index 0000000000..dd80c57862 --- /dev/null +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/ResponseItemBuilder.kt @@ -0,0 +1,20 @@ +package com.tangem.tangemtest.ucase.ui + +import android.view.ViewGroup +import com.tangem.tangemtest._arch.structure.abstraction.BaseItem +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.ui.widgets.ResponseTextWidget + +/** +[REDACTED_AUTHOR] + */ +class ResponseItemBuilder : ItemWidgetBuilder { + override fun build(item: BaseItem<*>, parent: ViewGroup): ViewWidget? { + return when (item) { + is TextItem -> ResponseTextWidget(parent, item) + else -> null + } + } +} \ No newline at end of file diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/widgets/ResponseTextWidget.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/widgets/ResponseTextWidget.kt new file mode 100644 index 0000000000..fde72c3d54 --- /dev/null +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/widgets/ResponseTextWidget.kt @@ -0,0 +1,34 @@ +package com.tangem.tangemtest.ucase.ui.widgets + +import android.content.ClipData +import android.content.ClipboardManager +import android.content.Context +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._arch.widget.abstraction.getResNameId +import com.tangem.tangemtest.ucase.variants.personalize.ui.widgets.DescriptionWidget + +/** +[REDACTED_AUTHOR] + */ +class ResponseTextWidget(parent: ViewGroup, textItem: TextItem) : DescriptionWidget(parent, textItem) { + override fun getLayoutId(): Int = R.layout.w_response_item + + private val tvName: TextView by lazy { view.findViewById(R.id.tv_name) } + private val tvValue: TextView by lazy { view.findViewById(R.id.tv_value) } + + init { + tvName.setText(getResNameId()) + tvValue.text = dataItem.getData() + + view.setOnClickListener { + val clipboard = view.context.getSystemService(Context.CLIPBOARD_SERVICE) as? ClipboardManager + ?: return@setOnClickListener + + val clip: ClipData = ClipData.newPlainText("FieldValue", "${dataItem.getData()}") + clipboard.setPrimaryClip(clip) + } + } +} \ No newline at end of file diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/PersonalizeFragment.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/PersonalizeFragment.kt index 3456d91ad2..d206e1f3d1 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/PersonalizeFragment.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/PersonalizeFragment.kt @@ -1,22 +1,20 @@ package com.tangem.tangemtest.ucase.variants.personalize.ui import android.os.Bundle -import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.LinearLayout -import androidx.fragment.app.Fragment +import androidx.core.os.bundleOf 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.Id import com.tangem.tangemtest._arch.structure.abstraction.Item +import com.tangem.tangemtest._arch.widget.WidgetBuilder import com.tangem.tangemtest._main.MainViewModel import com.tangem.tangemtest.ucase.domain.paramsManager.ItemsManager import com.tangem.tangemtest.ucase.domain.paramsManager.ParamsManagerFactory @@ -26,67 +24,89 @@ 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.BaseCardResponseFragment +import com.tangem.tangemtest.ucase.ui.BaseFragment 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 com.tangem.tangemtest.ucase.variants.personalize.ui.widgets.PersonalizeItemBuilder import ru.dev.gbixahue.eu4d.lib.android.global.log.Log /** [REDACTED_AUTHOR] */ -class PersonalizeFragment : Fragment(), ActionView { +class PersonalizeFragment : BaseFragment(), ActionView { private val mainActivityVM: MainViewModel by activityViewModels() private val personalizeVM: PersonalizeViewModel by viewModels { PersonalizeViewModelFactory(PersonalizeConfig()) } - - private val itemsManager: ItemsManager by lazy { ParamsManagerFactory.createFactory().get(ActionType.Personalize)!! } private val paramsVM: ParamsViewModel by viewModels() { ActionViewModelFactory(itemsManager) } - private lateinit var mainView: View private val blockContainer: ViewGroup by lazy { mainView.findViewById(R.id.ll_container) } private val actionFab: FloatingActionButton by lazy { mainView.findViewById(R.id.fab_action) } - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - Log.d(this, "onCreateView") - mainView = inflater.inflate(R.layout.fg_personalize, container, false) - return mainView - } + private val itemsManager: ItemsManager by lazy { ParamsManagerFactory.createFactory().get(ActionType.Personalize)!! } + + override fun getLayoutId(): Int = R.layout.fg_personalize override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + paramsVM.setCardManager(CardManager.init(requireActivity())) + paramsVM.attachToPayload(mutableMapOf(PayloadKey.actionView to this as ActionView)) + + initFab() + createWidgets { subscribeToViewModelChanges() } + } + + private fun initFab() { + enableActionFab(true) + actionFab.setOnClickListener { paramsVM.invokeMainAction() } + } + + private fun createWidgets(widgetCreatedCallback: () -> Unit) { val blockList = mutableListOf() personalizeVM.ldBlockList.observe(viewLifecycleOwner, Observer { list -> blockList.clear() blockList.addAll(list) - blockList.forEach { WidgetBuilder().build(it, blockContainer) } + blockList.forEach { WidgetBuilder(PersonalizeItemBuilder()).build(it, blockContainer) } paramsVM.attachToPayload(mutableMapOf( PayloadKey.actionView to this as ActionView, PayloadKey.itemList to blockList )) - + widgetCreatedCallback() }) + } + + override fun enableActionFab(enable: Boolean) { + if (enable) actionFab.show() else actionFab.hide() + } + + private fun subscribeToViewModelChanges() { + Log.d(this, "subscribeToViewModelChanges") + listenReadResponse() + listenResponse() + listenError() +// listenChangedItems() + listenDescriptionSwitchChanges() + } + + private fun listenReadResponse() {} + + private fun listenResponse() { + paramsVM.seResponse.observe(viewLifecycleOwner, Observer { + navigateTo(R.id.action_nav_card_action_to_response_screen, + bundleOf(Pair(BaseCardResponseFragment.response, it))) + }) + } + + private fun listenError() { + paramsVM.seError.observe(viewLifecycleOwner, Observer { showSnackbar(it) }) + + } + + private fun listenDescriptionSwitchChanges() { mainActivityVM.ldDescriptionSwitch.observe(viewLifecycleOwner, Observer { personalizeVM.toggleDescriptionVisibility(it) }) - - val cardManager = CardManager.init(requireActivity()) - paramsVM.setCardManager(cardManager) - paramsVM.ldResponse.observe(viewLifecycleOwner, Observer { - Log.d(this, "action response: ${if (it.length > 50) it.substring(0..50) else it}") - showSnackbarMessage(it) - }) - paramsVM.seError.observe(viewLifecycleOwner, Observer { showSnackbarMessage(it) }) - - 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) { @@ -98,14 +118,6 @@ class PersonalizeFragment : Fragment(), ActionView { 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() - } } diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/impl/item/TextWidget.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/BlockHeadWidget.kt similarity index 68% rename from tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/impl/item/TextWidget.kt rename to tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/BlockHeadWidget.kt index ae12a19231..2f2dde5d72 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/impl/item/TextWidget.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/BlockHeadWidget.kt @@ -1,15 +1,15 @@ -package com.tangem.tangemtest.ucase.variants.personalize.ui.widgets.impl.item +package com.tangem.tangemtest.ucase.variants.personalize.ui.widgets 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.personalize.ui.widgets.abstraction.getResNameId +import com.tangem.tangemtest._arch.widget.abstraction.getResNameId /** [REDACTED_AUTHOR] */ -class TextWidget(parent: ViewGroup, data: TextItem) : DescriptionWidget(parent, data) { +class BlockHeadWidget(parent: ViewGroup, data: TextItem) : DescriptionWidget(parent, data) { override fun getLayoutId(): Int = R.layout.w_personalize_item_text private val tvName = view.findViewById(R.id.tv_name) diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/impl/item/DescriptionWidget.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/DescriptionWidget.kt similarity index 87% rename from tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/impl/item/DescriptionWidget.kt rename to tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/DescriptionWidget.kt index c50a5e91dd..70107becdb 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/impl/item/DescriptionWidget.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/DescriptionWidget.kt @@ -1,4 +1,4 @@ -package com.tangem.tangemtest.ucase.variants.personalize.ui.widgets.impl.item +package com.tangem.tangemtest.ucase.variants.personalize.ui.widgets import android.view.ViewGroup import android.widget.TextView @@ -6,8 +6,8 @@ import androidx.transition.AutoTransition import androidx.transition.TransitionManager import com.tangem.tangemtest.R import com.tangem.tangemtest._arch.structure.abstraction.BaseItem -import com.tangem.tangemtest.ucase.variants.personalize.ui.widgets.abstraction.BaseWidget -import com.tangem.tangemtest.ucase.variants.personalize.ui.widgets.abstraction.getResDescription +import com.tangem.tangemtest._arch.widget.abstraction.BaseWidget +import com.tangem.tangemtest._arch.widget.abstraction.getResDescription import ru.dev.gbixahue.eu4d.lib.android.global.log.Log /** diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/impl/item/EditTextWidget.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/EditTextWidget.kt similarity index 94% rename from tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/impl/item/EditTextWidget.kt rename to tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/EditTextWidget.kt index c4bff5cfab..791e80b048 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/impl/item/EditTextWidget.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/EditTextWidget.kt @@ -1,4 +1,4 @@ -package com.tangem.tangemtest.ucase.variants.personalize.ui.widgets.impl.item +package com.tangem.tangemtest.ucase.variants.personalize.ui.widgets import android.text.Editable import android.text.TextWatcher @@ -7,7 +7,7 @@ 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.EditTextItem -import com.tangem.tangemtest.ucase.variants.personalize.ui.widgets.abstraction.getResNameId +import com.tangem.tangemtest._arch.widget.abstraction.getResNameId import ru.dev.gbixahue.eu4d.lib.android._android.views.moveCursorToEnd import ru.dev.gbixahue.eu4d.lib.kotlin.stringOf diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/impl/item/NumberWidget.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/NumberWidget.kt similarity index 95% rename from tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/impl/item/NumberWidget.kt rename to tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/NumberWidget.kt index 794c4c1ea0..1b72f5f21d 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/impl/item/NumberWidget.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/NumberWidget.kt @@ -1,4 +1,4 @@ -package com.tangem.tangemtest.ucase.variants.personalize.ui.widgets.impl.item +package com.tangem.tangemtest.ucase.variants.personalize.ui.widgets import android.text.Editable import android.text.InputFilter @@ -8,8 +8,8 @@ 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._arch.widget.abstraction.getResNameId 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 diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/PersonalizeItemBuilder.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/PersonalizeItemBuilder.kt new file mode 100644 index 0000000000..30788d1e70 --- /dev/null +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/PersonalizeItemBuilder.kt @@ -0,0 +1,23 @@ +package com.tangem.tangemtest.ucase.variants.personalize.ui.widgets + +import android.view.ViewGroup +import com.tangem.tangemtest._arch.structure.abstraction.BaseItem +import com.tangem.tangemtest._arch.structure.impl.* +import com.tangem.tangemtest._arch.widget.ItemWidgetBuilder +import com.tangem.tangemtest._arch.widget.abstraction.ViewWidget + +/** +[REDACTED_AUTHOR] + */ +class PersonalizeItemBuilder : ItemWidgetBuilder { + override fun build(item: BaseItem<*>, parent: ViewGroup): ViewWidget? { + return when (item) { + is TextItem -> BlockHeadWidget(parent, item) + is EditTextItem -> EditTextWidget(parent, item) + is NumberItem -> NumberWidget(parent, item) + is BoolItem -> SwitchWidget(parent, item) + is ListItem -> SpinnerWidget(parent, item) + else -> null + } + } +} \ No newline at end of file diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/impl/item/SpinnerWidget.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/SpinnerWidget.kt similarity index 96% rename from tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/impl/item/SpinnerWidget.kt rename to tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/SpinnerWidget.kt index b6ec1d006c..8ea3d0f4ac 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/impl/item/SpinnerWidget.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/SpinnerWidget.kt @@ -1,4 +1,4 @@ -package com.tangem.tangemtest.ucase.variants.personalize.ui.widgets.impl.item +package com.tangem.tangemtest.ucase.variants.personalize.ui.widgets import android.view.View import android.view.ViewGroup @@ -10,7 +10,7 @@ import com.tangem.tangemtest.R import com.tangem.tangemtest._arch.structure.impl.KeyValue import com.tangem.tangemtest._arch.structure.impl.ListItem import com.tangem.tangemtest._arch.structure.impl.ListValueWrapper -import com.tangem.tangemtest.ucase.variants.personalize.ui.widgets.abstraction.getResNameId +import com.tangem.tangemtest._arch.widget.abstraction.getResNameId import ru.dev.gbixahue.eu4d.lib.android._android.views.inflate import ru.dev.gbixahue.eu4d.lib.kotlin.stringOf diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/impl/item/SwitchWidget.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/SwitchWidget.kt similarity index 91% rename from tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/impl/item/SwitchWidget.kt rename to tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/SwitchWidget.kt index 3b7d85bed0..35a12ff069 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/impl/item/SwitchWidget.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/SwitchWidget.kt @@ -1,11 +1,11 @@ -package com.tangem.tangemtest.ucase.variants.personalize.ui.widgets.impl.item +package com.tangem.tangemtest.ucase.variants.personalize.ui.widgets import android.view.ViewGroup import android.widget.CompoundButton import androidx.appcompat.widget.SwitchCompat import com.tangem.tangemtest.R import com.tangem.tangemtest._arch.structure.impl.BoolItem -import com.tangem.tangemtest.ucase.variants.personalize.ui.widgets.abstraction.getResNameId +import com.tangem.tangemtest._arch.widget.abstraction.getResNameId /** [REDACTED_AUTHOR] diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/WidgetBuilder.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/WidgetBuilder.kt deleted file mode 100644 index a9aa921bcf..0000000000 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/WidgetBuilder.kt +++ /dev/null @@ -1,48 +0,0 @@ -package com.tangem.tangemtest.ucase.variants.personalize.ui.widgets - -import android.view.ViewGroup -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.ListItemBlock -import com.tangem.tangemtest._arch.structure.impl.* -import com.tangem.tangemtest.ucase.variants.personalize.ui.widgets.abstraction.ViewWidget -import com.tangem.tangemtest.ucase.variants.personalize.ui.widgets.impl.StubWidget -import com.tangem.tangemtest.ucase.variants.personalize.ui.widgets.impl.block.LinearBlockWidget -import com.tangem.tangemtest.ucase.variants.personalize.ui.widgets.impl.item.* - -/** -[REDACTED_AUTHOR] - */ -class WidgetBuilder { - - fun build(item: Item, parent: ViewGroup): ViewWidget? { - return when (item) { - is Block -> buildBlock(item, parent) - is BaseItem<*> -> buildUnitItem(item, parent) - else -> StubWidget(parent) - } - } - - private fun buildBlock(block: Block, parent: ViewGroup): ViewWidget { - return when (block) { - is ListItemBlock -> { - val linearBlock = LinearBlockWidget(parent, block) - block.getItems().forEach { build(it, linearBlock.view as ViewGroup) } - linearBlock - } - else -> StubWidget(parent) - } - } - - private fun buildUnitItem(item: BaseItem<*>, parent: ViewGroup): ViewWidget? { - return when (item) { - is TextItem -> TextWidget(parent, item) - is EditTextItem -> EditTextWidget(parent, item) - is NumberItem -> NumberWidget(parent, item) - is BoolItem -> SwitchWidget(parent, item) - is ListItem -> SpinnerWidget(parent, item) - else -> null - } - } -} \ No newline at end of file diff --git a/tangem-demo/src/main/res/layout/fg_card_response.xml b/tangem-demo/src/main/res/layout/fg_card_response.xml new file mode 100644 index 0000000000..f9aeaa5923 --- /dev/null +++ b/tangem-demo/src/main/res/layout/fg_card_response.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/tangem-demo/src/main/res/layout/w_response_item.xml b/tangem-demo/src/main/res/layout/w_response_item.xml new file mode 100644 index 0000000000..a18a8a24b7 --- /dev/null +++ b/tangem-demo/src/main/res/layout/w_response_item.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tangem-demo/src/main/res/navigation/navigation.xml b/tangem-demo/src/main/res/navigation/navigation.xml index 42db9c6d1c..68e2e1ca1c 100644 --- a/tangem-demo/src/main/res/navigation/navigation.xml +++ b/tangem-demo/src/main/res/navigation/navigation.xml @@ -4,6 +4,10 @@ xmlns:tools="http://schemas.android.com/tools" app:startDestination="@+id/nav_entry_point"> + + + + From 2111f460faad0e8d15af9ba4b762746be980e4ea Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 7 Apr 2020 16:14:00 +0300 Subject: [PATCH 03/39] Updated on 2026-08-14 --- .../variants/scan/ui/ScanActionFragment.kt | 21 ++++++++----------- 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/scan/ui/ScanActionFragment.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/scan/ui/ScanActionFragment.kt index ef5e6d8f01..11a16f5fb0 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/scan/ui/ScanActionFragment.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/scan/ui/ScanActionFragment.kt @@ -2,13 +2,12 @@ package com.tangem.tangemtest.ucase.variants.scan.ui import android.os.Bundle import android.view.View -import android.widget.TextView +import androidx.core.os.bundleOf import androidx.lifecycle.Observer import com.tangem.tangemtest.R import com.tangem.tangemtest.ucase.resources.ActionType import com.tangem.tangemtest.ucase.ui.BaseCardActionFragment -import ru.dev.gbixahue.eu4d.lib.android._android.views.show -import ru.dev.gbixahue.eu4d.lib.android.global.log.Log +import com.tangem.tangemtest.ucase.ui.BaseCardResponseFragment /** [REDACTED_AUTHOR] @@ -21,17 +20,15 @@ class ScanActionFragment : BaseCardActionFragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - - showActionFab(true) + enableActionFab(true) } - override fun listenResponse() { - val tvStub = mainView.findViewById(R.id.tv_screen_stub) - val tvResponse by lazy { mainView.findViewById(R.id.tv_action_response_json) } - paramsVM.ldReadResponse.observe(viewLifecycleOwner, Observer { - Log.d(this, "action response: ${if (it.length > 50) it.substring(0..50) else it}") - tvStub.show(false) - tvResponse.text = it + override fun listenReadResponse() { + paramsVM.seReadResponse.observe(viewLifecycleOwner, Observer { + navigateTo( + R.id.action_nav_card_action_to_response_screen, + bundleOf(Pair(BaseCardResponseFragment.response, it)) + ) }) } } \ No newline at end of file From 7bea5ecea1cb24ab12245d98560e904a585cc691 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 7 Apr 2020 17:33:56 +0300 Subject: [PATCH 04/39] Updated on 2026-08-14 --- .../main/java/com/tangem/server_android/data/LocalStorage.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/server-android/src/main/java/com/tangem/server_android/data/LocalStorage.kt b/server-android/src/main/java/com/tangem/server_android/data/LocalStorage.kt index 102e7ef8d9..4fbfe8956c 100644 --- a/server-android/src/main/java/com/tangem/server_android/data/LocalStorage.kt +++ b/server-android/src/main/java/com/tangem/server_android/data/LocalStorage.kt @@ -47,10 +47,11 @@ class LocalStorage artworks = HashMap() } - if (artworks.count() < 43) { + if (artworks.count() < 44) { // forceSave=true only on the last one putResourceArtworkToCatalog(R.drawable.card_default, false) putResourceArtworkToCatalog(R.drawable.card_default_nft, false) + putResourceArtworkToCatalog(R.drawable.card_id_issuer, false) putResourceArtworkToCatalog(R.drawable.card_ru006, false) putResourceArtworkToCatalog(R.drawable.card_ru007, false) putResourceArtworkToCatalog(R.drawable.card_ru011, false) From 93117f441f9d37595e5e16ee5e9a10b3a40ad323 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 7 Apr 2020 17:43:43 +0300 Subject: [PATCH 05/39] Updated on 2026-08-14 --- .idea/codeStyles/codeStyleConfig.xml | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 .idea/codeStyles/codeStyleConfig.xml diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml deleted file mode 100644 index a55e7a179b..0000000000 --- a/.idea/codeStyles/codeStyleConfig.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - \ No newline at end of file From e7d4125588c0e2c23885dc1a70dc3b52b50751f2 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 7 Apr 2020 21:14:44 +0300 Subject: [PATCH 06/39] Updated on 2026-08-14 --- .gitignore | 1 + .../_arch/structure/abstraction/Item.kt | 2 +- .../tangemtest/_arch/structure/impl/Items.kt | 2 +- .../ParamChangeConsequence.kt | 4 +- .../ucase/domain/responses/GsonInitializer.kt | 2 +- .../personalize/ui/PersonalizeFragment.kt | 38 +++++++++---------- .../variants/scan/ui/ScanActionFragment.kt | 2 +- .../src/main/res/layout/w_response_item.xml | 5 ++- .../src/main/res/navigation/navigation.xml | 2 +- 9 files changed, 29 insertions(+), 29 deletions(-) diff --git a/.gitignore b/.gitignore index ae9ed9c524..d741153000 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,7 @@ local.properties .idea/caches/ .idea/libraries/ .idea/*.xml +.idea/codeStyles # OS-specific files .DS_Store diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/structure/abstraction/Item.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/structure/abstraction/Item.kt index 2cfbc69173..40a546b78b 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/structure/abstraction/Item.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/structure/abstraction/Item.kt @@ -25,7 +25,7 @@ abstract class BaseItem( viewModel.data = value } - fun restoreDefaultData(){ + fun restoreDefaultData() { setData(viewModel.defaultData) } } \ No newline at end of file diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/structure/impl/Items.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/structure/impl/Items.kt index 11ea5510c0..5e6d10bd37 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/structure/impl/Items.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/structure/impl/Items.kt @@ -16,4 +16,4 @@ class ListItem(override val id: Id, value: List, selectedValue: Any?) : BaseItem(ListViewModel(ListValueWrapper(selectedValue, value)) ) -inline fun BaseItem.getData(): T? = viewModel.data \ No newline at end of file +inline fun BaseItem.getData(): T? = viewModel.data \ No newline at end of file diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/triggers/changeConsequence/ParamChangeConsequence.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/triggers/changeConsequence/ParamChangeConsequence.kt index 5f61c31c34..41bd4cd0a3 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/triggers/changeConsequence/ParamChangeConsequence.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/triggers/changeConsequence/ParamChangeConsequence.kt @@ -22,7 +22,7 @@ interface ItemsChangeConsequence { fun affectChanges(payload: PayloadHolder, changedItem: Item, itemList: List): List? } -class SignScanConsequence: ItemsChangeConsequence { +class SignScanConsequence : ItemsChangeConsequence { override fun affectChanges(payload: PayloadHolder, changedItem: Item, itemList: List): List? { if (changedItem.id != TlvId.CardId) return null @@ -35,7 +35,7 @@ class SignScanConsequence: ItemsChangeConsequence { postUI { (payload.get(PayloadKey.actionView) as? ActionView)?.enableActionFab(false) } } else { val dataForHashing = hashItem.getData() as? String ?: "Any data mother...s" - val hashedData = when(card.curve) { + val hashedData = when (card.curve) { EllipticCurve.Secp256k1 -> dataForHashing.calculateSha256() EllipticCurve.Ed25519 -> dataForHashing.calculateSha512() else -> return null diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/responses/GsonInitializer.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/responses/GsonInitializer.kt index 32c93a7ff8..4d6e2407d7 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/responses/GsonInitializer.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/responses/GsonInitializer.kt @@ -57,7 +57,7 @@ class SigningMethodTypeAdapter : JsonSerializer { } } -class DateTypeAdapter: JsonSerializer { +class DateTypeAdapter : JsonSerializer { 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()) diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/PersonalizeFragment.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/PersonalizeFragment.kt index d206e1f3d1..f6501e0187 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/PersonalizeFragment.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/PersonalizeFragment.kt @@ -3,8 +3,6 @@ package com.tangem.tangemtest.ucase.variants.personalize.ui import android.os.Bundle import android.view.View import android.view.ViewGroup -import android.widget.LinearLayout -import androidx.core.os.bundleOf import androidx.fragment.app.activityViewModels import androidx.fragment.app.viewModels import androidx.lifecycle.Observer @@ -24,7 +22,6 @@ 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.BaseCardResponseFragment import com.tangem.tangemtest.ucase.ui.BaseFragment import com.tangem.tangemtest.ucase.ui.ParamsViewModel import com.tangem.tangemtest.ucase.variants.personalize.dto.PersonalizeConfig @@ -36,19 +33,21 @@ import ru.dev.gbixahue.eu4d.lib.android.global.log.Log */ class PersonalizeFragment : BaseFragment(), ActionView { + private lateinit var itemContainer: ViewGroup + private lateinit var actionFab: FloatingActionButton + private val mainActivityVM: MainViewModel by activityViewModels() private val personalizeVM: PersonalizeViewModel by viewModels { PersonalizeViewModelFactory(PersonalizeConfig()) } private val paramsVM: ParamsViewModel by viewModels() { ActionViewModelFactory(itemsManager) } - private val blockContainer: ViewGroup by lazy { mainView.findViewById(R.id.ll_container) } - private val actionFab: FloatingActionButton by lazy { mainView.findViewById(R.id.fab_action) } - private val itemsManager: ItemsManager by lazy { ParamsManagerFactory.createFactory().get(ActionType.Personalize)!! } override fun getLayoutId(): Int = R.layout.fg_personalize override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + itemContainer = mainView.findViewById(R.id.ll_container) + actionFab = mainView.findViewById(R.id.fab_action) paramsVM.setCardManager(CardManager.init(requireActivity())) paramsVM.attachToPayload(mutableMapOf(PayloadKey.actionView to this as ActionView)) @@ -58,16 +57,17 @@ class PersonalizeFragment : BaseFragment(), ActionView { } private fun initFab() { - enableActionFab(true) actionFab.setOnClickListener { paramsVM.invokeMainAction() } } private fun createWidgets(widgetCreatedCallback: () -> Unit) { + Log.d(this, "createWidgets") val blockList = mutableListOf() personalizeVM.ldBlockList.observe(viewLifecycleOwner, Observer { list -> + Log.d(this, "ldBlockList size: ${list.size}") blockList.clear() blockList.addAll(list) - blockList.forEach { WidgetBuilder(PersonalizeItemBuilder()).build(it, blockContainer) } + blockList.forEach { WidgetBuilder(PersonalizeItemBuilder()).build(it, itemContainer) } paramsVM.attachToPayload(mutableMapOf( PayloadKey.actionView to this as ActionView, PayloadKey.itemList to blockList @@ -76,25 +76,17 @@ class PersonalizeFragment : BaseFragment(), ActionView { }) } - override fun enableActionFab(enable: Boolean) { - if (enable) actionFab.show() else actionFab.hide() - } - private fun subscribeToViewModelChanges() { Log.d(this, "subscribeToViewModelChanges") - listenReadResponse() - listenResponse() + listenEvent() listenError() -// listenChangedItems() listenDescriptionSwitchChanges() } - private fun listenReadResponse() {} - - private fun listenResponse() { - paramsVM.seResponse.observe(viewLifecycleOwner, Observer { - navigateTo(R.id.action_nav_card_action_to_response_screen, - bundleOf(Pair(BaseCardResponseFragment.response, it))) + private fun listenEvent() { + paramsVM.seResponseEvent.observe(viewLifecycleOwner, Observer { + mainActivityVM.changeResponseEvent(it) + navigateTo(R.id.action_nav_card_action_to_response_screen) }) } @@ -109,6 +101,10 @@ class PersonalizeFragment : BaseFragment(), ActionView { }) } + override fun enableActionFab(enable: Boolean) { + if (enable) actionFab.show() else actionFab.hide() + } + override fun showSnackbar(id: Id) { // MainResourceHolder.safeGet<>() when (id) { diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/scan/ui/ScanActionFragment.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/scan/ui/ScanActionFragment.kt index 11a16f5fb0..ff1428465b 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/scan/ui/ScanActionFragment.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/scan/ui/ScanActionFragment.kt @@ -7,7 +7,7 @@ import androidx.lifecycle.Observer import com.tangem.tangemtest.R import com.tangem.tangemtest.ucase.resources.ActionType import com.tangem.tangemtest.ucase.ui.BaseCardActionFragment -import com.tangem.tangemtest.ucase.ui.BaseCardResponseFragment +import com.tangem.tangemtest.ucase.ui.response.BaseCardResponseFragment /** [REDACTED_AUTHOR] diff --git a/tangem-demo/src/main/res/layout/w_response_item.xml b/tangem-demo/src/main/res/layout/w_response_item.xml index a18a8a24b7..c44aa9cd78 100644 --- a/tangem-demo/src/main/res/layout/w_response_item.xml +++ b/tangem-demo/src/main/res/layout/w_response_item.xml @@ -4,7 +4,10 @@ android:id="@+id/root" android:layout_width="match_parent" android:layout_height="wrap_content" - android:padding="@dimen/def_indent"> + android:paddingStart="@dimen/def_indent" + android:paddingTop="6dp" + android:paddingEnd="@dimen/def_indent" + android:paddingBottom="6dp"> From 6a69b80ae80e0c5e38ab958dc723cbf72fcea31c Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 8 Apr 2020 00:07:11 +0300 Subject: [PATCH 07/39] Updated on 2026-08-14 --- .../managers/PersonalizeItemsManager.kt | 4 +- .../tangem/tangemtest/ucase/tunnel/Errors.kt | 4 +- .../ucase/ui/BaseCardActionFragment.kt | 46 ++++++----- .../ucase/ui/BaseCardResponseFragment.kt | 58 -------------- .../tangemtest/ucase/ui/BaseFragment.kt | 2 + .../tangemtest/ucase/ui/ParamsViewModel.kt | 22 ++++-- .../personalize/ui/PersonalizeFragment.kt | 78 ++++--------------- .../personalize/ui/PersonalizeViewModel.kt | 32 -------- .../main/res/layout/fg_action_card_scan.xml | 2 +- .../main/res/layout/fg_action_card_sign.xml | 2 +- .../src/main/res/layout/fg_depersonalize.xml | 2 +- 11 files changed, 68 insertions(+), 184 deletions(-) delete mode 100644 tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/BaseCardResponseFragment.kt delete mode 100644 tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/PersonalizeViewModel.kt diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/managers/PersonalizeItemsManager.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/managers/PersonalizeItemsManager.kt index 7f40ac54f5..c8c0b07380 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/managers/PersonalizeItemsManager.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/managers/PersonalizeItemsManager.kt @@ -4,12 +4,14 @@ import com.tangem.CardManager import com.tangem.tangemtest._arch.structure.abstraction.Item import com.tangem.tangemtest.ucase.domain.actions.PersonalizeAction import com.tangem.tangemtest.ucase.domain.paramsManager.ActionCallback +import com.tangem.tangemtest.ucase.variants.personalize.converter.PersonalizeConfigConverter +import com.tangem.tangemtest.ucase.variants.personalize.dto.PersonalizeConfig /** [REDACTED_AUTHOR] */ class PersonalizeItemsManager : BaseItemsManager(PersonalizeAction()) { - override fun createItemsList(): List = listOf() + override fun createItemsList(): List = PersonalizeConfigConverter().convert(PersonalizeConfig()) override fun invokeMainAction(cardManager: CardManager, callback: ActionCallback) { action.executeMainAction(this, getAttrsForAction(cardManager), callback) diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/tunnel/Errors.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/tunnel/Errors.kt index 7b7cb530f7..8ae2a5370a 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/tunnel/Errors.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/tunnel/Errors.kt @@ -5,13 +5,13 @@ import com.tangem.tangemtest._arch.structure.Id /** [REDACTED_AUTHOR] */ -interface Errors: Id +interface Errors : Id enum class CardError : Errors { NotPersonalized, } -enum class ItemError: Errors { +enum class ItemError : Errors { BadSeries, BadCardNumber, } \ No newline at end of file diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/BaseCardActionFragment.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/BaseCardActionFragment.kt index 6871b38bbb..d5313ac34e 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/BaseCardActionFragment.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/BaseCardActionFragment.kt @@ -4,8 +4,6 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import android.widget.LinearLayout -import androidx.core.os.bundleOf import androidx.fragment.app.activityViewModels import androidx.fragment.app.viewModels import androidx.lifecycle.Observer @@ -32,15 +30,18 @@ import ru.dev.gbixahue.eu4d.lib.kotlin.common.LayoutHolder */ abstract class BaseCardActionFragment : BaseFragment(), LayoutHolder, ActionView { - protected val incomingParamsContainer: ViewGroup by lazy { - mainView.findViewById(R.id.ll_incoming_params_container) - } - protected val actionFab: FloatingActionButton by lazy { mainView.findViewById(R.id.fab_action) } + protected val itemContainer: ViewGroup + get() = mainView.findViewById(R.id.ll_container) + protected val actionFab: FloatingActionButton + get() = mainView.findViewById(R.id.fab_action) + + protected val mainActivityVM by activityViewModels() protected val paramsVM: ParamsViewModel by viewModels { ActionViewModelFactory(itemsManager) } - protected val widgetList = mutableListOf() - private val itemsManager: ItemsManager by lazy { ParamsManagerFactory.createFactory().get(getAction())!! } + protected val itemsManager: ItemsManager by lazy { ParamsManagerFactory.createFactory().get(getAction())!! } + + private val paramsWidgetList = mutableListOf() override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) @@ -49,30 +50,32 @@ abstract class BaseCardActionFragment : BaseFragment(), LayoutHolder, ActionView viewLifecycleOwner.lifecycle.addObserver(paramsVM) paramsVM.setCardManager(CardManager.init(requireActivity())) paramsVM.attachToPayload(mutableMapOf(PayloadKey.actionView to this as ActionView)) + initFab() createWidgets { subscribeToViewModelChanges() } } - private fun initFab() { + protected open fun initFab() { enableActionFab(false) actionFab.setOnClickListener { paramsVM.invokeMainAction() } } - private fun createWidgets(widgetCreatedCallback: () -> Unit) { + protected open fun createWidgets(widgetCreatedCallback: () -> Unit) { Log.d(this, "createWidgets") paramsVM.ldItemList.observe(viewLifecycleOwner, Observer { itemList -> itemList.forEach { param -> - val widget = ParameterWidget(inflateParamView(incomingParamsContainer), param) + val widget = ParameterWidget(inflateParamView(itemContainer), param) widget.onValueChanged = { id, value -> paramsVM.userChangedItem(id, value) } widget.onActionBtnClickListener = paramsVM.getItemAction(param.id) - widgetList.add(widget) + paramsWidgetList.add(widget) } widgetCreatedCallback() }) } - private fun subscribeToViewModelChanges() { + protected open fun subscribeToViewModelChanges() { Log.d(this, "subscribeToViewModelChanges") + listenEvent() listenReadResponse() listenResponse() listenError() @@ -80,12 +83,17 @@ abstract class BaseCardActionFragment : BaseFragment(), LayoutHolder, ActionView listenDescriptionSwitchChanges() } + protected open fun listenEvent() { + paramsVM.seResponseEvent.observe(viewLifecycleOwner, Observer { + mainActivityVM.changeResponseEvent(it) + }) + } + protected open fun listenReadResponse() {} protected open fun listenResponse() { paramsVM.seResponse.observe(viewLifecycleOwner, Observer { - navigateTo(R.id.action_nav_card_action_to_response_screen, - bundleOf(Pair(BaseCardResponseFragment.response, it))) + navigateTo(R.id.action_nav_card_action_to_response_screen) }) } @@ -93,21 +101,21 @@ abstract class BaseCardActionFragment : BaseFragment(), LayoutHolder, ActionView paramsVM.seError.observe(viewLifecycleOwner, Observer { showSnackbar(it) }) } + @Deprecated("Start to use itemViewModel") protected open fun listenChangedItems() { paramsVM.seChangedItems.observe(viewLifecycleOwner, Observer { itemList -> itemList.forEach { item -> Log.d(this, "item changed from VM - name: ${item.id}") val dataItem = item as? BaseItem ?: return@Observer Log.d(this, "item changed from VM - name: ${dataItem.id}, value:${dataItem.viewModel.data}") - widgetList.firstOrNull { it.id == item.id }?.changeParamValue(item.viewModel.data) + paramsWidgetList.firstOrNull { it.id == item.id }?.changeParamValue(item.viewModel.data) } }) } protected open fun listenDescriptionSwitchChanges() { - val mainActViewModel by activityViewModels() - mainActViewModel.ldDescriptionSwitch.observe(viewLifecycleOwner, Observer { isEnabled -> - widgetList.forEach { it.toggleDescriptionVisibility(isEnabled) } + mainActivityVM.ldDescriptionSwitch.observe(viewLifecycleOwner, Observer { + paramsVM.toggleDescriptionVisibility(it) }) } diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/BaseCardResponseFragment.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/BaseCardResponseFragment.kt deleted file mode 100644 index 51d5e0866f..0000000000 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/BaseCardResponseFragment.kt +++ /dev/null @@ -1,58 +0,0 @@ -package com.tangem.tangemtest.ucase.ui - -import android.os.Bundle -import android.view.View -import android.view.ViewGroup -import android.widget.LinearLayout -import androidx.fragment.app.activityViewModels -import androidx.lifecycle.Observer -import com.tangem.tangemtest.R -import com.tangem.tangemtest._arch.structure.abstraction.BaseItem -import com.tangem.tangemtest._arch.structure.abstraction.Item -import com.tangem.tangemtest._arch.structure.abstraction.ListItemBlock -import com.tangem.tangemtest._arch.structure.abstraction.iterate -import com.tangem.tangemtest._arch.structure.impl.TextItem -import com.tangem.tangemtest._arch.widget.WidgetBuilder -import com.tangem.tangemtest._main.MainViewModel -import com.tangem.tangemtest.ucase.resources.StringId - -/** -[REDACTED_AUTHOR] - */ -class BaseCardResponseFragment : BaseFragment() { - - companion object Arg { - val response = "response" - } - - private val mainActivityVM: MainViewModel by activityViewModels() - private val blockContainer: ViewGroup by lazy { mainView.findViewById(R.id.ll_container) } - - override fun getLayoutId(): Int = R.layout.fg_card_response - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - - val response = requireArguments().getString(Arg.response) ?: return - if (response.isEmpty()) return - - val itemList = createItemList(response) - itemList.forEach { WidgetBuilder(ResponseItemBuilder()).build(it, blockContainer) } - - mainActivityVM.ldDescriptionSwitch.observe(viewLifecycleOwner, Observer { - itemList.iterate { item -> - val baseItem = item as? BaseItem<*> ?: return@iterate - baseItem.viewModel.viewState.descriptionVisibility = if (it) View.VISIBLE else View.GONE - } - }) - - } - - protected open fun createItemList(response: String): List { - return listOf( - ListItemBlock(StringId("")).apply { - addItem(TextItem(StringId("resp.fieldName"), response)) - } - ) - } -} diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/BaseFragment.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/BaseFragment.kt index 29dfeec6be..a7fabc3df0 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/BaseFragment.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/BaseFragment.kt @@ -10,6 +10,7 @@ import androidx.navigation.fragment.NavHostFragment.findNavController import com.google.android.material.snackbar.Snackbar import com.tangem.tangemtest.ucase.getDefaultNavigationOptions import com.tangem.tangemtest.ucase.tunnel.SnackbarHolder +import ru.dev.gbixahue.eu4d.lib.android.global.log.Log import ru.dev.gbixahue.eu4d.lib.kotlin.common.LayoutHolder /** @@ -25,6 +26,7 @@ abstract class BaseFragment : Fragment(), LayoutHolder, SnackbarHolder { } protected fun navigateTo(navigationId: Int, bundle: Bundle? = null, options: NavOptions? = getDefaultNavigationOptions()) { + Log.d(this, "navigateTo id: $navigationId") findNavController(this).navigate(navigationId, bundle, options) } diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/ParamsViewModel.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/ParamsViewModel.kt index 94b4d47a3f..0c0dc21908 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/ParamsViewModel.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/ParamsViewModel.kt @@ -1,5 +1,6 @@ package com.tangem.tangemtest.ucase.ui +import android.view.View import androidx.annotation.UiThread import androidx.lifecycle.* import com.google.gson.Gson @@ -7,7 +8,9 @@ import com.tangem.CardManager import com.tangem.tangemtest._arch.SingleLiveEvent import com.tangem.tangemtest._arch.structure.Id import com.tangem.tangemtest._arch.structure.Payload +import com.tangem.tangemtest._arch.structure.abstraction.BaseItem import com.tangem.tangemtest._arch.structure.abstraction.Item +import com.tangem.tangemtest._arch.structure.abstraction.iterate import com.tangem.tangemtest.commons.performAction import com.tangem.tangemtest.ucase.domain.paramsManager.ItemsManager import com.tangem.tangemtest.ucase.domain.responses.GsonInitializer @@ -26,6 +29,7 @@ class ActionViewModelFactory(private val manager: ItemsManager) : ViewModelProvi class ParamsViewModel(private val itemsManager: ItemsManager) : ViewModel(), LifecycleObserver { + val seResponseEvent = SingleLiveEvent>() val seReadResponse = SingleLiveEvent() val seResponse = SingleLiveEvent() @@ -67,6 +71,13 @@ class ParamsViewModel(private val itemsManager: ItemsManager) : ViewModel(), Lif } } + fun toggleDescriptionVisibility(state: Boolean) { + ldItemList.value?.iterate { + val baseItem = it as? BaseItem<*> ?: return@iterate + baseItem.viewModel.viewState.descriptionVisibility = if (state) View.VISIBLE else View.GONE + } + } + fun attachToPayload(payload: Payload) { itemsManager.attachPayload(payload) } @@ -99,18 +110,17 @@ internal class Notifier(private val vm: ParamsViewModel) { when (taskEvent) { is TaskEvent.Completion -> handleCompletionEvent(taskEvent) - is TaskEvent.Event -> handleDataEvent(taskEvent.data) + is TaskEvent.Event -> { + handleDataEvent(taskEvent.data) + vm.seResponseEvent.postValue(taskEvent) + } } } private fun handleDataEvent(event: Any?) { when (event) { - is ScanEvent.OnReadEvent -> { -// vm.ldCard.postValue(event.card) - vm.seReadResponse.postValue(gson.toJson(event)) - } + is ScanEvent.OnReadEvent -> vm.seReadResponse.postValue(gson.toJson(event)) is ScanEvent.OnVerifyEvent -> { -// vm.ldIsVerified.postValue(true) } else -> vm.seResponse.postValue(gson.toJson(event)) } diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/PersonalizeFragment.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/PersonalizeFragment.kt index f6501e0187..7c0928b7bd 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/PersonalizeFragment.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/PersonalizeFragment.kt @@ -1,112 +1,62 @@ package com.tangem.tangemtest.ucase.variants.personalize.ui -import android.os.Bundle -import android.view.View -import android.view.ViewGroup -import androidx.fragment.app.activityViewModels -import androidx.fragment.app.viewModels import androidx.lifecycle.Observer -import com.google.android.material.floatingactionbutton.FloatingActionButton -import com.tangem.CardManager -import com.tangem.tangem_sdk_new.extensions.init import com.tangem.tangemtest.R import com.tangem.tangemtest._arch.structure.Id import com.tangem.tangemtest._arch.structure.abstraction.Item import com.tangem.tangemtest._arch.widget.WidgetBuilder -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.BaseFragment -import com.tangem.tangemtest.ucase.ui.ParamsViewModel -import com.tangem.tangemtest.ucase.variants.personalize.dto.PersonalizeConfig +import com.tangem.tangemtest.ucase.ui.BaseCardActionFragment import com.tangem.tangemtest.ucase.variants.personalize.ui.widgets.PersonalizeItemBuilder import ru.dev.gbixahue.eu4d.lib.android.global.log.Log /** [REDACTED_AUTHOR] */ -class PersonalizeFragment : BaseFragment(), ActionView { - - private lateinit var itemContainer: ViewGroup - private lateinit var actionFab: FloatingActionButton - - private val mainActivityVM: MainViewModel by activityViewModels() - private val personalizeVM: PersonalizeViewModel by viewModels { PersonalizeViewModelFactory(PersonalizeConfig()) } - private val paramsVM: ParamsViewModel by viewModels() { ActionViewModelFactory(itemsManager) } - - private val itemsManager: ItemsManager by lazy { ParamsManagerFactory.createFactory().get(ActionType.Personalize)!! } +class PersonalizeFragment : BaseCardActionFragment(), ActionView { override fun getLayoutId(): Int = R.layout.fg_personalize - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - itemContainer = mainView.findViewById(R.id.ll_container) - actionFab = mainView.findViewById(R.id.fab_action) - - paramsVM.setCardManager(CardManager.init(requireActivity())) - paramsVM.attachToPayload(mutableMapOf(PayloadKey.actionView to this as ActionView)) - - initFab() - createWidgets { subscribeToViewModelChanges() } - } - - private fun initFab() { + override fun initFab() { actionFab.setOnClickListener { paramsVM.invokeMainAction() } } - private fun createWidgets(widgetCreatedCallback: () -> Unit) { + override fun createWidgets(widgetCreatedCallback: () -> Unit) { Log.d(this, "createWidgets") - val blockList = mutableListOf() - personalizeVM.ldBlockList.observe(viewLifecycleOwner, Observer { list -> + val itemList = mutableListOf() + + paramsVM.ldItemList.observe(viewLifecycleOwner, Observer { list -> Log.d(this, "ldBlockList size: ${list.size}") - blockList.clear() - blockList.addAll(list) - blockList.forEach { WidgetBuilder(PersonalizeItemBuilder()).build(it, itemContainer) } + itemList.clear() + itemList.addAll(list) + itemList.forEach { WidgetBuilder(PersonalizeItemBuilder()).build(it, itemContainer) } paramsVM.attachToPayload(mutableMapOf( PayloadKey.actionView to this as ActionView, - PayloadKey.itemList to blockList + PayloadKey.itemList to itemList )) widgetCreatedCallback() }) } - private fun subscribeToViewModelChanges() { + override fun subscribeToViewModelChanges() { Log.d(this, "subscribeToViewModelChanges") listenEvent() listenError() listenDescriptionSwitchChanges() } - private fun listenEvent() { + override fun listenEvent() { paramsVM.seResponseEvent.observe(viewLifecycleOwner, Observer { mainActivityVM.changeResponseEvent(it) navigateTo(R.id.action_nav_card_action_to_response_screen) }) } - private fun listenError() { - paramsVM.seError.observe(viewLifecycleOwner, Observer { showSnackbar(it) }) - - } - - private fun listenDescriptionSwitchChanges() { - mainActivityVM.ldDescriptionSwitch.observe(viewLifecycleOwner, Observer { - personalizeVM.toggleDescriptionVisibility(it) - }) - } - - override fun enableActionFab(enable: Boolean) { - if (enable) 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) @@ -114,6 +64,8 @@ class PersonalizeFragment : BaseFragment(), ActionView { else -> showSnackbar(requireContext().getString(R.string.unknown)) } } + + override fun getAction(): ActionType = ActionType.Personalize } diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/PersonalizeViewModel.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/PersonalizeViewModel.kt deleted file mode 100644 index b2c7ec7fce..0000000000 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/PersonalizeViewModel.kt +++ /dev/null @@ -1,32 +0,0 @@ -package com.tangem.tangemtest.ucase.variants.personalize.ui - -import android.view.View -import androidx.lifecycle.MutableLiveData -import androidx.lifecycle.ViewModel -import androidx.lifecycle.ViewModelProvider -import com.tangem.tangemtest._arch.structure.abstraction.BaseItem -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 - -/** -[REDACTED_AUTHOR] - */ -class PersonalizeViewModelFactory(private val config: PersonalizeConfig) : ViewModelProvider.NewInstanceFactory() { - override fun create(modelClass: Class): T = PersonalizeViewModel(config) as T -} - -class PersonalizeViewModel(private val config: PersonalizeConfig) : ViewModel() { - - val ldBlockList: MutableLiveData> by lazy { MutableLiveData(initBlockList()) } - - private fun initBlockList(): List = PersonalizeConfigConverter().convert(config) - - fun toggleDescriptionVisibility(state: Boolean) { - ldBlockList.value?.iterate { - val baseItem = it as? BaseItem<*> ?: return@iterate - baseItem.viewModel.viewState.descriptionVisibility = if (state) View.VISIBLE else View.GONE - } - } -} \ No newline at end of file diff --git a/tangem-demo/src/main/res/layout/fg_action_card_scan.xml b/tangem-demo/src/main/res/layout/fg_action_card_scan.xml index aa343731bc..8b2deca0e8 100644 --- a/tangem-demo/src/main/res/layout/fg_action_card_scan.xml +++ b/tangem-demo/src/main/res/layout/fg_action_card_scan.xml @@ -15,7 +15,7 @@ android:orientation="vertical"> diff --git a/tangem-demo/src/main/res/layout/fg_action_card_sign.xml b/tangem-demo/src/main/res/layout/fg_action_card_sign.xml index 45978494a3..8013659452 100644 --- a/tangem-demo/src/main/res/layout/fg_action_card_sign.xml +++ b/tangem-demo/src/main/res/layout/fg_action_card_sign.xml @@ -15,7 +15,7 @@ android:orientation="vertical"> diff --git a/tangem-demo/src/main/res/layout/fg_depersonalize.xml b/tangem-demo/src/main/res/layout/fg_depersonalize.xml index 45978494a3..8013659452 100644 --- a/tangem-demo/src/main/res/layout/fg_depersonalize.xml +++ b/tangem-demo/src/main/res/layout/fg_depersonalize.xml @@ -15,7 +15,7 @@ android:orientation="vertical"> From 1a1109d1d851eff2fc1cd400d272dca63dfa6a11 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 8 Apr 2020 00:08:50 +0300 Subject: [PATCH 08/39] Updated on 2026-08-14 --- .../com/tangem/tangemtest/ucase/ui/BaseCardActionFragment.kt | 3 +-- .../ucase/variants/personalize/ui/PersonalizeFragment.kt | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/BaseCardActionFragment.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/BaseCardActionFragment.kt index d5313ac34e..eba8eec1d6 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/BaseCardActionFragment.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/BaseCardActionFragment.kt @@ -23,12 +23,11 @@ import com.tangem.tangemtest.ucase.tunnel.CardError import com.tangem.tangemtest.ucase.ui.widgets.ParameterWidget import ru.dev.gbixahue.eu4d.lib.android._android.views.enable import ru.dev.gbixahue.eu4d.lib.android.global.log.Log -import ru.dev.gbixahue.eu4d.lib.kotlin.common.LayoutHolder /** [REDACTED_AUTHOR] */ -abstract class BaseCardActionFragment : BaseFragment(), LayoutHolder, ActionView { +abstract class BaseCardActionFragment : BaseFragment(), ActionView { protected val itemContainer: ViewGroup get() = mainView.findViewById(R.id.ll_container) diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/PersonalizeFragment.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/PersonalizeFragment.kt index 7c0928b7bd..0a18a0989f 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/PersonalizeFragment.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/PersonalizeFragment.kt @@ -17,7 +17,7 @@ import ru.dev.gbixahue.eu4d.lib.android.global.log.Log /** [REDACTED_AUTHOR] */ -class PersonalizeFragment : BaseCardActionFragment(), ActionView { +class PersonalizeFragment : BaseCardActionFragment() { override fun getLayoutId(): Int = R.layout.fg_personalize From e6426d92f40ab0e42e451575c3258367a7cc002d Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 8 Apr 2020 00:50:00 +0300 Subject: [PATCH 09/39] Updated on 2026-08-14 --- .../tangem/tangemtest/_main/MainViewModel.kt | 10 ++++++ .../ucase/domain/paramsManager/Factory.kt | 12 +++++-- ...{ParamsViewModel.kt => ActionViewModel.kt} | 7 ++-- .../ucase/ui/BaseCardActionFragment.kt | 36 ++++++++++--------- .../personalize/ui/PersonalizeFragment.kt | 8 ++--- .../variants/scan/ui/ScanActionFragment.kt | 2 +- 6 files changed, 48 insertions(+), 27 deletions(-) rename tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/{ParamsViewModel.kt => ActionViewModel.kt} (95%) diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/_main/MainViewModel.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/_main/MainViewModel.kt index 17ce97d8f9..a7d2eef283 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/_main/MainViewModel.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/_main/MainViewModel.kt @@ -2,14 +2,24 @@ package com.tangem.tangemtest._main import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel +import com.tangem.tasks.TaskEvent +import ru.dev.gbixahue.eu4d.lib.android.global.log.Log /** [REDACTED_AUTHOR] */ class MainViewModel : ViewModel() { val ldDescriptionSwitch = MutableLiveData(false) + val ldResponseEvent = MutableLiveData>() fun switchToggled(state: Boolean) { ldDescriptionSwitch.postValue(state) } + + fun changeResponseEvent(event: TaskEvent<*>?) { + Log.d(this, "changeResponseEvent") + val reqEvent = event ?: return + + ldResponseEvent.value = reqEvent + } } \ No newline at end of file diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/Factory.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/Factory.kt index 000e9a53c5..c71097292c 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/Factory.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/Factory.kt @@ -5,16 +5,22 @@ import com.tangem.tangemtest.ucase.domain.paramsManager.managers.PersonalizeItem import com.tangem.tangemtest.ucase.domain.paramsManager.managers.ScanItemsManager import com.tangem.tangemtest.ucase.domain.paramsManager.managers.SignItemsManager import com.tangem.tangemtest.ucase.resources.ActionType +import ru.dev.gbixahue.eu4d.lib.android.global.log.Log import ru.dev.gbixahue.eu4d.lib.kotlin.common.BaseTypedHolder /** [REDACTED_AUTHOR] */ -class ParamsManagerFactory : BaseTypedHolder() { +class ItemManagersStore : BaseTypedHolder() { companion object { - fun createFactory(): ParamsManagerFactory { - return ParamsManagerFactory().apply { + val instance: ItemManagersStore + get() = createFactory() + + private fun createFactory(): ItemManagersStore { + Log.d(this, "New instance was created") + + return ItemManagersStore().apply { register(ActionType.Scan, ScanItemsManager()) register(ActionType.Sign, SignItemsManager()) register(ActionType.Personalize, PersonalizeItemsManager()) diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/ParamsViewModel.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/ActionViewModel.kt similarity index 95% rename from tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/ParamsViewModel.kt rename to tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/ActionViewModel.kt index 0c0dc21908..8ffcb80328 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/ParamsViewModel.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/ActionViewModel.kt @@ -24,10 +24,10 @@ import ru.dev.gbixahue.eu4d.lib.android.global.log.Log [REDACTED_AUTHOR] */ class ActionViewModelFactory(private val manager: ItemsManager) : ViewModelProvider.NewInstanceFactory() { - override fun create(modelClass: Class): T = ParamsViewModel(manager) as T + override fun create(modelClass: Class): T = ActionViewModel(manager) as T } -class ParamsViewModel(private val itemsManager: ItemsManager) : ViewModel(), LifecycleObserver { +class ActionViewModel(private val itemsManager: ItemsManager) : ViewModel(), LifecycleObserver { val seResponseEvent = SingleLiveEvent>() val seReadResponse = SingleLiveEvent() @@ -44,6 +44,7 @@ class ParamsViewModel(private val itemsManager: ItemsManager) : ViewModel(), Lif this.cardManager = cardManager } + @Deprecated("Events must be send directly from the Widget") fun userChangedItem(id: Id, value: Any?) { itemChanged(id, value) } @@ -90,7 +91,7 @@ class ParamsViewModel(private val itemsManager: ItemsManager) : ViewModel(), Lif } } -internal class Notifier(private val vm: ParamsViewModel) { +internal class Notifier(private val vm: ActionViewModel) { private var notShowedError: TaskError? = null private val gson: Gson = GsonInitializer().gson diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/BaseCardActionFragment.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/BaseCardActionFragment.kt index eba8eec1d6..63e2c8be82 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/BaseCardActionFragment.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/BaseCardActionFragment.kt @@ -4,6 +4,7 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import androidx.core.view.plusAssign import androidx.fragment.app.activityViewModels import androidx.fragment.app.viewModels import androidx.lifecycle.Observer @@ -14,8 +15,8 @@ import com.tangem.tangemtest.R import com.tangem.tangemtest._arch.structure.Id import com.tangem.tangemtest._arch.structure.abstraction.BaseItem import com.tangem.tangemtest._main.MainViewModel +import com.tangem.tangemtest.ucase.domain.paramsManager.ItemManagersStore 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 @@ -36,9 +37,12 @@ abstract class BaseCardActionFragment : BaseFragment(), ActionView { get() = mainView.findViewById(R.id.fab_action) protected val mainActivityVM by activityViewModels() - protected val paramsVM: ParamsViewModel by viewModels { ActionViewModelFactory(itemsManager) } + protected val actionVM: ActionViewModel by viewModels { ActionViewModelFactory(itemsManager) } - protected val itemsManager: ItemsManager by lazy { ParamsManagerFactory.createFactory().get(getAction())!! } + protected val itemsManager: ItemsManager by lazy { + Log.d(this, "create ItemManager") + ItemManagersStore.instance.get(getAction())!! + } private val paramsWidgetList = mutableListOf() @@ -46,9 +50,9 @@ abstract class BaseCardActionFragment : BaseFragment(), ActionView { super.onViewCreated(view, savedInstanceState) Log.d(this, "onViewCreated") - viewLifecycleOwner.lifecycle.addObserver(paramsVM) - paramsVM.setCardManager(CardManager.init(requireActivity())) - paramsVM.attachToPayload(mutableMapOf(PayloadKey.actionView to this as ActionView)) + viewLifecycleOwner.lifecycle.addObserver(actionVM) + actionVM.setCardManager(CardManager.init(requireActivity())) + actionVM.attachToPayload(mutableMapOf(PayloadKey.actionView to this as ActionView)) initFab() createWidgets { subscribeToViewModelChanges() } @@ -56,16 +60,16 @@ abstract class BaseCardActionFragment : BaseFragment(), ActionView { protected open fun initFab() { enableActionFab(false) - actionFab.setOnClickListener { paramsVM.invokeMainAction() } + actionFab.setOnClickListener { actionVM.invokeMainAction() } } protected open fun createWidgets(widgetCreatedCallback: () -> Unit) { Log.d(this, "createWidgets") - paramsVM.ldItemList.observe(viewLifecycleOwner, Observer { itemList -> + actionVM.ldItemList.observe(viewLifecycleOwner, Observer { itemList -> itemList.forEach { param -> val widget = ParameterWidget(inflateParamView(itemContainer), param) - widget.onValueChanged = { id, value -> paramsVM.userChangedItem(id, value) } - widget.onActionBtnClickListener = paramsVM.getItemAction(param.id) + widget.onValueChanged = { id, value -> actionVM.userChangedItem(id, value) } + widget.onActionBtnClickListener = actionVM.getItemAction(param.id) paramsWidgetList.add(widget) } widgetCreatedCallback() @@ -83,7 +87,7 @@ abstract class BaseCardActionFragment : BaseFragment(), ActionView { } protected open fun listenEvent() { - paramsVM.seResponseEvent.observe(viewLifecycleOwner, Observer { + actionVM.seResponseEvent.observe(viewLifecycleOwner, Observer { mainActivityVM.changeResponseEvent(it) }) } @@ -91,18 +95,18 @@ abstract class BaseCardActionFragment : BaseFragment(), ActionView { protected open fun listenReadResponse() {} protected open fun listenResponse() { - paramsVM.seResponse.observe(viewLifecycleOwner, Observer { + actionVM.seResponse.observe(viewLifecycleOwner, Observer { navigateTo(R.id.action_nav_card_action_to_response_screen) }) } protected open fun listenError() { - paramsVM.seError.observe(viewLifecycleOwner, Observer { showSnackbar(it) }) + actionVM.seError.observe(viewLifecycleOwner, Observer { showSnackbar(it) }) } @Deprecated("Start to use itemViewModel") protected open fun listenChangedItems() { - paramsVM.seChangedItems.observe(viewLifecycleOwner, Observer { itemList -> + actionVM.seChangedItems.observe(viewLifecycleOwner, Observer { itemList -> itemList.forEach { item -> Log.d(this, "item changed from VM - name: ${item.id}") val dataItem = item as? BaseItem ?: return@Observer @@ -114,14 +118,14 @@ abstract class BaseCardActionFragment : BaseFragment(), ActionView { protected open fun listenDescriptionSwitchChanges() { mainActivityVM.ldDescriptionSwitch.observe(viewLifecycleOwner, Observer { - paramsVM.toggleDescriptionVisibility(it) + actionVM.toggleDescriptionVisibility(it) }) } private fun inflateParamView(where: ViewGroup): ViewGroup { val inflater = LayoutInflater.from(where.context) val view = inflater.inflate(R.layout.w_card_incoming_param, where, false) - where.addView(view) + where.plusAssign(view) return view as ViewGroup } diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/PersonalizeFragment.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/PersonalizeFragment.kt index 0a18a0989f..ebf8b92c2a 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/PersonalizeFragment.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/PersonalizeFragment.kt @@ -22,19 +22,19 @@ class PersonalizeFragment : BaseCardActionFragment() { override fun getLayoutId(): Int = R.layout.fg_personalize override fun initFab() { - actionFab.setOnClickListener { paramsVM.invokeMainAction() } + actionFab.setOnClickListener { actionVM.invokeMainAction() } } override fun createWidgets(widgetCreatedCallback: () -> Unit) { Log.d(this, "createWidgets") val itemList = mutableListOf() - paramsVM.ldItemList.observe(viewLifecycleOwner, Observer { list -> + actionVM.ldItemList.observe(viewLifecycleOwner, Observer { list -> Log.d(this, "ldBlockList size: ${list.size}") itemList.clear() itemList.addAll(list) itemList.forEach { WidgetBuilder(PersonalizeItemBuilder()).build(it, itemContainer) } - paramsVM.attachToPayload(mutableMapOf( + actionVM.attachToPayload(mutableMapOf( PayloadKey.actionView to this as ActionView, PayloadKey.itemList to itemList )) @@ -50,7 +50,7 @@ class PersonalizeFragment : BaseCardActionFragment() { } override fun listenEvent() { - paramsVM.seResponseEvent.observe(viewLifecycleOwner, Observer { + actionVM.seResponseEvent.observe(viewLifecycleOwner, Observer { mainActivityVM.changeResponseEvent(it) navigateTo(R.id.action_nav_card_action_to_response_screen) }) diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/scan/ui/ScanActionFragment.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/scan/ui/ScanActionFragment.kt index ff1428465b..c878382e63 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/scan/ui/ScanActionFragment.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/scan/ui/ScanActionFragment.kt @@ -24,7 +24,7 @@ class ScanActionFragment : BaseCardActionFragment() { } override fun listenReadResponse() { - paramsVM.seReadResponse.observe(viewLifecycleOwner, Observer { + actionVM.seReadResponse.observe(viewLifecycleOwner, Observer { navigateTo( R.id.action_nav_card_action_to_response_screen, bundleOf(Pair(BaseCardResponseFragment.response, it)) From 6ea0feb3ee5232ea30970ba7424ed2f1512d2004 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 8 Apr 2020 02:45:50 +0300 Subject: [PATCH 10/39] Updated on 2026-08-14 --- .../converter/fromTo/ItemsToPersonalizeConfig.kt | 10 +++------- .../variants/personalize/ui/widgets/SpinnerWidget.kt | 2 +- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/converter/fromTo/ItemsToPersonalizeConfig.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/converter/fromTo/ItemsToPersonalizeConfig.kt index 6b8083ce29..177ac87463 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/converter/fromTo/ItemsToPersonalizeConfig.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/converter/fromTo/ItemsToPersonalizeConfig.kt @@ -1,11 +1,7 @@ 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.abstraction.* import com.tangem.tangemtest._arch.structure.impl.ListValueWrapper import com.tangem.tangemtest.ucase.variants.personalize.* import com.tangem.tangemtest.ucase.variants.personalize.converter.PersonalizeConfigValuesHolder @@ -25,7 +21,7 @@ class ItemsToPersonalizeConfig : ItemsToModel { } private fun mapListItems(itemList: List) { - itemList.forEach { item -> mapItemToHolder(item) } + itemList.iterate { item -> mapItemToHolder(item) } } private fun mapItemToHolder(item: Item) { @@ -110,7 +106,7 @@ class ItemsToPersonalizeConfig : ItemsToModel { var typedValue = holder.get(id)?.get() typedValue = when (typedValue) { - is ListValueWrapper -> (typedValue.selectedItem as KeyValue).value as Type + is ListValueWrapper -> typedValue.selectedItem as Type else -> typedValue as? Type ?: null } return typedValue diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/SpinnerWidget.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/SpinnerWidget.kt index 8ea3d0f4ac..88736e6517 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/SpinnerWidget.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/SpinnerWidget.kt @@ -29,7 +29,7 @@ class SpinnerWidget(parent: ViewGroup, listItem: ListItem) : DescriptionWidget?) {} override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) { - data.selectedItem = data.itemList[position] + data.selectedItem = data.itemList[position].value dataItem.viewModel.updateDataByView(data) } } From 273f02f256704cf91286e6958e6129eb7284b75b Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 8 Apr 2020 03:02:10 +0300 Subject: [PATCH 11/39] Updated on 2026-08-14 --- .../com/tangem/tangemtest/AppTangemDemo.kt | 6 +++ .../ucase/domain/paramsManager/Factory.kt | 31 ---------------- .../managers/BaseItemsManager.kt | 2 +- .../managers/PersonalizeItemsManager.kt | 30 ++++++++++++++- .../ucase/ui/BaseCardActionFragment.kt | 23 +++++------- .../ui/DepersonalizeActionFragment.kt | 8 ++-- .../personalize/ui/PersonalizeFragment.kt | 37 +++++++++++++++++-- .../variants/scan/ui/ScanActionFragment.kt | 7 ++-- .../variants/sign/ui/SignActionFragment.kt | 7 ++-- 9 files changed, 90 insertions(+), 61 deletions(-) delete mode 100644 tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/Factory.kt diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/AppTangemDemo.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/AppTangemDemo.kt index c9fc162b6a..ab604ee506 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/AppTangemDemo.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/AppTangemDemo.kt @@ -1,6 +1,8 @@ package com.tangem.tangemtest import android.app.Application +import android.content.Context +import android.content.SharedPreferences import com.tangem.tangemtest._arch.structure.ILog import com.tangem.tangemtest._arch.structure.ItemLogger import com.tangem.tangemtest.commons.TangemLogger @@ -23,6 +25,10 @@ class AppTangemDemo : Application() { ILog.setLogger(ItemLogger()) } + fun sharedPreferences(name: String = "DevKitApp", mode: Int = Context.MODE_PRIVATE): SharedPreferences { + return getSharedPreferences(name, mode) + } + companion object { lateinit var appInstance: AppTangemDemo } diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/Factory.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/Factory.kt deleted file mode 100644 index c71097292c..0000000000 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/Factory.kt +++ /dev/null @@ -1,31 +0,0 @@ -package com.tangem.tangemtest.ucase.domain.paramsManager - -import com.tangem.tangemtest.ucase.domain.paramsManager.managers.DepersonalizeItemsManager -import com.tangem.tangemtest.ucase.domain.paramsManager.managers.PersonalizeItemsManager -import com.tangem.tangemtest.ucase.domain.paramsManager.managers.ScanItemsManager -import com.tangem.tangemtest.ucase.domain.paramsManager.managers.SignItemsManager -import com.tangem.tangemtest.ucase.resources.ActionType -import ru.dev.gbixahue.eu4d.lib.android.global.log.Log -import ru.dev.gbixahue.eu4d.lib.kotlin.common.BaseTypedHolder - -/** -[REDACTED_AUTHOR] - */ -class ItemManagersStore : BaseTypedHolder() { - - companion object { - val instance: ItemManagersStore - get() = createFactory() - - private fun createFactory(): ItemManagersStore { - Log.d(this, "New instance was created") - - return ItemManagersStore().apply { - register(ActionType.Scan, ScanItemsManager()) - register(ActionType.Sign, SignItemsManager()) - register(ActionType.Personalize, PersonalizeItemsManager()) - register(ActionType.Depersonalize, DepersonalizeItemsManager()) - } - } - } -} \ No newline at end of file diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/managers/BaseItemsManager.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/managers/BaseItemsManager.kt index c9016165ac..03ccaf4728 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/managers/BaseItemsManager.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/managers/BaseItemsManager.kt @@ -31,7 +31,7 @@ abstract class BaseItemsManager(protected val action: Action) : ItemsManager { applyChangesByAffectedItems(foundItem, callback) } - override fun getItems(): MutableList = itemList.toMutableList() + override fun getItems(): List = itemList override fun getActionByTag(id: Id, cardManager: CardManager): ((ActionCallback) -> Unit)? = null diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/managers/PersonalizeItemsManager.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/managers/PersonalizeItemsManager.kt index c8c0b07380..c0381252bb 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/managers/PersonalizeItemsManager.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/managers/PersonalizeItemsManager.kt @@ -1,19 +1,45 @@ package com.tangem.tangemtest.ucase.domain.paramsManager.managers +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.LifecycleObserver +import androidx.lifecycle.OnLifecycleEvent import com.tangem.CardManager import com.tangem.tangemtest._arch.structure.abstraction.Item import com.tangem.tangemtest.ucase.domain.actions.PersonalizeAction import com.tangem.tangemtest.ucase.domain.paramsManager.ActionCallback import com.tangem.tangemtest.ucase.variants.personalize.converter.PersonalizeConfigConverter import com.tangem.tangemtest.ucase.variants.personalize.dto.PersonalizeConfig +import ru.dev.gbixahue.eu4d.lib.android.global.log.Log /** [REDACTED_AUTHOR] */ -class PersonalizeItemsManager : BaseItemsManager(PersonalizeAction()) { - override fun createItemsList(): List = PersonalizeConfigConverter().convert(PersonalizeConfig()) +class PersonalizeItemsManager( + private val store: Store +) : BaseItemsManager(PersonalizeAction()), LifecycleObserver { + + init { + Log.d(this, "new instance created") + } + private val converter = PersonalizeConfigConverter() + + override fun createItemsList(): List { + val config = store.restore() + return converter.convert(config) + } override fun invokeMainAction(cardManager: CardManager, callback: ActionCallback) { action.executeMainAction(this, getAttrsForAction(cardManager), callback) } + + @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY) + fun onDestroy() { + val config = converter.convert(itemList, PersonalizeConfig()) + store.save(config) + } +} + +interface Store { + fun save(config: M) + fun restore(): M } \ No newline at end of file diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/BaseCardActionFragment.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/BaseCardActionFragment.kt index 63e2c8be82..67242ae326 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/BaseCardActionFragment.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/BaseCardActionFragment.kt @@ -15,10 +15,8 @@ import com.tangem.tangemtest.R import com.tangem.tangemtest._arch.structure.Id import com.tangem.tangemtest._arch.structure.abstraction.BaseItem import com.tangem.tangemtest._main.MainViewModel -import com.tangem.tangemtest.ucase.domain.paramsManager.ItemManagersStore import com.tangem.tangemtest.ucase.domain.paramsManager.ItemsManager 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.ui.widgets.ParameterWidget @@ -30,26 +28,20 @@ import ru.dev.gbixahue.eu4d.lib.android.global.log.Log */ abstract class BaseCardActionFragment : BaseFragment(), ActionView { - protected val itemContainer: ViewGroup - get() = mainView.findViewById(R.id.ll_container) - - protected val actionFab: FloatingActionButton - get() = mainView.findViewById(R.id.fab_action) + protected lateinit var itemContainer: ViewGroup + protected lateinit var actionFab: FloatingActionButton + protected abstract val itemsManager: ItemsManager protected val mainActivityVM by activityViewModels() protected val actionVM: ActionViewModel by viewModels { ActionViewModelFactory(itemsManager) } - protected val itemsManager: ItemsManager by lazy { - Log.d(this, "create ItemManager") - ItemManagersStore.instance.get(getAction())!! - } - private val paramsWidgetList = mutableListOf() override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) Log.d(this, "onViewCreated") + bindViews() viewLifecycleOwner.lifecycle.addObserver(actionVM) actionVM.setCardManager(CardManager.init(requireActivity())) actionVM.attachToPayload(mutableMapOf(PayloadKey.actionView to this as ActionView)) @@ -58,6 +50,11 @@ abstract class BaseCardActionFragment : BaseFragment(), ActionView { createWidgets { subscribeToViewModelChanges() } } + protected open fun bindViews() { + itemContainer = mainView.findViewById(R.id.ll_container) + actionFab = mainView.findViewById(R.id.fab_action) + } + protected open fun initFab() { enableActionFab(false) actionFab.setOnClickListener { actionVM.invokeMainAction() } @@ -140,6 +137,4 @@ abstract class BaseCardActionFragment : BaseFragment(), ActionView { else -> showSnackbar(requireContext().getString(R.string.unknown)) } } - - abstract fun getAction(): ActionType } \ No newline at end of file diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/depersonalize/ui/DepersonalizeActionFragment.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/depersonalize/ui/DepersonalizeActionFragment.kt index 6567e465b1..e2ae9cdbdd 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/depersonalize/ui/DepersonalizeActionFragment.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/depersonalize/ui/DepersonalizeActionFragment.kt @@ -1,7 +1,8 @@ package com.tangem.tangemtest.ucase.variants.depersonalize.ui import com.tangem.tangemtest.R -import com.tangem.tangemtest.ucase.resources.ActionType +import com.tangem.tangemtest.ucase.domain.paramsManager.ItemsManager +import com.tangem.tangemtest.ucase.domain.paramsManager.managers.DepersonalizeItemsManager import com.tangem.tangemtest.ucase.ui.BaseCardActionFragment /** @@ -9,8 +10,7 @@ import com.tangem.tangemtest.ucase.ui.BaseCardActionFragment */ class DepersonalizeActionFragment : BaseCardActionFragment() { + override val itemsManager: ItemsManager by lazy { DepersonalizeItemsManager() } + override fun getLayoutId(): Int = R.layout.fg_depersonalize - - override fun getAction(): ActionType = ActionType.Depersonalize - } \ No newline at end of file diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/PersonalizeFragment.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/PersonalizeFragment.kt index ebf8b92c2a..788d196392 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/PersonalizeFragment.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/PersonalizeFragment.kt @@ -1,16 +1,26 @@ package com.tangem.tangemtest.ucase.variants.personalize.ui +import android.content.Context +import android.content.SharedPreferences +import android.os.Bundle +import android.view.View +import androidx.core.content.edit import androidx.lifecycle.Observer +import com.google.gson.Gson +import com.tangem.tangemtest.AppTangemDemo import com.tangem.tangemtest.R import com.tangem.tangemtest._arch.structure.Id import com.tangem.tangemtest._arch.structure.abstraction.Item import com.tangem.tangemtest._arch.widget.WidgetBuilder +import com.tangem.tangemtest.ucase.domain.paramsManager.ItemsManager import com.tangem.tangemtest.ucase.domain.paramsManager.PayloadKey -import com.tangem.tangemtest.ucase.resources.ActionType +import com.tangem.tangemtest.ucase.domain.paramsManager.managers.PersonalizeItemsManager +import com.tangem.tangemtest.ucase.domain.paramsManager.managers.Store 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.BaseCardActionFragment +import com.tangem.tangemtest.ucase.variants.personalize.dto.PersonalizeConfig import com.tangem.tangemtest.ucase.variants.personalize.ui.widgets.PersonalizeItemBuilder import ru.dev.gbixahue.eu4d.lib.android.global.log.Log @@ -19,8 +29,15 @@ import ru.dev.gbixahue.eu4d.lib.android.global.log.Log */ class PersonalizeFragment : BaseCardActionFragment() { + override val itemsManager: ItemsManager by lazy { PersonalizeItemsManager(PersonalizationConfigStore(requireContext())) } + override fun getLayoutId(): Int = R.layout.fg_personalize + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + lifecycle.addObserver(itemsManager as PersonalizeItemsManager) + } + override fun initFab() { actionFab.setOnClickListener { actionVM.invokeMainAction() } } @@ -64,8 +81,23 @@ class PersonalizeFragment : BaseCardActionFragment() { else -> showSnackbar(requireContext().getString(R.string.unknown)) } } +} - override fun getAction(): ActionType = ActionType.Personalize +class PersonalizationConfigStore(context: Context) : Store { + + private val key = "personalization_config" + + private val sp: SharedPreferences = (context.applicationContext as AppTangemDemo).sharedPreferences() + private val gson: Gson = Gson() + + override fun save(config: PersonalizeConfig) { + sp.edit(true) { putString(key, gson.toJson(config)) } + } + + override fun restore(): PersonalizeConfig { + val json = sp.getString(key, gson.toJson(PersonalizeConfig())) + return gson.fromJson(json, PersonalizeConfig::class.java) + } } @@ -73,4 +105,3 @@ class PersonalizeFragment : BaseCardActionFragment() { - diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/scan/ui/ScanActionFragment.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/scan/ui/ScanActionFragment.kt index c878382e63..1edc4c72de 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/scan/ui/ScanActionFragment.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/scan/ui/ScanActionFragment.kt @@ -5,7 +5,8 @@ import android.view.View import androidx.core.os.bundleOf import androidx.lifecycle.Observer import com.tangem.tangemtest.R -import com.tangem.tangemtest.ucase.resources.ActionType +import com.tangem.tangemtest.ucase.domain.paramsManager.ItemsManager +import com.tangem.tangemtest.ucase.domain.paramsManager.managers.ScanItemsManager import com.tangem.tangemtest.ucase.ui.BaseCardActionFragment import com.tangem.tangemtest.ucase.ui.response.BaseCardResponseFragment @@ -14,9 +15,9 @@ import com.tangem.tangemtest.ucase.ui.response.BaseCardResponseFragment */ class ScanActionFragment : BaseCardActionFragment() { - override fun getLayoutId(): Int = R.layout.fg_action_card_scan + override val itemsManager: ItemsManager by lazy { ScanItemsManager() } - override fun getAction(): ActionType = ActionType.Scan + override fun getLayoutId(): Int = R.layout.fg_action_card_scan override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/sign/ui/SignActionFragment.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/sign/ui/SignActionFragment.kt index 9ce99ee075..1699672834 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/sign/ui/SignActionFragment.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/sign/ui/SignActionFragment.kt @@ -1,7 +1,8 @@ package com.tangem.tangemtest.ucase.variants.sign.ui import com.tangem.tangemtest.R -import com.tangem.tangemtest.ucase.resources.ActionType +import com.tangem.tangemtest.ucase.domain.paramsManager.ItemsManager +import com.tangem.tangemtest.ucase.domain.paramsManager.managers.SignItemsManager import com.tangem.tangemtest.ucase.ui.BaseCardActionFragment /** @@ -9,7 +10,7 @@ import com.tangem.tangemtest.ucase.ui.BaseCardActionFragment */ class SignActionFragment : BaseCardActionFragment() { - override fun getLayoutId(): Int = R.layout.fg_action_card_sign + override val itemsManager: ItemsManager by lazy { SignItemsManager() } - override fun getAction(): ActionType = ActionType.Sign + override fun getLayoutId(): Int = R.layout.fg_action_card_sign } \ No newline at end of file From 87bd34abd9eb7b83e1fc1fecc47b4d50fa4cc64d Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 8 Apr 2020 10:44:13 +0300 Subject: [PATCH 12/39] Updated on 2026-08-14 --- app/src/main/java/com/tangem/App.kt | 4 +++- app/src/main/java/com/tangem/ui/fragment/SettingsFragment.kt | 2 +- app/src/main/java/com/tangem/util/Analytics.kt | 1 + 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/tangem/App.kt b/app/src/main/java/com/tangem/App.kt index b5a4451cb7..ef34464937 100644 --- a/app/src/main/java/com/tangem/App.kt +++ b/app/src/main/java/com/tangem/App.kt @@ -80,8 +80,10 @@ class App : Application() { ) } - FirebaseAnalytics.getInstance(this).setAnalyticsCollectionEnabled(Analytics.isEnabled()) FirebaseCrashlytics.getInstance().setCrashlyticsCollectionEnabled(Analytics.isEnabled()) + FirebaseAnalytics.getInstance(this).setAnalyticsCollectionEnabled( + Analytics.isEnabled() && !BuildConfig.DEBUG + ) } private fun buildToastHelperComponent(): ToastHelperComponent { diff --git a/app/src/main/java/com/tangem/ui/fragment/SettingsFragment.kt b/app/src/main/java/com/tangem/ui/fragment/SettingsFragment.kt index 98b57d7910..23cdd5d09b 100644 --- a/app/src/main/java/com/tangem/ui/fragment/SettingsFragment.kt +++ b/app/src/main/java/com/tangem/ui/fragment/SettingsFragment.kt @@ -53,7 +53,7 @@ class SettingsFragment : PreferenceFragmentCompat(), SharedPreferences.OnSharedP if (!Analytics.isEnabled()) { analytics.logEvent(AnalyticsEvent.ANALYTICS_TURNED_OFF.event, bundleOf()) } - analytics.setAnalyticsCollectionEnabled(Analytics.isEnabled()) + analytics.setAnalyticsCollectionEnabled(Analytics.isEnabled() && !BuildConfig.DEBUG) } FirebaseCrashlytics.getInstance().setCrashlyticsCollectionEnabled(Analytics.isEnabled()) diff --git a/app/src/main/java/com/tangem/util/Analytics.kt b/app/src/main/java/com/tangem/util/Analytics.kt index c01d40e340..d1120edf6e 100644 --- a/app/src/main/java/com/tangem/util/Analytics.kt +++ b/app/src/main/java/com/tangem/util/Analytics.kt @@ -22,6 +22,7 @@ enum class AnalyticsParam(val param: String) { FIRMWARE("firmware"), ; } + class Analytics { companion object { fun setCardData(ctx: TangemContext): Bundle { From f9b627bd62c273b04d336b3b7a1f6173f2c7bdf3 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 8 Apr 2020 20:58:06 +0300 Subject: [PATCH 13/39] Updated on 2026-08-14 --- .idea/codeStyles/Project.xml | 116 ----------------------------------- 1 file changed, 116 deletions(-) delete mode 100644 .idea/codeStyles/Project.xml diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml deleted file mode 100644 index 681f41ae2a..0000000000 --- a/.idea/codeStyles/Project.xml +++ /dev/null @@ -1,116 +0,0 @@ - - - - - - - -
- - - - xmlns:android - - ^$ - - - -
-
- - - - xmlns:.* - - ^$ - - - BY_NAME - -
-
- - - - .*:id - - http://schemas.android.com/apk/res/android - - - -
-
- - - - .*:name - - http://schemas.android.com/apk/res/android - - - -
-
- - - - name - - ^$ - - - -
-
- - - - style - - ^$ - - - -
-
- - - - .* - - ^$ - - - BY_NAME - -
-
- - - - .* - - http://schemas.android.com/apk/res/android - - - ANDROID_ATTRIBUTE_ORDER - -
-
- - - - .* - - .* - - - BY_NAME - -
-
-
-
-
-
\ No newline at end of file From 6e762cc7fffc5b42e999059792bc6072d62fe0d6 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 8 Apr 2020 23:04:48 +0300 Subject: [PATCH 14/39] Updated on 2026-08-14 --- .../tangem/tangemtest/_arch/structure/Ids.kt | 2 + .../_arch/widget/abstraction/ViewWidget.kt | 7 + .../ucase/resources/MainResourceHolder.kt | 4 +- .../initializers/ResponseResources.kt | 53 +++++++ .../personalize/ui/widgets/BlockHeadWidget.kt | 4 +- .../ui/widgets/DescriptionWidget.kt | 15 +- .../personalize/ui/widgets/EditTextWidget.kt | 6 +- .../personalize/ui/widgets/NumberWidget.kt | 6 +- .../personalize/ui/widgets/SpinnerWidget.kt | 4 +- .../personalize/ui/widgets/SwitchWidget.kt | 6 +- .../ucase/variants/responses/Ids.kt | 44 ++++++ .../variants/responses/ResponseViewModel.kt | 41 ++++++ .../responses/converter/CardConverter.kt | 85 +++++++++++ .../responses/converter/ConvertersStore.kt | 17 +++ .../responses/converter/SimpleConverters.kt | 35 +++++ .../variants/responses/ui/ResponseFragment.kt | 56 +++++++ .../responses/ui/widget/CheckBoxWidget.kt | 25 ++++ .../ui/widget}/ResponseItemBuilder.kt | 5 +- .../ui/widget}/ResponseTextWidget.kt | 25 +++- .../variants/scan/ui/ScanActionFragment.kt | 4 +- .../src/main/res/layout/fg_card_response.xml | 8 +- .../src/main/res/layout/w_response_item.xml | 24 ++- .../res/layout/w_response_item_checkbox.xml | 41 ++++++ .../src/main/res/navigation/navigation.xml | 2 +- tangem-demo/src/main/res/values/colors.xml | 5 +- tangem-demo/src/main/res/values/strings.xml | 2 +- .../src/main/res/values/strings_response.xml | 138 ++++++++++++++++++ 27 files changed, 611 insertions(+), 53 deletions(-) create mode 100644 tangem-demo/src/main/java/com/tangem/tangemtest/ucase/resources/initializers/ResponseResources.kt create mode 100644 tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/responses/Ids.kt create mode 100644 tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/responses/ResponseViewModel.kt create mode 100644 tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/responses/converter/CardConverter.kt create mode 100644 tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/responses/converter/ConvertersStore.kt create mode 100644 tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/responses/converter/SimpleConverters.kt create mode 100644 tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/responses/ui/ResponseFragment.kt create mode 100644 tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/responses/ui/widget/CheckBoxWidget.kt rename tangem-demo/src/main/java/com/tangem/tangemtest/ucase/{ui => variants/responses/ui/widget}/ResponseItemBuilder.kt (75%) rename tangem-demo/src/main/java/com/tangem/tangemtest/ucase/{ui/widgets => variants/responses/ui/widget}/ResponseTextWidget.kt (53%) create mode 100644 tangem-demo/src/main/res/layout/w_response_item_checkbox.xml create mode 100644 tangem-demo/src/main/res/values/strings_response.xml diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/structure/Ids.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/structure/Ids.kt index 3b40edcc77..ce27853356 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/structure/Ids.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/structure/Ids.kt @@ -5,6 +5,8 @@ package com.tangem.tangemtest._arch.structure */ interface Id +class StringId(val name: String) : Id + enum class Additional : Id { UNDEFINED, JSON_INCOMING, diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/widget/abstraction/ViewWidget.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/widget/abstraction/ViewWidget.kt index b3ca53c3a0..7fa8fd5bbb 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/widget/abstraction/ViewWidget.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/widget/abstraction/ViewWidget.kt @@ -5,10 +5,12 @@ import android.view.View import android.view.ViewGroup import com.tangem.tangemtest.R import com.tangem.tangemtest._arch.structure.DataHolder +import com.tangem.tangemtest._arch.structure.StringId import com.tangem.tangemtest._arch.structure.abstraction.BaseItem import com.tangem.tangemtest._arch.structure.abstraction.ItemViewModel import com.tangem.tangemtest.ucase.resources.MainResourceHolder import com.tangem.tangemtest.ucase.resources.Resources +import ru.dev.gbixahue.eu4d.lib.android._android.views.stringFrom import ru.dev.gbixahue.eu4d.lib.kotlin.common.LayoutHolder /** @@ -46,6 +48,11 @@ fun DataWidget<*>.getResNameId(): Int = MainResourceHolder.safeGet(da fun DataWidget<*>.getResDescription(): Int? = MainResourceHolder.safeGet(dataItem.id).resDescription +fun DataWidget<*>.getName(): String { + val id = dataItem.id + return if (id is StringId) id.name else view.stringFrom(getResNameId()) +} + internal fun inflate(id: Int, parent: ViewGroup): View { val layoutId = if (id <= 0) R.layout.w_empty else id val inflatedView = LayoutInflater.from(parent.context).inflate(layoutId, parent, false) diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/resources/MainResourceHolder.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/resources/MainResourceHolder.kt index 90b908b742..44028d9018 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/resources/MainResourceHolder.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/resources/MainResourceHolder.kt @@ -4,6 +4,7 @@ import com.tangem.tangemtest.R import com.tangem.tangemtest._arch.structure.Id import com.tangem.tangemtest.ucase.resources.initializers.ActionResources import com.tangem.tangemtest.ucase.resources.initializers.PersonalizeResources +import com.tangem.tangemtest.ucase.resources.initializers.ResponseResources import com.tangem.tangemtest.ucase.resources.initializers.TlvResources import ru.dev.gbixahue.eu4d.lib.kotlin.common.BaseTypedHolder @@ -17,10 +18,11 @@ object MainResourceHolder : ResourceHolder() { PersonalizeResources().init(this) ActionResources().init(this) TlvResources().init(this) + ResponseResources().init(this) } inline fun safeGet(id: Id): Res { val result = super.get(id) ?: Resources(R.string.unknown, R.string.unknown) return result as Res } -} +} \ 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 new file mode 100644 index 0000000000..a9ad0a3d69 --- /dev/null +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/resources/initializers/ResponseResources.kt @@ -0,0 +1,53 @@ +package com.tangem.tangemtest.ucase.resources.initializers + +import com.tangem.tangemtest.R +import com.tangem.tangemtest.ucase.resources.MainResourceHolder +import com.tangem.tangemtest.ucase.resources.Resources +import com.tangem.tangemtest.ucase.variants.responses.CardDataId +import com.tangem.tangemtest.ucase.variants.responses.CardId + +/** +[REDACTED_AUTHOR] + */ +class ResponseResources { + fun init(holder: MainResourceHolder) { + initCard(holder) + initCardData(holder) + } + + private fun initCard(holder: MainResourceHolder) { + holder.register(CardId.cardId, Resources(R.string.response_card_cid, R.string.info_response_card_cid)) + holder.register(CardId.manufacturerName, Resources(R.string.response_card_manufacturer_name, R.string.info_response_card_manufacturer_name)) + holder.register(CardId.status, Resources(R.string.response_card_status, R.string.info_response_card_status)) + holder.register(CardId.firmwareVersion, Resources(R.string.response_card_firmware_version, R.string.info_response_card_firmware_version)) + holder.register(CardId.cardPublicKey, Resources(R.string.response_card_public_key, R.string.info_response_card_public_key)) + holder.register(CardId.settingsMask, Resources(R.string.response_card_settings_mask, R.string.info_response_card_settings_mask)) + holder.register(CardId.cardData, Resources(R.string.response_card_card_data, R.string.info_response_card_card_data)) + 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.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)) + holder.register(CardId.walletSignedHashes, Resources(R.string.response_card_wallet_signed_hashes, R.string.info_response_card_wallet_signed_hashes)) + holder.register(CardId.health, Resources(R.string.response_card_health, R.string.info_response_card_health)) + holder.register(CardId.isActivated, Resources(R.string.response_card_is_activated, R.string.info_response_card_is_activated)) + holder.register(CardId.activationSeed, Resources(R.string.response_card_activation_seed, R.string.info_response_card_activation_seed)) + holder.register(CardId.paymentFlowVersion, Resources(R.string.response_card_payment_flow_version, R.string.info_response_card_payment_flow_version)) + holder.register(CardId.userCounter, Resources(R.string.response_card_user_counter, R.string.info_response_card_user_counter)) + holder.register(CardId.userProtectedCounter, Resources(R.string.response_card_user_protected_counter, R.string.info_response_card_user_protected_counter)) + } + + private fun initCardData(holder: MainResourceHolder) { + holder.register(CardDataId.batchId, Resources(R.string.response_card_card_data_batch_id, R.string.info_response_card_card_data_batch_id)) + holder.register(CardDataId.manufactureDateTime, Resources(R.string.response_card_card_data_manufacture_date_time, R.string.info_response_card_card_data_manufacture_date_time)) + holder.register(CardDataId.issuerName, Resources(R.string.response_card_card_data_issuer_name, R.string.info_response_card_card_data_issuer_name)) + holder.register(CardDataId.blockchainName, Resources(R.string.response_card_card_data_blockchain_name, R.string.info_response_card_card_data_blockchain_name)) + holder.register(CardDataId.manufacturerSignature, Resources(R.string.response_card_card_data_manufacturer_signature, R.string.info_response_card_card_data_manufacturer_signature)) + holder.register(CardDataId.productMask, Resources(R.string.response_card_card_data_product_mask, R.string.info_response_card_card_data_product_mask)) + holder.register(CardDataId.tokenSymbol, Resources(R.string.response_card_card_data_token_symbol, R.string.info_response_card_card_data_token_symbol)) + holder.register(CardDataId.tokenContractAddress, Resources(R.string.response_card_card_data_token_contract_address, R.string.info_response_card_card_data_token_contract_address)) + holder.register(CardDataId.tokenDecimal, Resources(R.string.response_card_card_data_token_decimal, R.string.info_response_card_card_data_token_decimal)) + } +} \ No newline at end of file diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/BlockHeadWidget.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/BlockHeadWidget.kt index 2f2dde5d72..4b0837723b 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/BlockHeadWidget.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/BlockHeadWidget.kt @@ -4,7 +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._arch.widget.abstraction.getResNameId +import com.tangem.tangemtest._arch.widget.abstraction.getName /** [REDACTED_AUTHOR] @@ -15,6 +15,6 @@ class BlockHeadWidget(parent: ViewGroup, data: TextItem) : DescriptionWidget(R.id.tv_name) init { - tvName?.setText(getResNameId()) + tvName.text = getName() } } \ No newline at end of file diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/DescriptionWidget.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/DescriptionWidget.kt index 70107becdb..9dfff32c48 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/DescriptionWidget.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/DescriptionWidget.kt @@ -30,7 +30,7 @@ abstract class DescriptionWidget( dataItem.viewModel.viewState.onDescriptionVisibilityChanged = { changeDescriptionVisibility(it) } } - protected fun changeDescriptionVisibility(state: Int) { + protected open fun changeDescriptionVisibility(state: Int) { val tv = tvDescription ?: return val descriptionId = getResDescription() ?: return val description = tv.context.getString(descriptionId) @@ -40,15 +40,4 @@ abstract class DescriptionWidget( TransitionManager.beginDelayedTransition(view.parent as ViewGroup, AutoTransition()) descriptionContainer.visibility = state } -} - - - - - - - - - - - +} \ No newline at end of file diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/EditTextWidget.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/EditTextWidget.kt index 791e80b048..9cb1061414 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/EditTextWidget.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/EditTextWidget.kt @@ -7,7 +7,7 @@ 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.EditTextItem -import com.tangem.tangemtest._arch.widget.abstraction.getResNameId +import com.tangem.tangemtest._arch.widget.abstraction.getName import ru.dev.gbixahue.eu4d.lib.android._android.views.moveCursorToEnd import ru.dev.gbixahue.eu4d.lib.kotlin.stringOf @@ -30,8 +30,8 @@ class EditTextWidget(parent: ViewGroup, data: EditTextItem) : DescriptionWidget< } init { - tilItem.hint = tilItem.context.getString(getResNameId()) - etItem.setText(dataItem.viewModel.data) + tilItem.hint = getName() + etItem.setText(dataItem.getData()) etItem.addTextChangedListener(watcher) dataItem.viewModel.onDataUpdated = { silentUpdate(it) } } diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/NumberWidget.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/NumberWidget.kt index 1b72f5f21d..e2702465a4 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/NumberWidget.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/NumberWidget.kt @@ -8,7 +8,7 @@ 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._arch.widget.abstraction.getResNameId +import com.tangem.tangemtest._arch.widget.abstraction.getName import com.tangem.tangemtest.ucase.variants.personalize.CardNumber import ru.dev.gbixahue.eu4d.lib.android._android.views.addInputFilter import ru.dev.gbixahue.eu4d.lib.android._android.views.moveCursorToEnd @@ -33,12 +33,12 @@ class NumberWidget(parent: ViewGroup, data: NumberItem) : DescriptionWidget(R.id.tv_name) - name.setText(getResNameId()) + name.text = getName() spItem.adapter = spAdapter spAdapter.getItemPosition(stringOf(data.selectedItem))?.let { spItem.setSelection(it) diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/SwitchWidget.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/SwitchWidget.kt index 35a12ff069..015ea477b2 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/SwitchWidget.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/SwitchWidget.kt @@ -5,7 +5,7 @@ import android.widget.CompoundButton import androidx.appcompat.widget.SwitchCompat import com.tangem.tangemtest.R import com.tangem.tangemtest._arch.structure.impl.BoolItem -import com.tangem.tangemtest._arch.widget.abstraction.getResNameId +import com.tangem.tangemtest._arch.widget.abstraction.getName /** [REDACTED_AUTHOR] @@ -20,8 +20,8 @@ class SwitchWidget(parent: ViewGroup, data: BoolItem) : DescriptionWidget? = null + + fun createItemList(taskEvent: TaskEvent<*>): List { + Log.d(this, "createItemList: itemList size: ${itemList?.size ?: 0}") + + val event = taskEvent as? TaskEvent.Event ?: return emptyList() + val type = event.data::class.java + val converter = convertersHolder.get(type) as? ModelToItems ?: return emptyList() + itemList = converter.convert(event.data) + return itemList!! + } + + fun toggleDescriptionVisibility(state: Boolean) { + itemList?.iterate { + val baseItem = it as? BaseItem<*> ?: return@iterate + baseItem.viewModel.viewState.descriptionVisibility = if (state) View.VISIBLE else View.GONE + } + } +} + + + + 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 new file mode 100644 index 0000000000..492989ebe1 --- /dev/null +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/responses/converter/CardConverter.kt @@ -0,0 +1,85 @@ +package com.tangem.tangemtest.ucase.variants.responses.converter + +import com.tangem.commands.Card +import com.tangem.commands.Settings +import com.tangem.commands.SettingsMask +import com.tangem.tangemtest._arch.structure.Id +import com.tangem.tangemtest._arch.structure.StringId +import com.tangem.tangemtest._arch.structure.abstraction.Item +import com.tangem.tangemtest._arch.structure.abstraction.ListItemBlock +import com.tangem.tangemtest._arch.structure.abstraction.ModelToItems +import com.tangem.tangemtest._arch.structure.impl.BoolItem +import com.tangem.tangemtest._arch.structure.impl.TextItem +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 + +/** +[REDACTED_AUTHOR] + */ +class CardConverter : ModelToItems { + override fun convert(from: Card): List { + val itemList = mutableListOf() +// val holder = GsonInitializer() +// itemList.add(TextItem(Additional.JSON_INCOMING, holder.gson.toJson(from))) + + itemList.add(simpleFields(from)) + itemList.add(cardData(from)) + itemList.add(settingsMask(from.settingsMask)) + return itemList + } + + private fun simpleFields(from: Card): Item { + val block = createBlock(BlockId.Common) + block.addItem(TextItem(CardId.cardId, from.cardId)) + block.addItem(TextItem(CardId.manufacturerName, from.manufacturerName)) + block.addItem(TextItem(CardId.status, stringOf(from.status))) + block.addItem(TextItem(CardId.firmwareVersion, from.firmwareVersion)) + block.addItem(TextItem(CardId.cardPublicKey, stringOf(from.cardPublicKey))) + block.addItem(TextItem(CardId.issuerPublicKey, stringOf(from.issuerPublicKey))) + block.addItem(TextItem(CardId.curve, stringOf(from.curve))) + block.addItem(TextItem(CardId.maxSignatures, stringOf(from.maxSignatures))) + block.addItem(TextItem(CardId.signingMethod, stringOf(from.signingMethod))) + block.addItem(TextItem(CardId.pauseBeforePin2, stringOf(from.pauseBeforePin2))) + block.addItem(TextItem(CardId.walletPublicKey, stringOf(from.walletPublicKey))) + block.addItem(TextItem(CardId.walletRemainingSignatures, stringOf(from.walletRemainingSignatures))) + block.addItem(TextItem(CardId.walletSignedHashes, stringOf(from.walletSignedHashes))) + block.addItem(TextItem(CardId.health, stringOf(from.health))) + block.addItem(TextItem(CardId.isActivated, stringOf(from.isActivated))) + block.addItem(TextItem(CardId.activationSeed, stringOf(from.activationSeed))) + block.addItem(TextItem(CardId.paymentFlowVersion, stringOf(from.paymentFlowVersion))) + block.addItem(TextItem(CardId.userCounter, stringOf(from.userCounter))) +// block.addItem(TextItem(CardId.UserProtectedCounter, stringOf(from.userProtectedCounter))) + return block + } + + private fun cardData(from: Card): Item { + val block = createBlock(BlockId.Common) + val data = from.cardData ?: return block + + val itemList = block.itemList + itemList.add(TextItem(CardDataId.batchId, data.batchId)) +// Format: Year (2 bytes) | Month (1 byte) | Day (1 byte) + itemList.add(TextItem(CardDataId.manufactureDateTime, stringOf(data.manufactureDateTime))) + itemList.add(TextItem(CardDataId.issuerName, data.issuerName)) + itemList.add(TextItem(CardDataId.blockchainName, data.blockchainName)) + itemList.add(TextItem(CardDataId.manufacturerSignature, stringOf(data.manufacturerSignature))) + itemList.add(TextItem(CardDataId.productMask, stringOf(data.productMask))) + itemList.add(TextItem(CardDataId.tokenSymbol, data.tokenSymbol)) + itemList.add(TextItem(CardDataId.tokenContractAddress, data.tokenContractAddress)) + itemList.add(TextItem(CardDataId.tokenDecimal, data.tokenSymbol)) + + return block + } + + private fun settingsMask(from: SettingsMask?): Item { + val block = createBlock(CardId.settingsMask) + val data = from ?: return block + + Settings.values().forEach { block.addItem(BoolItem(StringId(it.name), data.contains(it))) } + return block + } + + private fun createBlock(id: Id): ListItemBlock = ListItemBlock(id).apply { addItem(TextItem(id)) } +} \ No newline at end of file diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/responses/converter/ConvertersStore.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/responses/converter/ConvertersStore.kt new file mode 100644 index 0000000000..9b1dc76a49 --- /dev/null +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/responses/converter/ConvertersStore.kt @@ -0,0 +1,17 @@ +package com.tangem.tangemtest.ucase.variants.responses.converter + +import com.tangem.commands.Card +import com.tangem.commands.SignResponse +import com.tangem.commands.personalization.DepersonalizeResponse +import com.tangem.tasks.ScanEvent +import ru.dev.gbixahue.eu4d.lib.kotlin.common.BaseTypedHolder +import java.lang.reflect.Type + +class ConvertersStore : BaseTypedHolder() { + init { + register(ScanEvent.OnReadEvent::class.java, ReadEventConverter()) + register(SignResponse::class.java, SignResponseConverter()) + register(Card::class.java, CardConverter()) + register(DepersonalizeResponse::class.java, DepersonalizeResponseConverter()) + } +} \ No newline at end of file diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/responses/converter/SimpleConverters.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/responses/converter/SimpleConverters.kt new file mode 100644 index 0000000000..a1c997465b --- /dev/null +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/responses/converter/SimpleConverters.kt @@ -0,0 +1,35 @@ +package com.tangem.tangemtest.ucase.variants.responses.converter + +import com.tangem.commands.SignResponse +import com.tangem.commands.personalization.DepersonalizeResponse +import com.tangem.tangemtest._arch.structure.StringId +import com.tangem.tangemtest._arch.structure.abstraction.Item +import com.tangem.tangemtest._arch.structure.abstraction.ModelToItems +import com.tangem.tangemtest._arch.structure.impl.TextItem +import com.tangem.tasks.ScanEvent +import ru.dev.gbixahue.eu4d.lib.kotlin.stringOf + +/** +[REDACTED_AUTHOR] + */ +class ReadEventConverter : ModelToItems { + override fun convert(from: ScanEvent.OnReadEvent): List = CardConverter().convert(from.card) +} + +class SignResponseConverter : ModelToItems { + + override fun convert(from: SignResponse): List { + return listOf( + TextItem(StringId("CID"), from.cardId), + TextItem(StringId("Wallet signed hashes"), stringOf(from.walletSignedHashes)), + TextItem(StringId("Wallet remaining signatures"), stringOf(from.walletRemainingSignatures)), + TextItem(StringId("Signature"), stringOf(from.signature)) + ) + } +} + +class DepersonalizeResponseConverter : ModelToItems { + override fun convert(from: DepersonalizeResponse): List { + return listOf(TextItem(StringId("Is success"), stringOf(from.success))) + } +} \ No newline at end of file diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/responses/ui/ResponseFragment.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/responses/ui/ResponseFragment.kt new file mode 100644 index 0000000000..e3009a8787 --- /dev/null +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/responses/ui/ResponseFragment.kt @@ -0,0 +1,56 @@ +package com.tangem.tangemtest.ucase.variants.responses.ui + +import android.os.Bundle +import android.view.View +import android.view.ViewGroup +import android.widget.LinearLayout +import androidx.fragment.app.activityViewModels +import androidx.fragment.app.viewModels +import androidx.lifecycle.Observer +import com.tangem.tangemtest.R +import com.tangem.tangemtest._arch.widget.WidgetBuilder +import com.tangem.tangemtest._main.MainViewModel +import com.tangem.tangemtest.ucase.ui.BaseFragment +import com.tangem.tangemtest.ucase.variants.responses.ResponseViewModel +import com.tangem.tangemtest.ucase.variants.responses.ui.widget.ResponseItemBuilder +import ru.dev.gbixahue.eu4d.lib.android.global.log.Log + +/** +[REDACTED_AUTHOR] + */ +open class ResponseFragment : BaseFragment() { + + companion object Arg { + val response = "response" + } + + private val mainActivityVM: MainViewModel by activityViewModels() + private val selfVM: ResponseViewModel by viewModels() + + private val itemContainer: ViewGroup by lazy { mainView.findViewById(R.id.ll_container) } + + override fun getLayoutId(): Int = R.layout.fg_card_response + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + mainActivityVM.ldResponseEvent.observe(viewLifecycleOwner, Observer { event -> + val builder = WidgetBuilder(ResponseItemBuilder()) + val itemList = selfVM.createItemList(event) + itemList.forEach { builder.build(it, itemContainer) } + }) + subscribeToViewModelChanges() + } + + private fun subscribeToViewModelChanges() { + Log.d(this, "subscribeToViewModelChanges") + listenDescriptionSwitchChanges() + } + + + private fun listenDescriptionSwitchChanges() { + mainActivityVM.ldDescriptionSwitch.observe(viewLifecycleOwner, Observer { + selfVM.toggleDescriptionVisibility(it) + }) + } +} diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/responses/ui/widget/CheckBoxWidget.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/responses/ui/widget/CheckBoxWidget.kt new file mode 100644 index 0000000000..5350ab0135 --- /dev/null +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/responses/ui/widget/CheckBoxWidget.kt @@ -0,0 +1,25 @@ +package com.tangem.tangemtest.ucase.variants.responses.ui.widget + +import android.view.ViewGroup +import com.google.android.material.checkbox.MaterialCheckBox +import com.tangem.tangemtest.R +import com.tangem.tangemtest._arch.structure.impl.BoolItem +import com.tangem.tangemtest._arch.widget.abstraction.getName +import com.tangem.tangemtest.ucase.variants.personalize.ui.widgets.DescriptionWidget +import ru.dev.gbixahue.eu4d.lib.android._android.views.colorFrom + +/** +[REDACTED_AUTHOR] + */ +class CheckBoxWidget(parent: ViewGroup, private val boolItem: BoolItem) : DescriptionWidget(parent, boolItem) { + override fun getLayoutId(): Int = R.layout.w_response_item_checkbox + + private val switchItem = view.findViewById(R.id.sw_item) + + init { + switchItem.text = getName() + switchItem.isChecked = dataItem.getData() ?: false + switchItem.isEnabled = false + switchItem.setTextColor(switchItem.colorFrom(R.color.action_name)) + } +} \ No newline at end of file diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/ResponseItemBuilder.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/responses/ui/widget/ResponseItemBuilder.kt similarity index 75% rename from tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/ResponseItemBuilder.kt rename to tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/responses/ui/widget/ResponseItemBuilder.kt index dd80c57862..cde044dc2c 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/ResponseItemBuilder.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/responses/ui/widget/ResponseItemBuilder.kt @@ -1,11 +1,11 @@ -package com.tangem.tangemtest.ucase.ui +package com.tangem.tangemtest.ucase.variants.responses.ui.widget import android.view.ViewGroup import com.tangem.tangemtest._arch.structure.abstraction.BaseItem +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.ui.widgets.ResponseTextWidget /** [REDACTED_AUTHOR] @@ -14,6 +14,7 @@ class ResponseItemBuilder : ItemWidgetBuilder { override fun build(item: BaseItem<*>, parent: ViewGroup): ViewWidget? { return when (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/ui/widgets/ResponseTextWidget.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/responses/ui/widget/ResponseTextWidget.kt similarity index 53% rename from tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/widgets/ResponseTextWidget.kt rename to tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/responses/ui/widget/ResponseTextWidget.kt index fde72c3d54..468b9963e7 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/widgets/ResponseTextWidget.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/responses/ui/widget/ResponseTextWidget.kt @@ -1,27 +1,38 @@ -package com.tangem.tangemtest.ucase.ui.widgets +package com.tangem.tangemtest.ucase.variants.responses.ui.widget import android.content.ClipData import android.content.ClipboardManager import android.content.Context +import android.view.View 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._arch.widget.abstraction.getResNameId +import com.tangem.tangemtest._arch.widget.abstraction.getName import com.tangem.tangemtest.ucase.variants.personalize.ui.widgets.DescriptionWidget /** [REDACTED_AUTHOR] */ -class ResponseTextWidget(parent: ViewGroup, textItem: TextItem) : DescriptionWidget(parent, textItem) { +class ResponseTextWidget(parent: ViewGroup, private val textItem: TextItem) : DescriptionWidget(parent, textItem) { override fun getLayoutId(): Int = R.layout.w_response_item - private val tvName: TextView by lazy { view.findViewById(R.id.tv_name) } - private val tvValue: TextView by lazy { view.findViewById(R.id.tv_value) } + private val tvName: TextView = view.findViewById(R.id.tv_name) + private val tvValue: TextView = view.findViewById(R.id.tv_value) init { - tvName.setText(getResNameId()) - tvValue.text = dataItem.getData() + initWidgets() + } + + private fun initWidgets() { + val data = textItem.getData() + if (data == null || data.isEmpty()) { + view.visibility = View.GONE + return + } + + tvName.text = getName() + tvValue.text = data view.setOnClickListener { val clipboard = view.context.getSystemService(Context.CLIPBOARD_SERVICE) as? ClipboardManager diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/scan/ui/ScanActionFragment.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/scan/ui/ScanActionFragment.kt index 1edc4c72de..3ec710a4b7 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/scan/ui/ScanActionFragment.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/scan/ui/ScanActionFragment.kt @@ -8,7 +8,7 @@ import com.tangem.tangemtest.R import com.tangem.tangemtest.ucase.domain.paramsManager.ItemsManager import com.tangem.tangemtest.ucase.domain.paramsManager.managers.ScanItemsManager import com.tangem.tangemtest.ucase.ui.BaseCardActionFragment -import com.tangem.tangemtest.ucase.ui.response.BaseCardResponseFragment +import com.tangem.tangemtest.ucase.variants.responses.ui.ResponseFragment /** [REDACTED_AUTHOR] @@ -28,7 +28,7 @@ class ScanActionFragment : BaseCardActionFragment() { actionVM.seReadResponse.observe(viewLifecycleOwner, Observer { navigateTo( R.id.action_nav_card_action_to_response_screen, - bundleOf(Pair(BaseCardResponseFragment.response, it)) + bundleOf(Pair(ResponseFragment.response, it)) ) }) } diff --git a/tangem-demo/src/main/res/layout/fg_card_response.xml b/tangem-demo/src/main/res/layout/fg_card_response.xml index f9aeaa5923..357f9c82c6 100644 --- a/tangem-demo/src/main/res/layout/fg_card_response.xml +++ b/tangem-demo/src/main/res/layout/fg_card_response.xml @@ -1,8 +1,8 @@ - + android:layout_height="match_parent"> - \ No newline at end of file + \ No newline at end of file diff --git a/tangem-demo/src/main/res/layout/w_response_item.xml b/tangem-demo/src/main/res/layout/w_response_item.xml index c44aa9cd78..c0f3b1e887 100644 --- a/tangem-demo/src/main/res/layout/w_response_item.xml +++ b/tangem-demo/src/main/res/layout/w_response_item.xml @@ -3,23 +3,24 @@ xmlns:tools="http://schemas.android.com/tools" android:id="@+id/root" android:layout_width="match_parent" - android:layout_height="wrap_content" - android:paddingStart="@dimen/def_indent" - android:paddingTop="6dp" - android:paddingEnd="@dimen/def_indent" - android:paddingBottom="6dp"> + android:layout_height="wrap_content"> + android:orientation="vertical" + android:paddingStart="@dimen/def_indent" + android:paddingTop="6dp" + android:paddingEnd="@dimen/def_indent" + android:paddingBottom="8dp"> + + \ No newline at end of file diff --git a/tangem-demo/src/main/res/layout/w_response_item_checkbox.xml b/tangem-demo/src/main/res/layout/w_response_item_checkbox.xml new file mode 100644 index 0000000000..2102af0305 --- /dev/null +++ b/tangem-demo/src/main/res/layout/w_response_item_checkbox.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + diff --git a/tangem-demo/src/main/res/navigation/navigation.xml b/tangem-demo/src/main/res/navigation/navigation.xml index cd84c2a849..8107b48599 100644 --- a/tangem-demo/src/main/res/navigation/navigation.xml +++ b/tangem-demo/src/main/res/navigation/navigation.xml @@ -127,7 +127,7 @@ diff --git a/tangem-demo/src/main/res/values/colors.xml b/tangem-demo/src/main/res/values/colors.xml index 20e2693328..c72e37bcd8 100644 --- a/tangem-demo/src/main/res/values/colors.xml +++ b/tangem-demo/src/main/res/values/colors.xml @@ -8,7 +8,10 @@ #E6959595 #2B2B2B - #888888 + + #888888 + #303030 + @color/field_info #C6C6C6 #FFE391 diff --git a/tangem-demo/src/main/res/values/strings.xml b/tangem-demo/src/main/res/values/strings.xml index 5cb0f4053a..df8e87a708 100644 --- a/tangem-demo/src/main/res/values/strings.xml +++ b/tangem-demo/src/main/res/values/strings.xml @@ -1,5 +1,5 @@ - Tangem Demo + Tangem Development Kit @string/app_name "Current card ID: " diff --git a/tangem-demo/src/main/res/values/strings_response.xml b/tangem-demo/src/main/res/values/strings_response.xml new file mode 100644 index 0000000000..c0086d1186 --- /dev/null +++ b/tangem-demo/src/main/res/values/strings_response.xml @@ -0,0 +1,138 @@ + + + + + + CID + Manufacturer_Name + Status + Firmware_Version + Card_PublicKey + Settings_Mask + Is_Reusable + Use_Activation + Prohibit_Purge_Wallet + Use_Block + Allow_SET_PIN1 + Allow_SET_PIN2 + Use_CVC + Prohibit_Default_PIN1 + Use_One_CommandAtTime + Use_NDEF + Use_Dynamic_NDEF + Smart_Security_Delay + Allow_Unencrypted + Allow_Fast_Encryption + Protect_Issuer_Data_Against_Replay + Allow_Select_Blockchain + Disable_Precomputed_NDEF + Skip_Security_Delay_If_Validated_By_Issuer + Skip_Check_PIN2_and_CVC_If_Validated_By_Issuer + Skip_Security_Delay_If_Validated_By_Linked_Terminal + Restrict_Overwrite_Issuer_Extra_Data + 1 – prohibit overwriting Issuer_Extra_Data + Require_Term_Tx_Signature + Require_Term_Cert_Signature + Check_PIN3_on_Card + Card_Data + Issuer_Data_PublicKey + Curve_ID + Max_Signatures + Signing_Method + Pause_Before_PIN2 + Wallet_PublicKey + Wallet_Remaining_Signatures + Wallet_Signed_Hashes + Health + Is_Activated + Activation_Seed + Payment_Flow_Version + User_Counter + User_Protected_Counter + + + Unique Tangem card ID number + Name of Tangem card manufacturer + 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): + 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 + + 0 – Card will accept PURGE_WALLET command + \n1 – Card will reject PURGE_WALLET command + + Is user allowed to change PIN1 with SET_PIN command + Is user allowed to change PIN2 with SET_PIN command + All commands requiring PIN2 will also require additional CVC code printed on the card + SET_PIN commands will not change PIN1 to default value (‘000000’). + 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. + 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) + 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 + 0 – Enable precomputed dynamic NDEF to work around iPhone 7+ NFC bug. + /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). + 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). + 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 + 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 + 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 + 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. + Total number of signatures allowed for the wallet when the card was personalized. + Defines what data should be submitted to SIGN command. + Delay in seconds before COS executes commands protected by PIN2. + Public key of the blockchain wallet. Value returned only if the wallet has already been created by CREATE_WALLET command. See Security section for more details. + Remaining number of SIGN operations before the wallet will stop signing transactions. Value returned only if the wallet has already been created by CREATE_WALLET command. + 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). + 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. + 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. + 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. + + + + Batch_ID + Manufacture_Date_Time + Issuer_Name + Blockchain_Name + Manufacturer_Signature + ProductMask + tokenSymbol + tokenContractAddress + tokenDecimal + + Tangem internal manufacturing batch ID, should correspond with the data in Tangem’s card list (see Card attestation and UID and CID sections) + Timestamp of manufacturing should correspond with the data in Tangem’s card list (see Card attestation section). + Name of the issuer + Name of the blockchain (once personalized, the target blockchain is fixed for the whole life cycle) + Signature of CID with the MANUFACTURER_PRIVATE_KEY + Mask of products enabled on card + + + + \ No newline at end of file From 5491d7c795a4d4299a6f319e1a0f97e59519f9c5 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 9 Apr 2020 00:27:51 +0300 Subject: [PATCH 15/39] Updated on 2026-08-14 --- .gitignore | 3 ++ .../tangem/tangemtest/commons/Abstractions.kt | 9 +++++ .../tangemtest/ucase/domain/actions/Action.kt | 2 ++ .../domain/paramsManager/ItemsManager.kt | 4 +-- .../managers/BaseItemsManager.kt | 36 ++++++++++++++----- .../managers/DepersonalizeItemsManager.kt | 26 -------------- .../managers/PersonalizeItemsManager.kt | 18 +++------- .../managers/ScanItemsManager.kt | 17 --------- .../managers/SignItemsManager.kt | 34 ------------------ .../paramsManager/managers/SimpleManagers.kt | 31 ++++++++++++++++ .../personalize/PersonalizationConfigStore.kt | 26 ++++++++++++++ .../personalize/ui/PersonalizeFragment.kt | 33 ++--------------- 12 files changed, 106 insertions(+), 133 deletions(-) create mode 100644 tangem-demo/src/main/java/com/tangem/tangemtest/commons/Abstractions.kt delete mode 100644 tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/managers/DepersonalizeItemsManager.kt delete mode 100644 tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/managers/ScanItemsManager.kt delete mode 100644 tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/managers/SignItemsManager.kt create mode 100644 tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/managers/SimpleManagers.kt create mode 100644 tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/PersonalizationConfigStore.kt diff --git a/.gitignore b/.gitignore index d741153000..39d65ab0a1 100644 --- a/.gitignore +++ b/.gitignore @@ -15,6 +15,9 @@ local.properties .idea/*.xml .idea/codeStyles +# LayoutInspector files +.captures + # OS-specific files .DS_Store .DS_Store? diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/commons/Abstractions.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/commons/Abstractions.kt new file mode 100644 index 0000000000..f5f02c0ace --- /dev/null +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/commons/Abstractions.kt @@ -0,0 +1,9 @@ +package com.tangem.tangemtest.commons + +/** +[REDACTED_AUTHOR] + */ +interface Store { + fun save(config: M) + fun restore(): M +} \ No newline at end of file diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/actions/Action.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/actions/Action.kt index 285c8de4b6..a5f736fe1f 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/actions/Action.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/actions/Action.kt @@ -43,4 +43,6 @@ abstract class BaseAction : Action { } callback(taskEvent, modifiedItems) } + + override fun getActionByTag(payload: PayloadHolder, id: Id, attrs: AttrForAction): ((ActionCallback) -> Unit)? = null } \ No newline at end of file diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/ItemsManager.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/ItemsManager.kt index f97013d6f8..42b92174ad 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/ItemsManager.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/ItemsManager.kt @@ -19,12 +19,12 @@ typealias ActionCallback = (ActionResponse, AffectedList) -> Unit interface ItemsManager : PayloadHolder { fun itemChanged(id: Id, value: Any?, callback: AffectedItemsCallback? = null) + fun setItems(items: List) fun getItems(): List + fun setItemChangeConsequences(consequence: ItemsChangeConsequence?) fun invokeMainAction(cardManager: CardManager, callback: ActionCallback) fun getActionByTag(id: Id, cardManager: CardManager): ((ActionCallback) -> Unit)? fun attachPayload(payload: Payload) - - val consequence: ItemsChangeConsequence? } interface PayloadKey { diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/managers/BaseItemsManager.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/managers/BaseItemsManager.kt index 03ccaf4728..bfb987742d 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/managers/BaseItemsManager.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/managers/BaseItemsManager.kt @@ -1,5 +1,6 @@ package com.tangem.tangemtest.ucase.domain.paramsManager.managers +import androidx.lifecycle.LifecycleObserver import com.tangem.CardManager import com.tangem.tangemtest._arch.structure.Id import com.tangem.tangemtest._arch.structure.Payload @@ -11,18 +12,22 @@ 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.triggers.changeConsequence.ItemsChangeConsequence +import ru.dev.gbixahue.eu4d.lib.android.global.log.Log /** [REDACTED_AUTHOR] */ -abstract class BaseItemsManager(protected val action: Action) : ItemsManager { +open class BaseItemsManager(protected val action: Action) : ItemsManager, LifecycleObserver { - protected val itemList: List by lazy { createItemsList() } - - override val consequence: ItemsChangeConsequence? = null + init { + Log.d(this, "new instance created") + } override val payload: MutableMap = mutableMapOf() + protected var changeConsequence: ItemsChangeConsequence? = null + protected val itemList: MutableList = mutableListOf() + override fun itemChanged(id: Id, value: Any?, callback: AffectedItemsCallback?) { if (itemList.isEmpty()) return val foundItem = itemList.findDataItem(id) ?: return @@ -31,9 +36,24 @@ abstract class BaseItemsManager(protected val action: Action) : ItemsManager { applyChangesByAffectedItems(foundItem, callback) } + override fun setItems(items: List) { + itemList.clear() + itemList.addAll(items) + } + override fun getItems(): List = itemList - override fun getActionByTag(id: Id, cardManager: CardManager): ((ActionCallback) -> Unit)? = null + override fun setItemChangeConsequences(consequence: ItemsChangeConsequence?) { + this.changeConsequence = consequence + } + + override fun invokeMainAction(cardManager: CardManager, callback: ActionCallback) { + action.executeMainAction(this, getAttrsForAction(cardManager), callback) + } + + override fun getActionByTag(id: Id, cardManager: CardManager): ((ActionCallback) -> Unit)? { + return action.getActionByTag(this, id, getAttrsForAction(cardManager)) + } override fun attachPayload(payload: Payload) { payload.forEach { this.payload[it.key] = it.value } @@ -41,11 +61,9 @@ abstract class BaseItemsManager(protected val action: Action) : ItemsManager { // Use it if current item needs to be affect any other items protected open fun applyChangesByAffectedItems(param: Item, callback: AffectedItemsCallback?) { - consequence?.affectChanges(this, param, itemList)?.let { callback?.invoke(it) } + changeConsequence?.affectChanges(this, param, itemList)?.let { callback?.invoke(it) } } protected fun getAttrsForAction(cardManager: CardManager) - : AttrForAction = AttrForAction(cardManager, itemList, payload, consequence) - - abstract fun createItemsList(): List + : AttrForAction = AttrForAction(cardManager, itemList, payload, changeConsequence) } \ No newline at end of file diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/managers/DepersonalizeItemsManager.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/managers/DepersonalizeItemsManager.kt deleted file mode 100644 index 8267c86bb6..0000000000 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/managers/DepersonalizeItemsManager.kt +++ /dev/null @@ -1,26 +0,0 @@ -package com.tangem.tangemtest.ucase.domain.paramsManager.managers - -import com.tangem.CardManager -import com.tangem.tangemtest._arch.structure.Id -import com.tangem.tangemtest._arch.structure.abstraction.Item -import com.tangem.tangemtest._arch.structure.impl.EditTextItem -import com.tangem.tangemtest.ucase.domain.actions.DepersonalizeAction -import com.tangem.tangemtest.ucase.domain.paramsManager.ActionCallback -import com.tangem.tangemtest.ucase.variants.TlvId - -/** -[REDACTED_AUTHOR] - */ -class DepersonalizeItemsManager : BaseItemsManager(DepersonalizeAction()) { - override fun createItemsList(): List { - return listOf(EditTextItem(TlvId.CardId, null)) - } - - override fun invokeMainAction(cardManager: CardManager, callback: ActionCallback) { - action.executeMainAction(this, getAttrsForAction(cardManager), callback) - } - - override fun getActionByTag(id: Id, cardManager: CardManager): ((ActionCallback) -> Unit)? { - return action.getActionByTag(this, id, getAttrsForAction(cardManager)) - } -} \ No newline at end of file diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/managers/PersonalizeItemsManager.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/managers/PersonalizeItemsManager.kt index c0381252bb..a483cfe69d 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/managers/PersonalizeItemsManager.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/managers/PersonalizeItemsManager.kt @@ -1,31 +1,26 @@ package com.tangem.tangemtest.ucase.domain.paramsManager.managers import androidx.lifecycle.Lifecycle -import androidx.lifecycle.LifecycleObserver import androidx.lifecycle.OnLifecycleEvent import com.tangem.CardManager -import com.tangem.tangemtest._arch.structure.abstraction.Item +import com.tangem.tangemtest.commons.Store import com.tangem.tangemtest.ucase.domain.actions.PersonalizeAction import com.tangem.tangemtest.ucase.domain.paramsManager.ActionCallback import com.tangem.tangemtest.ucase.variants.personalize.converter.PersonalizeConfigConverter import com.tangem.tangemtest.ucase.variants.personalize.dto.PersonalizeConfig -import ru.dev.gbixahue.eu4d.lib.android.global.log.Log /** [REDACTED_AUTHOR] */ class PersonalizeItemsManager( private val store: Store -) : BaseItemsManager(PersonalizeAction()), LifecycleObserver { +) : BaseItemsManager(PersonalizeAction()) { - init { - Log.d(this, "new instance created") - } private val converter = PersonalizeConfigConverter() - override fun createItemsList(): List { + init { val config = store.restore() - return converter.convert(config) + setItems(converter.convert(config)) } override fun invokeMainAction(cardManager: CardManager, callback: ActionCallback) { @@ -37,9 +32,4 @@ class PersonalizeItemsManager( val config = converter.convert(itemList, PersonalizeConfig()) store.save(config) } -} - -interface Store { - fun save(config: M) - fun restore(): M } \ No newline at end of file diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/managers/ScanItemsManager.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/managers/ScanItemsManager.kt deleted file mode 100644 index 1d9c973786..0000000000 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/managers/ScanItemsManager.kt +++ /dev/null @@ -1,17 +0,0 @@ -package com.tangem.tangemtest.ucase.domain.paramsManager.managers - -import com.tangem.CardManager -import com.tangem.tangemtest._arch.structure.abstraction.Item -import com.tangem.tangemtest.ucase.domain.actions.ScanAction -import com.tangem.tangemtest.ucase.domain.paramsManager.ActionCallback - -/** -[REDACTED_AUTHOR] - */ -class ScanItemsManager : BaseItemsManager(ScanAction()) { - override fun createItemsList(): List = listOf() - - override fun invokeMainAction(cardManager: CardManager, callback: ActionCallback) { - action.executeMainAction(this, getAttrsForAction(cardManager), callback) - } -} \ No newline at end of file diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/managers/SignItemsManager.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/managers/SignItemsManager.kt deleted file mode 100644 index 7d5d77a912..0000000000 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/managers/SignItemsManager.kt +++ /dev/null @@ -1,34 +0,0 @@ -package com.tangem.tangemtest.ucase.domain.paramsManager.managers - -import com.tangem.CardManager -import com.tangem.tangemtest._arch.structure.Id -import com.tangem.tangemtest._arch.structure.abstraction.Item -import com.tangem.tangemtest._arch.structure.impl.EditTextItem -import com.tangem.tangemtest.ucase.domain.actions.SignAction -import com.tangem.tangemtest.ucase.domain.paramsManager.ActionCallback -import com.tangem.tangemtest.ucase.domain.paramsManager.triggers.changeConsequence.ItemsChangeConsequence -import com.tangem.tangemtest.ucase.domain.paramsManager.triggers.changeConsequence.SignScanConsequence -import com.tangem.tangemtest.ucase.variants.TlvId - -/** -[REDACTED_AUTHOR] - */ -class SignItemsManager : BaseItemsManager(SignAction()) { - - override val consequence: ItemsChangeConsequence? by lazy { SignScanConsequence() } - - override fun createItemsList(): List { - return listOf( - EditTextItem(TlvId.CardId, null), - EditTextItem(TlvId.TransactionOutHash, "Data used for hashing") - ) - } - - override fun invokeMainAction(cardManager: CardManager, callback: ActionCallback) { - action.executeMainAction(this, getAttrsForAction(cardManager), callback) - } - - override fun getActionByTag(id: Id, cardManager: CardManager): ((ActionCallback) -> Unit)? { - return action.getActionByTag(this, id, getAttrsForAction(cardManager)) - } -} \ No newline at end of file diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/managers/SimpleManagers.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/managers/SimpleManagers.kt new file mode 100644 index 0000000000..c892b6761b --- /dev/null +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/managers/SimpleManagers.kt @@ -0,0 +1,31 @@ +package com.tangem.tangemtest.ucase.domain.paramsManager.managers + +import com.tangem.tangemtest._arch.structure.impl.EditTextItem +import com.tangem.tangemtest.ucase.domain.actions.DepersonalizeAction +import com.tangem.tangemtest.ucase.domain.actions.ScanAction +import com.tangem.tangemtest.ucase.domain.actions.SignAction +import com.tangem.tangemtest.ucase.domain.paramsManager.triggers.changeConsequence.SignScanConsequence +import com.tangem.tangemtest.ucase.variants.TlvId + +/** +[REDACTED_AUTHOR] + */ +class ScanItemsManager : BaseItemsManager(ScanAction()) + +class DepersonalizeItemsManager : BaseItemsManager(DepersonalizeAction()) { + + init { + setItems(listOf(EditTextItem(TlvId.CardId, null))) + } +} + +class SignItemsManager : BaseItemsManager(SignAction()) { + + init { + setItemChangeConsequences(SignScanConsequence()) + setItems(listOf( + EditTextItem(TlvId.CardId, null), + EditTextItem(TlvId.TransactionOutHash, "Data used for hashing") + )) + } +} \ No newline at end of file diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/PersonalizationConfigStore.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/PersonalizationConfigStore.kt new file mode 100644 index 0000000000..6f7acaba65 --- /dev/null +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/PersonalizationConfigStore.kt @@ -0,0 +1,26 @@ +package com.tangem.tangemtest.ucase.variants.personalize + +import android.content.Context +import android.content.SharedPreferences +import androidx.core.content.edit +import com.google.gson.Gson +import com.tangem.tangemtest.AppTangemDemo +import com.tangem.tangemtest.commons.Store +import com.tangem.tangemtest.ucase.variants.personalize.dto.PersonalizeConfig + +class PersonalizationConfigStore(context: Context) : Store { + + private val key = "personalization_config" + + private val sp: SharedPreferences = (context.applicationContext as AppTangemDemo).sharedPreferences() + private val gson: Gson = Gson() + + override fun save(config: PersonalizeConfig) { + sp.edit(true) { putString(key, gson.toJson(config)) } + } + + override fun restore(): PersonalizeConfig { + val json = sp.getString(key, gson.toJson(PersonalizeConfig())) + return gson.fromJson(json, PersonalizeConfig::class.java) + } +} \ No newline at end of file diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/PersonalizeFragment.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/PersonalizeFragment.kt index 788d196392..740eeb0dde 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/PersonalizeFragment.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/PersonalizeFragment.kt @@ -1,13 +1,8 @@ package com.tangem.tangemtest.ucase.variants.personalize.ui -import android.content.Context -import android.content.SharedPreferences import android.os.Bundle import android.view.View -import androidx.core.content.edit import androidx.lifecycle.Observer -import com.google.gson.Gson -import com.tangem.tangemtest.AppTangemDemo import com.tangem.tangemtest.R import com.tangem.tangemtest._arch.structure.Id import com.tangem.tangemtest._arch.structure.abstraction.Item @@ -15,12 +10,11 @@ import com.tangem.tangemtest._arch.widget.WidgetBuilder import com.tangem.tangemtest.ucase.domain.paramsManager.ItemsManager import com.tangem.tangemtest.ucase.domain.paramsManager.PayloadKey import com.tangem.tangemtest.ucase.domain.paramsManager.managers.PersonalizeItemsManager -import com.tangem.tangemtest.ucase.domain.paramsManager.managers.Store 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.BaseCardActionFragment -import com.tangem.tangemtest.ucase.variants.personalize.dto.PersonalizeConfig +import com.tangem.tangemtest.ucase.variants.personalize.PersonalizationConfigStore import com.tangem.tangemtest.ucase.variants.personalize.ui.widgets.PersonalizeItemBuilder import ru.dev.gbixahue.eu4d.lib.android.global.log.Log @@ -81,27 +75,4 @@ class PersonalizeFragment : BaseCardActionFragment() { else -> showSnackbar(requireContext().getString(R.string.unknown)) } } -} - -class PersonalizationConfigStore(context: Context) : Store { - - private val key = "personalization_config" - - private val sp: SharedPreferences = (context.applicationContext as AppTangemDemo).sharedPreferences() - private val gson: Gson = Gson() - - override fun save(config: PersonalizeConfig) { - sp.edit(true) { putString(key, gson.toJson(config)) } - } - - override fun restore(): PersonalizeConfig { - val json = sp.getString(key, gson.toJson(PersonalizeConfig())) - return gson.fromJson(json, PersonalizeConfig::class.java) - } -} - - - - - - +} \ No newline at end of file From 6de895da1889e46fd8ff81ca6588c865ff3f9c32 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 9 Apr 2020 00:37:03 +0300 Subject: [PATCH 16/39] Updated on 2026-08-14 --- .../ucase/domain/actions/PersonalizeAction.kt | 14 +++++----- ...ager.kt => PersonalizationItemsManager.kt} | 12 ++++---- .../ucase/resources/MainResourceHolder.kt | 4 +-- ...sources.kt => PersonalizationResources.kt} | 2 +- .../ucase/variants/personalize/Ids.kt | 28 +++++++++---------- .../personalize/PersonalizationConfigStore.kt | 12 ++++---- .../personalize/converter/DefaultValues.kt | 6 ++-- ...r.kt => PersonalizationConfigConverter.kt} | 16 +++++------ ...fig.kt => ItemsToPersonalizationConfig.kt} | 16 +++++------ .../converter/fromTo/PersonalizeConfigTo.kt | 16 +++++------ ...ams.kt => DefaultPersonalizationParams.kt} | 2 +- ...lizeConfig.kt => PersonalizationConfig.kt} | 2 +- ...Fragment.kt => PersonalizationFragment.kt} | 14 +++++----- ...ilder.kt => PersonalizationItemBuilder.kt} | 2 +- ...personalize.xml => fg_personalization.xml} | 0 .../src/main/res/navigation/navigation.xml | 4 +-- 16 files changed, 74 insertions(+), 76 deletions(-) rename tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/managers/{PersonalizeItemsManager.kt => PersonalizationItemsManager.kt} (77%) rename tangem-demo/src/main/java/com/tangem/tangemtest/ucase/resources/initializers/{PersonalizeResources.kt => PersonalizationResources.kt} (99%) rename tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/converter/{PersonalizeConfigConverter.kt => PersonalizationConfigConverter.kt} (55%) rename tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/converter/fromTo/{ItemsToPersonalizeConfig.kt => ItemsToPersonalizationConfig.kt} (92%) rename tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/dto/{DefaultPersonalizeParams.kt => DefaultPersonalizationParams.kt} (97%) rename tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/dto/{PersonalizeConfig.kt => PersonalizationConfig.kt} (98%) rename tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/{PersonalizeFragment.kt => PersonalizationFragment.kt} (86%) rename tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/{PersonalizeItemBuilder.kt => PersonalizationItemBuilder.kt} (93%) rename tangem-demo/src/main/res/layout/{fg_personalize.xml => fg_personalization.xml} (100%) diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/actions/PersonalizeAction.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/actions/PersonalizeAction.kt index 8ef5e4c0cc..a50bc09456 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/actions/PersonalizeAction.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/actions/PersonalizeAction.kt @@ -10,10 +10,10 @@ 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.PersonalizationConfigConverter 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 com.tangem.tangemtest.ucase.variants.personalize.dto.DefaultPersonalizationParams +import com.tangem.tangemtest.ucase.variants.personalize.dto.PersonalizationConfig import ru.dev.gbixahue.eu4d.lib.kotlin.stringOf /** @@ -33,11 +33,11 @@ class PersonalizeAction : BaseAction() { return } - val issuer = DefaultPersonalizeParams.issuer() - val acquirer = DefaultPersonalizeParams.acquirer() - val manufacturer = DefaultPersonalizeParams.manufacturer() + val issuer = DefaultPersonalizationParams.issuer() + val acquirer = DefaultPersonalizationParams.acquirer() + val manufacturer = DefaultPersonalizationParams.manufacturer() - val personalizeConfig = PersonalizeConfigConverter().convert(itemList, PersonalizeConfig()) + val personalizeConfig = PersonalizationConfigConverter().convert(itemList, PersonalizationConfig()) val cardConfig = PersonalizeConfigToCardConfig().convert(personalizeConfig) attrs.cardManager.personalize(cardConfig, issuer, manufacturer, acquirer) { diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/managers/PersonalizeItemsManager.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/managers/PersonalizationItemsManager.kt similarity index 77% rename from tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/managers/PersonalizeItemsManager.kt rename to tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/managers/PersonalizationItemsManager.kt index a483cfe69d..e67e278aca 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/managers/PersonalizeItemsManager.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/managers/PersonalizationItemsManager.kt @@ -6,17 +6,17 @@ import com.tangem.CardManager import com.tangem.tangemtest.commons.Store import com.tangem.tangemtest.ucase.domain.actions.PersonalizeAction import com.tangem.tangemtest.ucase.domain.paramsManager.ActionCallback -import com.tangem.tangemtest.ucase.variants.personalize.converter.PersonalizeConfigConverter -import com.tangem.tangemtest.ucase.variants.personalize.dto.PersonalizeConfig +import com.tangem.tangemtest.ucase.variants.personalize.converter.PersonalizationConfigConverter +import com.tangem.tangemtest.ucase.variants.personalize.dto.PersonalizationConfig /** [REDACTED_AUTHOR] */ -class PersonalizeItemsManager( - private val store: Store +class PersonalizationItemsManager( + private val store: Store ) : BaseItemsManager(PersonalizeAction()) { - private val converter = PersonalizeConfigConverter() + private val converter = PersonalizationConfigConverter() init { val config = store.restore() @@ -29,7 +29,7 @@ class PersonalizeItemsManager( @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY) fun onDestroy() { - val config = converter.convert(itemList, PersonalizeConfig()) + val config = converter.convert(itemList, PersonalizationConfig()) store.save(config) } } \ No newline at end of file diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/resources/MainResourceHolder.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/resources/MainResourceHolder.kt index 44028d9018..cf6a61abbc 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/resources/MainResourceHolder.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/resources/MainResourceHolder.kt @@ -3,7 +3,7 @@ package com.tangem.tangemtest.ucase.resources import com.tangem.tangemtest.R import com.tangem.tangemtest._arch.structure.Id import com.tangem.tangemtest.ucase.resources.initializers.ActionResources -import com.tangem.tangemtest.ucase.resources.initializers.PersonalizeResources +import com.tangem.tangemtest.ucase.resources.initializers.PersonalizationResources import com.tangem.tangemtest.ucase.resources.initializers.ResponseResources import com.tangem.tangemtest.ucase.resources.initializers.TlvResources import ru.dev.gbixahue.eu4d.lib.kotlin.common.BaseTypedHolder @@ -15,7 +15,7 @@ open class ResourceHolder : BaseTypedHolder() object MainResourceHolder : ResourceHolder() { init { - PersonalizeResources().init(this) + PersonalizationResources().init(this) ActionResources().init(this) TlvResources().init(this) ResponseResources().init(this) diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/resources/initializers/PersonalizeResources.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/resources/initializers/PersonalizationResources.kt similarity index 99% rename from tangem-demo/src/main/java/com/tangem/tangemtest/ucase/resources/initializers/PersonalizeResources.kt rename to tangem-demo/src/main/java/com/tangem/tangemtest/ucase/resources/initializers/PersonalizationResources.kt index fe56561030..a45bc55ed4 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/resources/initializers/PersonalizeResources.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/resources/initializers/PersonalizationResources.kt @@ -10,7 +10,7 @@ import ru.dev.gbixahue.eu4d.lib.kotlin.common.TypedHolder /** [REDACTED_AUTHOR] */ -class PersonalizeResources() { +class PersonalizationResources() { fun init(holder: ResourceHolder) { initBlock(holder) initCardNumber(holder) diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/Ids.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/Ids.kt index b8573bbdf5..6305b68975 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/Ids.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/Ids.kt @@ -6,11 +6,9 @@ import com.tangem.tangemtest._arch.structure.Id [REDACTED_AUTHOR] */ -interface PersonalizeId : Id +interface PersonalizationId : Id -interface BlockItem : PersonalizeId - -enum class BlockId : PersonalizeId { +enum class BlockId : PersonalizationId { CardNumber, Common, SigningMethod, @@ -24,13 +22,13 @@ enum class BlockId : PersonalizeId { Pins, } -enum class CardNumber : BlockItem { +enum class CardNumber : PersonalizationId { Series, Number, BatchId, } -enum class Common : BlockItem { +enum class Common : PersonalizationId { Curve, Blockchain, BlockchainCustom, @@ -38,7 +36,7 @@ enum class Common : BlockItem { CreateWallet, } -enum class SigningMethod : BlockItem { +enum class SigningMethod : PersonalizationId { SignTx, SignTxRaw, SignValidatedTx, @@ -48,7 +46,7 @@ enum class SigningMethod : BlockItem { SignExternal, } -enum class SignHashExProp : BlockItem { +enum class SignHashExProp : PersonalizationId { PinLessFloorLimit, CryptoExKey, RequireTerminalCertSig, @@ -56,26 +54,26 @@ enum class SignHashExProp : BlockItem { CheckPin3, } -enum class Denomination : BlockItem { +enum class Denomination : PersonalizationId { WriteOnPersonalize, Denomination, } -enum class Token : BlockItem { +enum class Token : PersonalizationId { ItsToken, Symbol, ContractAddress, Decimal } -enum class ProductMask : BlockItem { +enum class ProductMask : PersonalizationId { Note, Tag, IdCard, IdIssuerCard } -enum class SettingsMask : BlockItem { +enum class SettingsMask : PersonalizationId { IsReusable, NeedActivation, ForbidPurge, @@ -94,12 +92,12 @@ enum class SettingsMask : BlockItem { RestrictOverwriteExtraIssuerData, } -enum class SettingsMaskProtocolEnc : BlockItem { +enum class SettingsMaskProtocolEnc : PersonalizationId { AllowUnencrypted, AllowStaticEncryption } -enum class SettingsMaskNdef : BlockItem { +enum class SettingsMaskNdef : PersonalizationId { UseNdef, DynamicNdef, DisablePrecomputedNdef, @@ -108,7 +106,7 @@ enum class SettingsMaskNdef : BlockItem { Uri, } -enum class Pins : BlockItem { +enum class Pins : PersonalizationId { Pin, Pin2, Pin3, diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/PersonalizationConfigStore.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/PersonalizationConfigStore.kt index 6f7acaba65..77a68278f8 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/PersonalizationConfigStore.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/PersonalizationConfigStore.kt @@ -6,21 +6,21 @@ import androidx.core.content.edit import com.google.gson.Gson import com.tangem.tangemtest.AppTangemDemo import com.tangem.tangemtest.commons.Store -import com.tangem.tangemtest.ucase.variants.personalize.dto.PersonalizeConfig +import com.tangem.tangemtest.ucase.variants.personalize.dto.PersonalizationConfig -class PersonalizationConfigStore(context: Context) : Store { +class PersonalizationConfigStore(context: Context) : Store { private val key = "personalization_config" private val sp: SharedPreferences = (context.applicationContext as AppTangemDemo).sharedPreferences() private val gson: Gson = Gson() - override fun save(config: PersonalizeConfig) { + override fun save(config: PersonalizationConfig) { sp.edit(true) { putString(key, gson.toJson(config)) } } - override fun restore(): PersonalizeConfig { - val json = sp.getString(key, gson.toJson(PersonalizeConfig())) - return gson.fromJson(json, PersonalizeConfig::class.java) + override fun restore(): PersonalizationConfig { + val json = sp.getString(key, gson.toJson(PersonalizationConfig())) + return gson.fromJson(json, PersonalizationConfig::class.java) } } \ No newline at end of file diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/converter/DefaultValues.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/converter/DefaultValues.kt index 54ad12d4ab..49dd2faa8d 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/converter/DefaultValues.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/converter/DefaultValues.kt @@ -4,16 +4,16 @@ 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 com.tangem.tangemtest.ucase.variants.personalize.dto.PersonalizationConfig import ru.dev.gbixahue.eu4d.lib.kotlin.common.BaseTypedHolder /** [REDACTED_AUTHOR] */ -class PersonalizeConfigValuesHolder : BaseTypedHolder() { +class ConfigValuesHolder : BaseTypedHolder() { // All registered association values can't be objects - fun init(default: PersonalizeConfig) { + fun init(default: PersonalizationConfig) { register(CardNumber.Series, Value(default.series)) register(CardNumber.Number, Value(default.startNumber)) register(CardNumber.BatchId, Value(default.batchId)) diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/converter/PersonalizeConfigConverter.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/converter/PersonalizationConfigConverter.kt similarity index 55% rename from tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/converter/PersonalizeConfigConverter.kt rename to tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/converter/PersonalizationConfigConverter.kt index 6b9b041b49..7fbf672259 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/converter/PersonalizeConfigConverter.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/converter/PersonalizationConfigConverter.kt @@ -4,23 +4,23 @@ 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 com.tangem.tangemtest.ucase.variants.personalize.converter.fromTo.ItemsToPersonalizationConfig +import com.tangem.tangemtest.ucase.variants.personalize.converter.fromTo.PersonalizationConfigToItems +import com.tangem.tangemtest.ucase.variants.personalize.dto.PersonalizationConfig /** [REDACTED_AUTHOR] */ -class PersonalizeConfigConverter : ModelConverter { +class PersonalizationConfigConverter : ModelConverter { - private val toModel: ItemsToModel = ItemsToPersonalizeConfig() - private val toItems: ModelToItems = PersonalizeConfigToItem() + private val toModel: ItemsToModel = ItemsToPersonalizationConfig() + private val toItems: ModelToItems = PersonalizationConfigToItems() - override fun convert(from: List, default: PersonalizeConfig): PersonalizeConfig { + override fun convert(from: List, default: PersonalizationConfig): PersonalizationConfig { return toModel.convert(from, default) } - override fun convert(from: PersonalizeConfig): List { + override fun convert(from: PersonalizationConfig): List { return toItems.convert(from) } } \ No newline at end of file diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/converter/fromTo/ItemsToPersonalizeConfig.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/converter/fromTo/ItemsToPersonalizationConfig.kt similarity index 92% rename from tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/converter/fromTo/ItemsToPersonalizeConfig.kt rename to tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/converter/fromTo/ItemsToPersonalizationConfig.kt index 177ac87463..95fd18908d 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/converter/fromTo/ItemsToPersonalizeConfig.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/converter/fromTo/ItemsToPersonalizationConfig.kt @@ -4,17 +4,17 @@ import com.tangem.tangemtest._arch.structure.Id import com.tangem.tangemtest._arch.structure.abstraction.* 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 com.tangem.tangemtest.ucase.variants.personalize.converter.ConfigValuesHolder +import com.tangem.tangemtest.ucase.variants.personalize.dto.PersonalizationConfig import ru.dev.gbixahue.eu4d.lib.android.global.log.Log /** [REDACTED_AUTHOR] */ -class ItemsToPersonalizeConfig : ItemsToModel { - protected val valuesHolder = PersonalizeConfigValuesHolder() +class ItemsToPersonalizationConfig : ItemsToModel { + protected val valuesHolder = ConfigValuesHolder() - override fun convert(from: List, default: PersonalizeConfig): PersonalizeConfig { + override fun convert(from: List, default: PersonalizationConfig): PersonalizationConfig { valuesHolder.init(default) mapListItems(from) return createModel() @@ -34,8 +34,8 @@ class ItemsToPersonalizeConfig : ItemsToModel { } } - private fun createModel(): PersonalizeConfig { - val export = PersonalizeConfig() + private fun createModel(): PersonalizationConfig { + val export = PersonalizationConfig() export.series = getTyped(CardNumber.Series) export.startNumber = getTyped(CardNumber.Number) export.curveID = getTyped(Common.Curve) @@ -101,7 +101,7 @@ class ItemsToPersonalizeConfig : ItemsToModel { return getTypedBy(valuesHolder, id)!! } - private inline fun getTypedBy(holder: PersonalizeConfigValuesHolder, id: Id): Type? { + private inline fun getTypedBy(holder: ConfigValuesHolder, id: Id): Type? { Log.d(this, "getTyped for id: $id") var typedValue = holder.get(id)?.get() diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/converter/fromTo/PersonalizeConfigTo.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/converter/fromTo/PersonalizeConfigTo.kt index eb560d496d..30a581378f 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/converter/fromTo/PersonalizeConfigTo.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/converter/fromTo/PersonalizeConfigTo.kt @@ -11,19 +11,19 @@ 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 com.tangem.tangemtest.ucase.variants.personalize.converter.ConfigValuesHolder +import com.tangem.tangemtest.ucase.variants.personalize.dto.PersonalizationConfig import ru.dev.gbixahue.eu4d.lib.kotlin.common.Converter import java.util.* /** [REDACTED_AUTHOR] */ -class PersonalizeConfigToItem : ModelToItems { - private val valuesHolder = PersonalizeConfigValuesHolder() +class PersonalizationConfigToItems : ModelToItems { + private val valuesHolder = ConfigValuesHolder() private val itemTypes = ItemTypes() - override fun convert(from: PersonalizeConfig): List { + override fun convert(from: PersonalizationConfig): List { valuesHolder.init(from) val blocList = mutableListOf() blocList.add(cardNumber()) @@ -183,7 +183,7 @@ class PersonalizeConfigToItem : ModelToItems { return ListItemBlock(id).apply { addItem(TextItem(id)) } } - private fun addPayload(block: Block, from: PersonalizeConfig) { + private fun addPayload(block: Block, from: PersonalizationConfig) { block.payload[PayloadKey.incomingJson] = from } @@ -201,9 +201,9 @@ class PersonalizeConfigToItem : ModelToItems { } } -class PersonalizeConfigToCardConfig : Converter { +class PersonalizeConfigToCardConfig : Converter { - override fun convert(from: PersonalizeConfig): CardConfig { + override fun convert(from: PersonalizationConfig): CardConfig { val signingMethod = com.tangem.commands.SigningMethod.build( signHash = from.SigningMethod0, signRaw = from.SigningMethod1, diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/dto/DefaultPersonalizeParams.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/dto/DefaultPersonalizationParams.kt similarity index 97% rename from tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/dto/DefaultPersonalizeParams.kt rename to tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/dto/DefaultPersonalizationParams.kt index c8d9afa0d1..d82099cab8 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/dto/DefaultPersonalizeParams.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/dto/DefaultPersonalizationParams.kt @@ -9,7 +9,7 @@ import com.tangem.common.KeyPair [REDACTED_AUTHOR] */ -interface DefaultPersonalizeParams { +interface DefaultPersonalizationParams { companion object { fun issuer(): Issuer { diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/dto/PersonalizeConfig.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/dto/PersonalizationConfig.kt similarity index 98% rename from tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/dto/PersonalizeConfig.kt rename to tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/dto/PersonalizationConfig.kt index bc855da8b1..1b09726453 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/dto/PersonalizeConfig.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/dto/PersonalizationConfig.kt @@ -3,7 +3,7 @@ package com.tangem.tangemtest.ucase.variants.personalize.dto /** [REDACTED_AUTHOR] */ -class PersonalizeConfig { +class PersonalizationConfig { // Card number var series = "BB" diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/PersonalizeFragment.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/PersonalizationFragment.kt similarity index 86% rename from tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/PersonalizeFragment.kt rename to tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/PersonalizationFragment.kt index 740eeb0dde..dc6a412473 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/PersonalizeFragment.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/PersonalizationFragment.kt @@ -9,27 +9,27 @@ import com.tangem.tangemtest._arch.structure.abstraction.Item import com.tangem.tangemtest._arch.widget.WidgetBuilder import com.tangem.tangemtest.ucase.domain.paramsManager.ItemsManager import com.tangem.tangemtest.ucase.domain.paramsManager.PayloadKey -import com.tangem.tangemtest.ucase.domain.paramsManager.managers.PersonalizeItemsManager +import com.tangem.tangemtest.ucase.domain.paramsManager.managers.PersonalizationItemsManager 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.BaseCardActionFragment import com.tangem.tangemtest.ucase.variants.personalize.PersonalizationConfigStore -import com.tangem.tangemtest.ucase.variants.personalize.ui.widgets.PersonalizeItemBuilder +import com.tangem.tangemtest.ucase.variants.personalize.ui.widgets.PersonalizationItemBuilder import ru.dev.gbixahue.eu4d.lib.android.global.log.Log /** [REDACTED_AUTHOR] */ -class PersonalizeFragment : BaseCardActionFragment() { +class PersonalizationFragment : BaseCardActionFragment() { - override val itemsManager: ItemsManager by lazy { PersonalizeItemsManager(PersonalizationConfigStore(requireContext())) } + override val itemsManager: ItemsManager by lazy { PersonalizationItemsManager(PersonalizationConfigStore(requireContext())) } - override fun getLayoutId(): Int = R.layout.fg_personalize + override fun getLayoutId(): Int = R.layout.fg_personalization override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - lifecycle.addObserver(itemsManager as PersonalizeItemsManager) + lifecycle.addObserver(itemsManager as PersonalizationItemsManager) } override fun initFab() { @@ -44,7 +44,7 @@ class PersonalizeFragment : BaseCardActionFragment() { Log.d(this, "ldBlockList size: ${list.size}") itemList.clear() itemList.addAll(list) - itemList.forEach { WidgetBuilder(PersonalizeItemBuilder()).build(it, itemContainer) } + itemList.forEach { WidgetBuilder(PersonalizationItemBuilder()).build(it, itemContainer) } actionVM.attachToPayload(mutableMapOf( PayloadKey.actionView to this as ActionView, PayloadKey.itemList to itemList diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/PersonalizeItemBuilder.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/PersonalizationItemBuilder.kt similarity index 93% rename from tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/PersonalizeItemBuilder.kt rename to tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/PersonalizationItemBuilder.kt index 30788d1e70..53b6c336e4 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/PersonalizeItemBuilder.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/PersonalizationItemBuilder.kt @@ -9,7 +9,7 @@ import com.tangem.tangemtest._arch.widget.abstraction.ViewWidget /** [REDACTED_AUTHOR] */ -class PersonalizeItemBuilder : ItemWidgetBuilder { +class PersonalizationItemBuilder : ItemWidgetBuilder { override fun build(item: BaseItem<*>, parent: ViewGroup): ViewWidget? { return when (item) { is TextItem -> BlockHeadWidget(parent, item) diff --git a/tangem-demo/src/main/res/layout/fg_personalize.xml b/tangem-demo/src/main/res/layout/fg_personalization.xml similarity index 100% rename from tangem-demo/src/main/res/layout/fg_personalize.xml rename to tangem-demo/src/main/res/layout/fg_personalization.xml diff --git a/tangem-demo/src/main/res/navigation/navigation.xml b/tangem-demo/src/main/res/navigation/navigation.xml index 8107b48599..1cc5427895 100644 --- a/tangem-demo/src/main/res/navigation/navigation.xml +++ b/tangem-demo/src/main/res/navigation/navigation.xml @@ -67,9 +67,9 @@ + tools:layout="@layout/fg_personalization" /> Date: Thu, 9 Apr 2020 00:54:54 +0300 Subject: [PATCH 17/39] Updated on 2026-08-14 --- .../tangem/tangemtest/ucase/tunnel/ViewScreen.kt | 2 +- .../tangemtest/ucase/ui/BaseCardActionFragment.kt | 12 +++++++----- .../personalize/ui/PersonalizationFragment.kt | 14 +++++++------- 3 files changed, 15 insertions(+), 13 deletions(-) diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/tunnel/ViewScreen.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/tunnel/ViewScreen.kt index a22a6b14ae..53ab0b4883 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/tunnel/ViewScreen.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/tunnel/ViewScreen.kt @@ -12,5 +12,5 @@ interface ViewScreen interface ActionView : ViewScreen, SnackbarHolder { fun enableActionFab(enable: Boolean) - fun showSnackbar(id: Id) + fun showSnackbar(id: Id, additionalHandler: ((Id) -> Int)? = null) } \ No newline at end of file diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/BaseCardActionFragment.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/BaseCardActionFragment.kt index 67242ae326..44acd319b8 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/BaseCardActionFragment.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/BaseCardActionFragment.kt @@ -34,6 +34,7 @@ abstract class BaseCardActionFragment : BaseFragment(), ActionView { protected abstract val itemsManager: ItemsManager protected val mainActivityVM by activityViewModels() protected val actionVM: ActionViewModel by viewModels { ActionViewModelFactory(itemsManager) } + protected val UNDEFINED = -1 private val paramsWidgetList = mutableListOf() @@ -130,11 +131,12 @@ abstract class BaseCardActionFragment : BaseFragment(), ActionView { actionFab.enable(enable) } - override fun showSnackbar(id: Id) { -// MainResourceHolder.safeGet<>() - when (id) { - CardError.NotPersonalized -> showSnackbar(R.string.card_error_not_personalized) - else -> showSnackbar(requireContext().getString(R.string.unknown)) + override fun showSnackbar(id: Id, additionalHandler: ((Id) -> Int)?) { + val resourceId = when (id) { + CardError.NotPersonalized -> R.string.card_error_not_personalized + else -> additionalHandler?.invoke(id) ?: UNDEFINED } + + if (resourceId != UNDEFINED) showSnackbar(resourceId) } } \ No newline at end of file diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/PersonalizationFragment.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/PersonalizationFragment.kt index dc6a412473..d0216c725b 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/PersonalizationFragment.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/PersonalizationFragment.kt @@ -11,7 +11,6 @@ import com.tangem.tangemtest.ucase.domain.paramsManager.ItemsManager import com.tangem.tangemtest.ucase.domain.paramsManager.PayloadKey import com.tangem.tangemtest.ucase.domain.paramsManager.managers.PersonalizationItemsManager 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.BaseCardActionFragment import com.tangem.tangemtest.ucase.variants.personalize.PersonalizationConfigStore @@ -67,12 +66,13 @@ class PersonalizationFragment : BaseCardActionFragment() { }) } - override fun showSnackbar(id: Id) { - 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: Id, additionalHandler: ((Id) -> Int)?) { + super.showSnackbar(id) { + when (id) { + ItemError.BadSeries -> R.string.card_error_bad_series + ItemError.BadCardNumber -> R.string.card_error_bad_series_number + else -> additionalHandler?.invoke(id) ?: UNDEFINED + } } } } \ No newline at end of file From 9056c31bb6ae30622c2d67d9b38a247cd4d66b6b Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 9 Apr 2020 18:03:08 +0300 Subject: [PATCH 18/39] Updated on 2026-08-14 --- .../_arch/structure/HelpAbstracts.kt | 12 - .../tangem/tangemtest/_arch/structure/Ids.kt | 4 +- .../_arch/structure/abstraction/Extension.kt | 10 +- .../_arch/structure/abstraction/Item.kt | 32 +- .../abstraction/{Block.kt => ItemGroup.kt} | 32 +- .../{ViewModel.kt => ItemViewModel.kt} | 105 +++--- .../tangemtest/_arch/structure/impl/Items.kt | 24 +- .../_arch/structure/impl/ViewModels.kt | 29 -- .../_arch/widget/ItemWidgetBuilder.kt | 2 +- .../tangemtest/_arch/widget/WidgetBuilder.kt | 23 +- .../_arch/widget/abstraction/ViewWidget.kt | 44 +-- .../_arch/widget/impl/LinearBlockWidget.kt | 27 -- .../_arch/widget/impl/LinearGroupWidget.kt | 18 ++ .../_arch/widget/impl/StubWidget.kt | 9 +- .../tangemtest/commons/GlobalFunctions.kt | 3 +- .../domain/actions/DepersonalizeAction.kt | 4 +- .../ucase/domain/actions/PersonalizeAction.kt | 18 +- .../ucase/domain/actions/SignAction.kt | 6 +- .../managers/BaseItemsManager.kt | 4 +- .../triggers/afterAction/AfterScanModifier.kt | 4 +- .../ParamChangeConsequence.kt | 4 +- .../tangemtest/ucase/ui/ActionViewModel.kt | 8 +- .../ucase/ui/BaseCardActionFragment.kt | 5 +- .../ucase/ui/widgets/ParameterWidget.kt | 8 +- .../personalize/converter/DefaultValues.kt | 2 +- .../converter/{fromTo => }/ItemTypes.kt | 2 +- .../PersonalizationConfigConverter.kt | 289 ++++++++++++++++- .../PersonalizationConfigToCardConfig.kt | 113 +++++++ .../fromTo/ItemsToPersonalizationConfig.kt | 114 ------- .../converter/fromTo/PersonalizeConfigTo.kt | 305 ------------------ .../personalize/ui/widgets/BaseAppWidget.kt | 28 ++ .../ui/widgets/DescriptionWidget.kt | 14 +- .../personalize/ui/widgets/EditTextWidget.kt | 13 +- ...BlockHeadWidget.kt => GroupTitleWidget.kt} | 3 +- .../personalize/ui/widgets/NumberWidget.kt | 16 +- .../ui/widgets/PersonalizationItemBuilder.kt | 6 +- .../personalize/ui/widgets/SpinnerWidget.kt | 32 +- .../personalize/ui/widgets/SwitchWidget.kt | 15 +- .../variants/responses/ResponseViewModel.kt | 4 +- .../responses/converter/CardConverter.kt | 79 +++-- .../responses/ui/widget/CheckBoxWidget.kt | 9 +- .../ui/widget/ResponseItemBuilder.kt | 2 +- .../responses/ui/widget/ResponseTextWidget.kt | 11 +- 43 files changed, 730 insertions(+), 762 deletions(-) rename tangem-demo/src/main/java/com/tangem/tangemtest/_arch/structure/abstraction/{Block.kt => ItemGroup.kt} (61%) rename tangem-demo/src/main/java/com/tangem/tangemtest/_arch/structure/abstraction/{ViewModel.kt => ItemViewModel.kt} (70%) delete mode 100644 tangem-demo/src/main/java/com/tangem/tangemtest/_arch/structure/impl/ViewModels.kt delete mode 100644 tangem-demo/src/main/java/com/tangem/tangemtest/_arch/widget/impl/LinearBlockWidget.kt create mode 100644 tangem-demo/src/main/java/com/tangem/tangemtest/_arch/widget/impl/LinearGroupWidget.kt rename tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/converter/{fromTo => }/ItemTypes.kt (99%) create mode 100644 tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/converter/PersonalizationConfigToCardConfig.kt delete mode 100644 tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/converter/fromTo/ItemsToPersonalizationConfig.kt delete mode 100644 tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/converter/fromTo/PersonalizeConfigTo.kt create mode 100644 tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/BaseAppWidget.kt rename tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/{BlockHeadWidget.kt => GroupTitleWidget.kt} (72%) diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/structure/HelpAbstracts.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/structure/HelpAbstracts.kt index 98704005f5..309309c947 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/structure/HelpAbstracts.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/structure/HelpAbstracts.kt @@ -3,10 +3,6 @@ package com.tangem.tangemtest._arch.structure /** [REDACTED_AUTHOR] */ -interface DataHolder { - var viewModel: D -} - typealias Payload = MutableMap interface PayloadHolder { @@ -17,12 +13,4 @@ interface PayloadHolder { fun set(key: String, value: Any?) { payload[key] = value } -} - -interface ItemListHolder { - fun setItems(list: MutableList) - fun getItems(): MutableList - fun addItem(item: I) - fun removeItem(item: I) - fun clear() } \ No newline at end of file diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/structure/Ids.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/structure/Ids.kt index ce27853356..5e517808f2 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/structure/Ids.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/structure/Ids.kt @@ -5,7 +5,9 @@ package com.tangem.tangemtest._arch.structure */ interface Id -class StringId(val name: String) : Id +class StringId(val value: String) : Id + +class StringResId(val value: Int) : Id enum class Additional : Id { UNDEFINED, diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/structure/abstraction/Extension.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/structure/abstraction/Extension.kt index 71924302a3..76b43ce50a 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/structure/abstraction/Extension.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/structure/abstraction/Extension.kt @@ -5,11 +5,11 @@ import com.tangem.tangemtest._arch.structure.Id /** [REDACTED_AUTHOR] */ -fun List.findDataItem(id: Id): BaseItem? { - var foundItem: BaseItem? = null +fun List.findItem(id: Id): Item? { + var foundItem: Item? = null iterate { if (it.id == id) { - foundItem = it as? BaseItem + foundItem = it return@iterate } } @@ -19,8 +19,8 @@ fun List.findDataItem(id: Id): BaseItem? { fun List.iterate(func: (Item) -> Unit) { forEach { when (it) { - is BaseItem<*> -> func(it) - is Block -> it.itemList.iterate(func) + is BaseItem -> func(it) + is ItemGroup -> it.itemList.iterate(func) } } } \ No newline at end of file diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/structure/abstraction/Item.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/structure/abstraction/Item.kt index 40a546b78b..eaf3d7afa4 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/structure/abstraction/Item.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/structure/abstraction/Item.kt @@ -1,31 +1,39 @@ package com.tangem.tangemtest._arch.structure.abstraction -import com.tangem.tangemtest._arch.structure.DataHolder import com.tangem.tangemtest._arch.structure.Id -import com.tangem.tangemtest._arch.structure.Payload -import com.tangem.tangemtest._arch.structure.PayloadHolder /** [REDACTED_AUTHOR] */ -interface Item : PayloadHolder { +interface Item { val id: Id var parent: Item? -} + var viewModel: ItemViewModel -abstract class BaseItem( - override var viewModel: ItemViewModel -) : Item, DataHolder> { - override var parent: Item? = null - override val payload: Payload = mutableMapOf() + fun added(parent: Item) { + this.parent = parent + } - fun getData(): D? = viewModel.data + fun removed(parent: Item) { + this.parent = null + } - fun setData(value: D?) { + fun getData(): D? = viewModel.data as? D + + fun setData(value: Any?) { viewModel.data = value } fun restoreDefaultData() { setData(viewModel.defaultData) } +} + +open class BaseItem( + override val id: Id, + override var viewModel: ItemViewModel +) : Item { + + override var parent: Item? = null + } \ No newline at end of file diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/structure/abstraction/Block.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/structure/abstraction/ItemGroup.kt similarity index 61% rename from tangem-demo/src/main/java/com/tangem/tangemtest/_arch/structure/abstraction/Block.kt rename to tangem-demo/src/main/java/com/tangem/tangemtest/_arch/structure/abstraction/ItemGroup.kt index 11086e6f93..61a43a0d1c 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/structure/abstraction/Block.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/structure/abstraction/ItemGroup.kt @@ -2,45 +2,47 @@ package com.tangem.tangemtest._arch.structure.abstraction import com.tangem.tangemtest._arch.structure.ILog import com.tangem.tangemtest._arch.structure.Id -import com.tangem.tangemtest._arch.structure.ItemListHolder /** [REDACTED_AUTHOR] */ -interface Block : Item { +interface ItemGroup : Item { val itemList: MutableList + + fun setItems(list: MutableList) + fun getItems(): MutableList + fun addItem(item: Item) + fun removeItem(item: Item) + fun clear() } -abstract class BaseBlock : Block { +open class SimpleItemGroup( + override val id: Id, + override var viewModel: ItemViewModel = BaseItemViewModel() +) : ItemGroup { + override var parent: Item? = null override val itemList: MutableList = mutableListOf() - override val payload: MutableMap = mutableMapOf() -} - -open class ListItemBlock( - override val id: Id -) : BaseBlock(), ItemListHolder { override fun setItems(list: MutableList) { ILog.d(this, "setItems into: $id, count: ${list.size}") + itemList.forEach { it.removed(this) } itemList.clear() - itemList.addAll(list) - itemList.forEach { it.parent = this } + list.forEach { addItem(it) } } - override fun getItems(): MutableList { - return itemList - } + override fun getItems(): MutableList = itemList override fun addItem(item: Item) { ILog.d(this, "addItem into: $id, who: ${item.id}") - item.parent = this itemList.add(item) + item.added(this) } override fun removeItem(item: Item) { ILog.d(this, "removeItem from: $id, which: ${item.id}") itemList.remove(item) + item.removed(this) } override fun clear() { diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/structure/abstraction/ViewModel.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/structure/abstraction/ItemViewModel.kt similarity index 70% rename from tangem-demo/src/main/java/com/tangem/tangemtest/_arch/structure/abstraction/ViewModel.kt rename to tangem-demo/src/main/java/com/tangem/tangemtest/_arch/structure/abstraction/ItemViewModel.kt index 86d1d1b345..d298f84f93 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/structure/abstraction/ViewModel.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/structure/abstraction/ItemViewModel.kt @@ -11,54 +11,7 @@ import com.tangem.tangemtest._arch.structure.PayloadHolder typealias ValueChange = (V?) -> Unit typealias SafeValueChange = (V) -> Unit -interface ItemViewModel : PayloadHolder { - val viewState: ViewState - var data: D? - var defaultData: D? - var onDataUpdated: ValueChange? - - fun updateDataByView(data: D?) -} - -open class BaseItemViewModel : ItemViewModel { - - override val viewState: ViewState = ViewState() - override val payload: Payload = mutableMapOf() - - // Don't update it directly from a View. Use for it updateDataByView() - override var data: D? = null - set(value) { - if (handleDataUpdates(value)) field = value - } - - // Data for restoring initial value - override var defaultData: D? = null - set(value) { - field = value - data = value - } - - // Use it for handling data updates in View - override var onDataUpdated: ValueChange? = null - - // When data updates directly it invokes onDataUpdated - // return true = data will update - // return false = data won't update - protected open fun handleDataUpdates(value: D?): Boolean { - ILog.d(this, "handleDateUpdates: $value") - onDataUpdated?.invoke(value) - return true - } - - // Use it to update the data from a View. It disables onDataUpdated to prevent a callback loop - override fun updateDataByView(data: D?) { - ILog.d(this, "data changed: $data") - val callback = onDataUpdated - onDataUpdated = null - this.data = data - onDataUpdated = callback - } -} +class KeyValue(val key: String, val value: Any) class ViewState { var isHiddenField = false @@ -70,4 +23,58 @@ class ViewState { } var onDescriptionVisibilityChanged: SafeValueChange? = null -} \ No newline at end of file +} + +interface ItemViewModel : PayloadHolder { + val viewState: ViewState + var data: Any? + var defaultData: Any? + var onDataUpdated: ValueChange? + + fun updateDataByView(data: Any?) +} + +open class BaseItemViewModel(value: Any? = null) : ItemViewModel { + + override val viewState: ViewState = ViewState() + override val payload: Payload = mutableMapOf() + + // Don't update it directly from a View. Use for it updateDataByView() + override var data: Any? = value + set(value) { + if (handleDataUpdates(value)) field = value + } + + // Data for restoring initial value + override var defaultData: Any? = value + set(value) { + field = value + data = value + } + + // Use it for handling data updates in View + override var onDataUpdated: ValueChange? = null + + // When data updates directly it invokes onDataUpdated + // return true = data will update + // return false = data won't update + protected open fun handleDataUpdates(value: Any?): Boolean { + ILog.d(this, "handleDateUpdates: $value") + onDataUpdated?.invoke(value) + return true + } + + // Use it to update the data from a View. It disables onDataUpdated to prevent a callback loop + override fun updateDataByView(data: Any?) { + ILog.d(this, "data changed: $data") + val callback = onDataUpdated + onDataUpdated = null + this.data = data + onDataUpdated = callback + } +} + +class ListViewModel( + var selectedItem: Any?, + val itemList: List +) : BaseItemViewModel(selectedItem) \ No newline at end of file diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/structure/impl/Items.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/structure/impl/Items.kt index 5e6d10bd37..923f58f94a 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/structure/impl/Items.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/structure/impl/Items.kt @@ -2,18 +2,24 @@ package com.tangem.tangemtest._arch.structure.impl import com.tangem.tangemtest._arch.structure.Id import com.tangem.tangemtest._arch.structure.abstraction.BaseItem +import com.tangem.tangemtest._arch.structure.abstraction.BaseItemViewModel +import com.tangem.tangemtest._arch.structure.abstraction.KeyValue +import com.tangem.tangemtest._arch.structure.abstraction.ListViewModel /** [REDACTED_AUTHOR] */ -class AnyItem(override val id: Id, value: Any? = null) : BaseItem(AnyViewModel(value)) -class TextItem(override val id: Id, value: String? = null) : BaseItem(StringViewModel(value)) -class EditTextItem(override val id: Id, value: String? = null) : BaseItem(StringViewModel(value)) -class NumberItem(override val id: Id, value: Number? = null) : BaseItem(NumberViewModel(value)) -class BoolItem(override val id: Id, value: Boolean? = null) : BaseItem(BoolViewModel(value)) -class ListItem(override val id: Id, value: List, selectedValue: Any?) - : BaseItem(ListViewModel(ListValueWrapper(selectedValue, value)) -) +open class TypedItem(id: Id, value: D? = null) : BaseItem(id, BaseItemViewModel(value)) { + open fun getTypedData(): D? = viewModel.data as? D +} -inline fun BaseItem.getData(): T? = viewModel.data \ No newline at end of file +class TextItem(id: Id, value: String? = null) : TypedItem(id, value) +class NumberItem(id: Id, value: Number? = null) : TypedItem(id, value) +class BoolItem(id: Id, value: Boolean? = null) : TypedItem(id, value) + +class EditTextItem(id: Id, value: String? = null) : TypedItem(id, value) + +class SpinnerItem(id: Id, value: List, selectedValue: Any?) + : TypedItem(id, ListViewModel(selectedValue, value) +) \ No newline at end of file diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/structure/impl/ViewModels.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/structure/impl/ViewModels.kt deleted file mode 100644 index f082577e70..0000000000 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/structure/impl/ViewModels.kt +++ /dev/null @@ -1,29 +0,0 @@ -package com.tangem.tangemtest._arch.structure.impl - -import com.tangem.tangemtest._arch.structure.abstraction.BaseItemViewModel - -/** -[REDACTED_AUTHOR] - */ -open class TransitiveViewModel(value: D?) : BaseItemViewModel() { - init { - defaultData = value - } -} - -class AnyViewModel(value: Any? = null) : TransitiveViewModel(value) -class StringViewModel(value: String? = null) : TransitiveViewModel(value) -class NumberViewModel(value: Number? = null) : TransitiveViewModel(value) -class BoolViewModel(value: Boolean? = null) : TransitiveViewModel(value) -class ListViewModel(value: ListValueWrapper? = null) : TransitiveViewModel(value) { - - override fun handleDataUpdates(value: ListValueWrapper?): Boolean { - val newValue = value ?: return false - if (newValue.selectedItem == data?.selectedItem) return false - - return super.handleDataUpdates(value) - } -} - -class KeyValue(val key: String, val value: Any) -class ListValueWrapper(var selectedItem: Any?, val itemList: List) \ No newline at end of file diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/widget/ItemWidgetBuilder.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/widget/ItemWidgetBuilder.kt index b507573e99..2ed1e1ab8f 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/widget/ItemWidgetBuilder.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/widget/ItemWidgetBuilder.kt @@ -8,5 +8,5 @@ import com.tangem.tangemtest._arch.widget.abstraction.ViewWidget [REDACTED_AUTHOR] */ interface ItemWidgetBuilder { - fun build(item: BaseItem<*>, parent: ViewGroup): ViewWidget? + fun build(item: BaseItem, parent: ViewGroup): ViewWidget? } \ No newline at end of file diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/widget/WidgetBuilder.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/widget/WidgetBuilder.kt index a524006f74..3d9a3493a5 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/widget/WidgetBuilder.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/widget/WidgetBuilder.kt @@ -2,11 +2,10 @@ package com.tangem.tangemtest._arch.widget import android.view.ViewGroup 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.ListItemBlock +import com.tangem.tangemtest._arch.structure.abstraction.ItemGroup import com.tangem.tangemtest._arch.widget.abstraction.ViewWidget -import com.tangem.tangemtest._arch.widget.impl.LinearBlockWidget +import com.tangem.tangemtest._arch.widget.impl.LinearGroupWidget import com.tangem.tangemtest._arch.widget.impl.StubWidget /** @@ -18,20 +17,20 @@ class WidgetBuilder( fun build(item: Item, parent: ViewGroup): ViewWidget? { return when (item) { - is Block -> buildBlock(item, parent) - is BaseItem<*> -> itemBuilder.build(item, parent) - else -> StubWidget(parent) + is ItemGroup -> buildBlock(item, parent) + is BaseItem -> itemBuilder.build(item, parent) + else -> StubWidget(item.id, parent) } } - private fun buildBlock(block: Block, parent: ViewGroup): ViewWidget { - return when (block) { - is ListItemBlock -> { - val linearBlock = LinearBlockWidget(parent, block) - block.getItems().forEach { build(it, linearBlock.view as ViewGroup) } + private fun buildBlock(itemGroup: ItemGroup, parent: ViewGroup): ViewWidget { + return when (itemGroup) { + is ItemGroup -> { + val linearBlock = LinearGroupWidget(parent, itemGroup) + itemGroup.getItems().forEach { build(it, linearBlock.view as ViewGroup) } linearBlock } - else -> StubWidget(parent) + else -> StubWidget(itemGroup.id, parent) } } } \ No newline at end of file diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/widget/abstraction/ViewWidget.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/widget/abstraction/ViewWidget.kt index 7fa8fd5bbb..8a220c0f1a 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/widget/abstraction/ViewWidget.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/widget/abstraction/ViewWidget.kt @@ -4,13 +4,9 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import com.tangem.tangemtest.R -import com.tangem.tangemtest._arch.structure.DataHolder import com.tangem.tangemtest._arch.structure.StringId -import com.tangem.tangemtest._arch.structure.abstraction.BaseItem -import com.tangem.tangemtest._arch.structure.abstraction.ItemViewModel -import com.tangem.tangemtest.ucase.resources.MainResourceHolder -import com.tangem.tangemtest.ucase.resources.Resources -import ru.dev.gbixahue.eu4d.lib.android._android.views.stringFrom +import com.tangem.tangemtest._arch.structure.StringResId +import com.tangem.tangemtest._arch.structure.abstraction.Item import ru.dev.gbixahue.eu4d.lib.kotlin.common.LayoutHolder /** @@ -18,39 +14,31 @@ import ru.dev.gbixahue.eu4d.lib.kotlin.common.LayoutHolder */ interface ViewWidget : LayoutHolder { val view: View + var item: Item + + fun getName(): String } -interface DataWidget : ViewWidget { - var dataItem: BaseItem -} - -interface BlockViewWidget : ViewWidget, DataHolder>> - -abstract class BaseWidget( +abstract class BaseViewWidget( parent: ViewGroup, - override var dataItem: BaseItem -) : ViewWidget, DataWidget { + override var item: Item +) : ViewWidget { override val view: View = inflate(getLayoutId(), parent) init { - if (dataItem.viewModel.viewState.isHiddenField) { + if (item.viewModel.viewState.isHiddenField) { view.visibility = View.GONE } } -} -abstract class BaseBlockWidget(parent: ViewGroup) : BlockViewWidget { - override val view: View = inflate(getLayoutId(), parent) -} - -fun DataWidget<*>.getResNameId(): Int = MainResourceHolder.safeGet(dataItem.id).resName - -fun DataWidget<*>.getResDescription(): Int? = MainResourceHolder.safeGet(dataItem.id).resDescription - -fun DataWidget<*>.getName(): String { - val id = dataItem.id - return if (id is StringId) id.name else view.stringFrom(getResNameId()) + override fun getName(): String { + return when (val id = item.id) { + is StringId -> id.value + is StringResId -> view.resources.getString(id.value) + else -> view.resources.getString(R.string.unknown) + } + } } internal fun inflate(id: Int, parent: ViewGroup): View { diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/widget/impl/LinearBlockWidget.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/widget/impl/LinearBlockWidget.kt deleted file mode 100644 index 5f1d268c6f..0000000000 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/widget/impl/LinearBlockWidget.kt +++ /dev/null @@ -1,27 +0,0 @@ -package com.tangem.tangemtest._arch.widget.impl - -import android.view.ViewGroup -import com.tangem.tangemtest.R -import com.tangem.tangemtest._arch.structure.abstraction.ItemViewModel -import com.tangem.tangemtest._arch.structure.abstraction.ListItemBlock -import com.tangem.tangemtest._arch.structure.abstraction.iterate -import com.tangem.tangemtest._arch.widget.abstraction.BaseBlockWidget - -/** -[REDACTED_AUTHOR] - */ -class LinearBlockWidget( - parent: ViewGroup, - private val children: ListItemBlock -) : BaseBlockWidget(parent) { - override fun getLayoutId(): Int = R.layout.w_personilize_block - - override var viewModel: List> = listOf() - get() { - val vmList = mutableListOf>() - children.itemList.iterate { item -> - (item as? ItemViewModel<*>)?.let { vmList.add(it) } - } - return vmList.toList() - } -} \ No newline at end of file diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/widget/impl/LinearGroupWidget.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/widget/impl/LinearGroupWidget.kt new file mode 100644 index 0000000000..97c8d36a35 --- /dev/null +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/widget/impl/LinearGroupWidget.kt @@ -0,0 +1,18 @@ +package com.tangem.tangemtest._arch.widget.impl + +import android.view.ViewGroup +import com.tangem.tangemtest.R +import com.tangem.tangemtest._arch.structure.abstraction.ItemGroup +import com.tangem.tangemtest._arch.widget.abstraction.BaseViewWidget + +/** +[REDACTED_AUTHOR] + */ +class LinearGroupWidget( + parent: ViewGroup, + itemGroup: ItemGroup +) : BaseViewWidget(parent, itemGroup) { + + override fun getLayoutId(): Int = R.layout.w_personilize_block + +} \ No newline at end of file diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/widget/impl/StubWidget.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/widget/impl/StubWidget.kt index 98e2bcd57a..1b832e362a 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/widget/impl/StubWidget.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/widget/impl/StubWidget.kt @@ -2,13 +2,14 @@ package com.tangem.tangemtest._arch.widget.impl import android.view.ViewGroup import com.tangem.tangemtest.R -import com.tangem.tangemtest._arch.structure.abstraction.ItemViewModel -import com.tangem.tangemtest._arch.widget.abstraction.BaseBlockWidget +import com.tangem.tangemtest._arch.structure.Id +import com.tangem.tangemtest._arch.structure.abstraction.BaseItem +import com.tangem.tangemtest._arch.structure.abstraction.BaseItemViewModel +import com.tangem.tangemtest._arch.widget.abstraction.BaseViewWidget /** [REDACTED_AUTHOR] */ -class StubWidget(parent: ViewGroup) : BaseBlockWidget(parent) { +class StubWidget(id: Id, parent: ViewGroup) : BaseViewWidget(parent, BaseItem(id, BaseItemViewModel())) { override fun getLayoutId(): Int = R.layout.w_empty - override var viewModel: List> = listOf() } \ No newline at end of file diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/commons/GlobalFunctions.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/commons/GlobalFunctions.kt index 7a19cf981f..59ae0da2b9 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/commons/GlobalFunctions.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/commons/GlobalFunctions.kt @@ -3,6 +3,7 @@ package com.tangem.tangemtest.commons /** [REDACTED_AUTHOR] */ -fun performAction(a: A?, b: B?, action: (A, B) -> Unit) { +fun performAction(a: A?, b: B?, action: (A, B) -> Unit, onFail: (() -> Unit)? = null) { if (a != null && b != null) action(a, b) + else onFail?.invoke() } \ No newline at end of file diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/actions/DepersonalizeAction.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/actions/DepersonalizeAction.kt index 2433302a4b..5f49aa15aa 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/actions/DepersonalizeAction.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/actions/DepersonalizeAction.kt @@ -2,7 +2,7 @@ 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._arch.structure.abstraction.findItem import com.tangem.tangemtest.ucase.domain.paramsManager.ActionCallback import com.tangem.tangemtest.ucase.variants.TlvId @@ -11,7 +11,7 @@ import com.tangem.tangemtest.ucase.variants.TlvId */ class DepersonalizeAction : BaseAction() { override fun executeMainAction(payload: PayloadHolder, attrs: AttrForAction, callback: ActionCallback) { - val item = attrs.itemList.findDataItem(TlvId.CardId) ?: return + val item = attrs.itemList.findItem(TlvId.CardId) ?: return val cardId = item.viewModel.data as? String ?: return attrs.cardManager.depersonalize(cardId) { handleResult(payload, it, null, attrs, callback) } diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/actions/PersonalizeAction.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/actions/PersonalizeAction.kt index a50bc09456..21143ddec8 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/actions/PersonalizeAction.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/actions/PersonalizeAction.kt @@ -2,7 +2,7 @@ package com.tangem.tangemtest.ucase.domain.actions 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.abstraction.findItem import com.tangem.tangemtest._arch.structure.impl.EditTextItem import com.tangem.tangemtest._arch.structure.impl.NumberItem import com.tangem.tangemtest.ucase.domain.paramsManager.ActionCallback @@ -11,7 +11,7 @@ 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.PersonalizationConfigConverter -import com.tangem.tangemtest.ucase.variants.personalize.converter.fromTo.PersonalizeConfigToCardConfig +import com.tangem.tangemtest.ucase.variants.personalize.converter.PersonalizationConfigToCardConfig import com.tangem.tangemtest.ucase.variants.personalize.dto.DefaultPersonalizationParams import com.tangem.tangemtest.ucase.variants.personalize.dto.PersonalizationConfig import ru.dev.gbixahue.eu4d.lib.kotlin.stringOf @@ -38,7 +38,7 @@ class PersonalizeAction : BaseAction() { val manufacturer = DefaultPersonalizationParams.manufacturer() val personalizeConfig = PersonalizationConfigConverter().convert(itemList, PersonalizationConfig()) - val cardConfig = PersonalizeConfigToCardConfig().convert(personalizeConfig) + val cardConfig = PersonalizationConfigToCardConfig().convert(personalizeConfig) attrs.cardManager.personalize(cardConfig, issuer, manufacturer, acquirer) { handleResult(payload, it, null, attrs, callback) @@ -46,18 +46,18 @@ class PersonalizeAction : BaseAction() { } private fun checkSeries(itemList: List): Boolean { - val seriesItem = itemList.findDataItem(CardNumber.Series) as? EditTextItem ?: return false - val data = seriesItem.getData() ?: return false + val seriesItem = itemList.findItem(CardNumber.Series) as? EditTextItem ?: return false + val data = seriesItem.getData() as? String ?: return false seriesItem.setData(data.toUpperCase()) return data.length == 2 || data.length == 4 } private fun checkNumber(itemList: List): 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()) + val seriesItem = itemList.findItem(CardNumber.Series) as? EditTextItem ?: return false + val numberItem = itemList.findItem(CardNumber.Number) as? NumberItem ?: return false + val seriesData = seriesItem.getData() as? String ?: return false + val numberData = stringOf(numberItem.getData() as? Number) return if (seriesData.length == 2 && numberData.length > 13) false else !(seriesData.length == 4 && numberData.length > 11) diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/actions/SignAction.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/actions/SignAction.kt index 18fef1d8c5..d71eaeb900 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/actions/SignAction.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/actions/SignAction.kt @@ -2,7 +2,7 @@ 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._arch.structure.abstraction.findItem import com.tangem.tangemtest.ucase.domain.paramsManager.ActionCallback import com.tangem.tangemtest.ucase.variants.TlvId import ru.dev.gbixahue.eu4d.lib.kotlin.stringOf @@ -12,9 +12,9 @@ import ru.dev.gbixahue.eu4d.lib.kotlin.stringOf */ class SignAction : BaseAction() { override fun executeMainAction(payload: PayloadHolder, attrs: AttrForAction, callback: ActionCallback) { - val dataForHashing = attrs.itemList.findDataItem(TlvId.TransactionOutHash) ?: return + val dataForHashing = attrs.itemList.findItem(TlvId.TransactionOutHash) ?: return val hash = dataForHashing.getData() as? ByteArray ?: return - val cardId = attrs.itemList.findDataItem(TlvId.CardId)?.viewModel?.data ?: return + val cardId = attrs.itemList.findItem(TlvId.CardId)?.viewModel?.data ?: return attrs.cardManager.sign(arrayOf(hash), stringOf(cardId)) { handleResult(payload, it, null, attrs, callback) } } diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/managers/BaseItemsManager.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/managers/BaseItemsManager.kt index bfb987742d..72729d11eb 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/managers/BaseItemsManager.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/managers/BaseItemsManager.kt @@ -5,7 +5,7 @@ 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._arch.structure.abstraction.findItem import com.tangem.tangemtest.ucase.domain.actions.Action import com.tangem.tangemtest.ucase.domain.actions.AttrForAction import com.tangem.tangemtest.ucase.domain.paramsManager.ActionCallback @@ -30,7 +30,7 @@ open class BaseItemsManager(protected val action: Action) : ItemsManager, Lifecy override fun itemChanged(id: Id, value: Any?, callback: AffectedItemsCallback?) { if (itemList.isEmpty()) return - val foundItem = itemList.findDataItem(id) ?: return + val foundItem = itemList.findItem(id) ?: return foundItem.setData(value) applyChangesByAffectedItems(foundItem, callback) diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/triggers/afterAction/AfterScanModifier.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/triggers/afterAction/AfterScanModifier.kt index 990f2a78f4..5cf644eb32 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/triggers/afterAction/AfterScanModifier.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/triggers/afterAction/AfterScanModifier.kt @@ -4,7 +4,7 @@ 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._arch.structure.abstraction.findItem import com.tangem.tangemtest.ucase.domain.paramsManager.PayloadKey import com.tangem.tangemtest.ucase.tunnel.ActionView import com.tangem.tangemtest.ucase.tunnel.CardError @@ -18,7 +18,7 @@ import ru.dev.gbixahue.eu4d.lib.android.global.threading.postUI */ class AfterScanModifier : AfterActionModification { override fun modify(payload: PayloadHolder, taskEvent: TaskEvent<*>, itemList: List): List { - val foundItem = itemList.findDataItem(TlvId.CardId) ?: return listOf() + val foundItem = itemList.findItem(TlvId.CardId) ?: return listOf() val card = smartCast(taskEvent)?.card ?: return listOf() val actionView = payload.get(PayloadKey.actionView) as? ActionView ?: return listOf() diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/triggers/changeConsequence/ParamChangeConsequence.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/triggers/changeConsequence/ParamChangeConsequence.kt index 41bd4cd0a3..730c570be6 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/triggers/changeConsequence/ParamChangeConsequence.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/triggers/changeConsequence/ParamChangeConsequence.kt @@ -6,7 +6,7 @@ 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._arch.structure.abstraction.findItem import com.tangem.tangemtest.ucase.domain.paramsManager.PayloadKey import com.tangem.tangemtest.ucase.tunnel.ActionView import com.tangem.tangemtest.ucase.variants.TlvId @@ -27,7 +27,7 @@ class SignScanConsequence : ItemsChangeConsequence { override fun affectChanges(payload: PayloadHolder, changedItem: Item, itemList: List): List? { if (changedItem.id != TlvId.CardId) return null - val hashItem = itemList.findDataItem(TlvId.TransactionOutHash) ?: return null + val hashItem = itemList.findItem(TlvId.TransactionOutHash) ?: return null val affectedItems = mutableListOf(hashItem) val card = payload.remove(PayloadKey.card) as Card? if (card == null) { diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/ActionViewModel.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/ActionViewModel.kt index 8ffcb80328..e1b63c7df7 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/ActionViewModel.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/ActionViewModel.kt @@ -8,7 +8,6 @@ import com.tangem.CardManager import com.tangem.tangemtest._arch.SingleLiveEvent import com.tangem.tangemtest._arch.structure.Id import com.tangem.tangemtest._arch.structure.Payload -import com.tangem.tangemtest._arch.structure.abstraction.BaseItem import com.tangem.tangemtest._arch.structure.abstraction.Item import com.tangem.tangemtest._arch.structure.abstraction.iterate import com.tangem.tangemtest.commons.performAction @@ -55,11 +54,11 @@ class ActionViewModel(private val itemsManager: ItemsManager) : ViewModel(), Lif //invokes Scan, Sign etc... fun invokeMainAction() { - performAction(itemsManager, cardManager) { paramsManager, cardManager -> + performAction(itemsManager, cardManager, { paramsManager, cardManager -> paramsManager.invokeMainAction(cardManager) { response, listOfChangedParams -> notifier.handleActionResult(response, listOfChangedParams) } - } + }) } fun getItemAction(id: Id): (() -> Unit)? { @@ -74,8 +73,7 @@ class ActionViewModel(private val itemsManager: ItemsManager) : ViewModel(), Lif fun toggleDescriptionVisibility(state: Boolean) { ldItemList.value?.iterate { - val baseItem = it as? BaseItem<*> ?: return@iterate - baseItem.viewModel.viewState.descriptionVisibility = if (state) View.VISIBLE else View.GONE + it.viewModel.viewState.descriptionVisibility = if (state) View.VISIBLE else View.GONE } } diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/BaseCardActionFragment.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/BaseCardActionFragment.kt index 44acd319b8..b9186f495a 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/BaseCardActionFragment.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/BaseCardActionFragment.kt @@ -13,7 +13,6 @@ import com.tangem.CardManager import com.tangem.tangem_sdk_new.extensions.init import com.tangem.tangemtest.R import com.tangem.tangemtest._arch.structure.Id -import com.tangem.tangemtest._arch.structure.abstraction.BaseItem import com.tangem.tangemtest._main.MainViewModel import com.tangem.tangemtest.ucase.domain.paramsManager.ItemsManager import com.tangem.tangemtest.ucase.domain.paramsManager.PayloadKey @@ -106,9 +105,7 @@ abstract class BaseCardActionFragment : BaseFragment(), ActionView { protected open fun listenChangedItems() { actionVM.seChangedItems.observe(viewLifecycleOwner, Observer { itemList -> itemList.forEach { item -> - Log.d(this, "item changed from VM - name: ${item.id}") - val dataItem = item as? BaseItem ?: return@Observer - Log.d(this, "item changed from VM - name: ${dataItem.id}, value:${dataItem.viewModel.data}") + Log.d(this, "item changed from VM - name: ${item.id}, value:${item.viewModel.data}") paramsWidgetList.firstOrNull { it.id == item.id }?.changeParamValue(item.viewModel.data) } }) diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/widgets/ParameterWidget.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/widgets/ParameterWidget.kt index 15ba1cdcba..96037984ea 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/widgets/ParameterWidget.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/widgets/ParameterWidget.kt @@ -12,8 +12,8 @@ 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.Id -import com.tangem.tangemtest._arch.structure.abstraction.BaseItem import com.tangem.tangemtest._arch.structure.abstraction.Item +import com.tangem.tangemtest._arch.structure.impl.EditTextItem import com.tangem.tangemtest.ucase.resources.ActionType import com.tangem.tangemtest.ucase.resources.MainResourceHolder import com.tangem.tangemtest.ucase.resources.Resources @@ -29,7 +29,7 @@ class ParameterWidget( ) { val id: Id = item.id - val dataItem: BaseItem = item as BaseItem + val typedItem = item as EditTextItem var onValueChanged: ((Id, Any?) -> Unit)? = null var onActionBtnClickListener: (() -> Unit)? = null @@ -47,11 +47,11 @@ class ParameterWidget( private val tvDescription: TextView? by lazy { descriptionContainer.findViewById(R.id.tv_description) } private var actionBtnVisibilityState: Int = btnAction.visibility - private var value: Any? = dataItem.viewModel.data + private var value: Any? = typedItem.viewModel.data init { tilValue.hint = tilValue.context.getString(getResNameId()) - etValue.setText(stringOf(dataItem.viewModel.data)) + etValue.setText(stringOf(typedItem.viewModel.data)) etValue.addTextChangedListener(valueWatcher) btnAction.setOnClickListener { onActionBtnClickListener?.invoke() } btnAction.setText(getResNameId(ActionType.Scan)) diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/converter/DefaultValues.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/converter/DefaultValues.kt index 49dd2faa8d..37364a0f8a 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/converter/DefaultValues.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/converter/DefaultValues.kt @@ -2,7 +2,7 @@ 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._arch.structure.abstraction.KeyValue import com.tangem.tangemtest.ucase.variants.personalize.* import com.tangem.tangemtest.ucase.variants.personalize.dto.PersonalizationConfig import ru.dev.gbixahue.eu4d.lib.kotlin.common.BaseTypedHolder diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/converter/fromTo/ItemTypes.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/converter/ItemTypes.kt similarity index 99% rename from tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/converter/fromTo/ItemTypes.kt rename to tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/converter/ItemTypes.kt index 9902d38da4..238a0aeaa0 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/converter/fromTo/ItemTypes.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/converter/ItemTypes.kt @@ -1,4 +1,4 @@ -package com.tangem.tangemtest.ucase.variants.personalize.converter.fromTo +package com.tangem.tangemtest.ucase.variants.personalize.converter import com.tangem.tangemtest.ucase.variants.personalize.* diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/converter/PersonalizationConfigConverter.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/converter/PersonalizationConfigConverter.kt index 7fbf672259..c350690412 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/converter/PersonalizationConfigConverter.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/converter/PersonalizationConfigConverter.kt @@ -1,12 +1,12 @@ package com.tangem.tangemtest.ucase.variants.personalize.converter -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.ItemsToPersonalizationConfig -import com.tangem.tangemtest.ucase.variants.personalize.converter.fromTo.PersonalizationConfigToItems +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.variants.personalize.* import com.tangem.tangemtest.ucase.variants.personalize.dto.PersonalizationConfig +import ru.dev.gbixahue.eu4d.lib.android.global.log.Log /** [REDACTED_AUTHOR] @@ -23,4 +23,281 @@ class PersonalizationConfigConverter : ModelConverter { override fun convert(from: PersonalizationConfig): List { return toItems.convert(from) } +} + +class ItemsToPersonalizationConfig : ItemsToModel { + protected val valuesHolder = ConfigValuesHolder() + + override fun convert(from: List, default: PersonalizationConfig): PersonalizationConfig { + valuesHolder.init(default) + mapListItems(from) + return createModel() + } + + private fun mapListItems(itemList: List) { + itemList.iterate { item -> mapItemToHolder(item) } + } + + private fun mapItemToHolder(item: Item) { + when (item) { + is ItemGroup -> mapListItems(item.itemList) + is BaseItem -> { + val defValue = valuesHolder.get(item.id) ?: return + defValue.set(item.viewModel.data) + } + } + } + + private fun createModel(): PersonalizationConfig { + val export = PersonalizationConfig() + export.series = getTyped(CardNumber.Series) + export.startNumber = getTyped(CardNumber.Number) + export.curveID = getTyped(Common.Curve) + export.blockchain = getTyped(Common.Blockchain) + export.blockchainCustom = getTyped(Common.BlockchainCustom) + export.MaxSignatures = getTyped(Common.MaxSignatures) + export.createWallet = getTyped(Common.CreateWallet) + export.SigningMethod0 = getTyped(SigningMethod.SignTx) + export.SigningMethod1 = getTyped(SigningMethod.SignTxRaw) + export.SigningMethod2 = getTyped(SigningMethod.SignValidatedTx) + export.SigningMethod3 = getTyped(SigningMethod.SignValidatedTxRaw) + export.SigningMethod4 = getTyped(SigningMethod.SignValidatedTxIssuer) + export.SigningMethod5 = getTyped(SigningMethod.SignValidatedTxRawIssuer) + export.SigningMethod6 = getTyped(SigningMethod.SignExternal) + export.pinLessFloorLimit = getTyped(SignHashExProp.PinLessFloorLimit) + export.hexCrExKey = getTyped(SignHashExProp.CryptoExKey) + export.requireTerminalCertSignature = getTyped(SignHashExProp.RequireTerminalCertSig) + export.requireTerminalTxSignature = getTyped(SignHashExProp.RequireTerminalTxSig) + export.checkPIN3onCard = getTyped(SignHashExProp.CheckPin3) + export.writeOnPersonalization = getTyped(Denomination.WriteOnPersonalize) + export.denomination = getTyped(Denomination.Denomination) + export.itsToken = getTyped(Token.ItsToken) + export.symbol = getTyped(Token.Symbol) + export.contractAddress = getTyped(Token.ContractAddress) + 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.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) + export.allowSelectBlockchain = getTyped(SettingsMask.AllowSelectBlockchain) + export.useBlock = getTyped(SettingsMask.UseBlock) + export.oneApdu = getTyped(SettingsMask.OneApdu) + export.useCVC = getTyped(SettingsMask.UseCvc) + export.allowSwapPIN = getTyped(SettingsMask.AllowSwapPin) + export.allowSwapPIN2 = getTyped(SettingsMask.AllowSwapPin2) + export.forbidDefaultPIN = getTyped(SettingsMask.ForbidDefaultPin) + export.smartSecurityDelay = getTyped(SettingsMask.SmartSecurityDelay) + export.protectIssuerDataAgainstReplay = getTyped(SettingsMask.ProtectIssuerDataAgainstReplay) + export.skipSecurityDelayIfValidatedByIssuer = getTyped(SettingsMask.SkipSecurityDelayIfValidated) + export.skipCheckPIN2andCVCIfValidatedByIssuer = getTyped(SettingsMask.SkipPin2CvcIfValidated) + export.skipSecurityDelayIfValidatedByLinkedTerminal = getTyped(SettingsMask.SkipSecurityDelayOnLinkedTerminal) + export.restrictOverwriteIssuerDataEx = getTyped(SettingsMask.RestrictOverwriteExtraIssuerData) + export.protocolAllowUnencrypted = getTyped(SettingsMaskProtocolEnc.AllowUnencrypted) + export.protocolAllowStaticEncryption = getTyped(SettingsMaskProtocolEnc.AllowStaticEncryption) + export.useNDEF = getTyped(SettingsMaskNdef.UseNdef) + export.useDynamicNDEF = getTyped(SettingsMaskNdef.DynamicNdef) + export.disablePrecomputedNDEF = getTyped(SettingsMaskNdef.DisablePrecomputedNdef) + export.aar = getTyped(SettingsMaskNdef.Aar) + export.aarCustom = getTyped(SettingsMaskNdef.AarCustom) + export.uri = getTyped(SettingsMaskNdef.Uri) + export.PIN = getTyped(Pins.Pin) + export.PIN2 = getTyped(Pins.Pin2) + export.PIN3 = getTyped(Pins.Pin3) + export.CVC = getTyped(Pins.Cvc) + export.pauseBeforePIN2 = getTyped(Pins.PauseBeforePin2) + return export + } + + private inline fun getTyped(id: Id): Type { + return getTypedBy(valuesHolder, id)!! + } + + private inline fun getTypedBy(holder: ConfigValuesHolder, id: Id): Type? { + Log.d(this, "getTyped for id: $id") + var typedValue = holder.get(id)?.get() + + typedValue = when (typedValue) { + is ListViewModel -> typedValue.selectedItem as Type + else -> typedValue as? Type ?: null + } + return typedValue + } +} + +class PersonalizationConfigToItems : ModelToItems { + private val valuesHolder = ConfigValuesHolder() + private val itemTypes = ItemTypes() + + override fun convert(from: PersonalizationConfig): List { + valuesHolder.init(from) + val blocList = mutableListOf() + blocList.add(cardNumber()) + blocList.add(common()) + blocList.add(signingMethod()) + blocList.add(signHashExProperties()) + blocList.add(denomination()) + blocList.add(token()) + blocList.add(productMask()) + blocList.add(settingsMask()) + blocList.add(settingsMaskProtocolEnc()) + blocList.add(settingsMaskNdef()) + blocList.add(pins()) + blocList.iterate { + if (itemTypes.hiddenList.contains(it.id)) { + it.viewModel.viewState.isHiddenField = true + } + } + return blocList + } + + private fun cardNumber(): ItemGroup { + val block = createGroup(BlockId.CardNumber) + mutableListOf( + CardNumber.Series, + CardNumber.Number, + CardNumber.BatchId + ).forEach { createItem(block, it as Id) } + return block + } + + private fun common(): ItemGroup { + val block = createGroup(BlockId.Common) + mutableListOf( + Common.Curve, + Common.Blockchain, + Common.BlockchainCustom, + Common.MaxSignatures, + Common.CreateWallet + ).forEach { createItem(block, it) } + return block + } + + private fun signingMethod(): ItemGroup { + val block = createGroup(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(): ItemGroup { + val block = createGroup(BlockId.SignHashExProp) + mutableListOf( + SignHashExProp.PinLessFloorLimit, + SignHashExProp.CryptoExKey, + SignHashExProp.RequireTerminalCertSig, + SignHashExProp.RequireTerminalTxSig, + SignHashExProp.CheckPin3 + ).forEach { createItem(block, it) } + return block + } + + private fun denomination(): ItemGroup { + val block = createGroup(BlockId.Denomination) + mutableListOf( + Denomination.WriteOnPersonalize, + Denomination.Denomination + ).forEach { createItem(block, it) } + return block + } + + private fun token(): ItemGroup { + val block = createGroup(BlockId.Token) + mutableListOf( + Token.ItsToken, + Token.Symbol, + Token.ContractAddress, + Token.Decimal + ).forEach { createItem(block, it) } + return block + } + + private fun productMask(): ItemGroup { + val block = createGroup(BlockId.ProdMask) + mutableListOf( + ProductMask.Note, + ProductMask.Tag, + ProductMask.IdCard, + ProductMask.IdIssuerCard + ).forEach { createItem(block, it) } + return block + } + + private fun settingsMask(): ItemGroup { + val block = createGroup(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(): ItemGroup { + val block = createGroup(BlockId.SettingsMaskProtocolEnc) + mutableListOf( + SettingsMaskProtocolEnc.AllowUnencrypted, + SettingsMaskProtocolEnc.AllowStaticEncryption + ).forEach { createItem(block, it) } + return block + } + + private fun settingsMaskNdef(): ItemGroup { + val block = createGroup(BlockId.SettingsMaskNdef) + mutableListOf( + SettingsMaskNdef.UseNdef, + SettingsMaskNdef.DynamicNdef, + SettingsMaskNdef.DisablePrecomputedNdef, + SettingsMaskNdef.Aar, + SettingsMaskNdef.AarCustom, + SettingsMaskNdef.Uri + ).forEach { createItem(block, it) } + return block + } + + private fun pins(): ItemGroup { + val block = createGroup(BlockId.Pins) + mutableListOf(Pins.Pin, Pins.Pin2, Pins.Pin3, Pins.Cvc, Pins.PauseBeforePin2) + .forEach { createItem(block, it) } + return block + } + + private fun createGroup(id: Id): ItemGroup { + return SimpleItemGroup(id).apply { addItem(TextItem(id)) } + } + + private fun createItem(itemGroup: ItemGroup, 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) -> SpinnerItem(id, holder.list as List, 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 -> SimpleItemGroup(Additional.UNDEFINED) + } + itemGroup.addItem(item) + } } \ No newline at end of file diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/converter/PersonalizationConfigToCardConfig.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/converter/PersonalizationConfigToCardConfig.kt new file mode 100644 index 0000000000..fa79df96e0 --- /dev/null +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/converter/PersonalizationConfigToCardConfig.kt @@ -0,0 +1,113 @@ +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.ucase.variants.personalize.dto.PersonalizationConfig +import ru.dev.gbixahue.eu4d.lib.kotlin.common.Converter +import java.util.* + +class PersonalizationConfigToCardConfig : Converter { + + override fun convert(from: PersonalizationConfig): 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() + if (from.uri.isNotEmpty()) { + ndefs.add(NdefRecord(NdefRecord.Type.URI, from.uri)) + } + when (from.aar) { + "None" -> null + "--- CUSTOM ---" -> NdefRecord(NdefRecord.Type.AAR, from.aarCustom) + else -> NdefRecord(NdefRecord.Type.AAR, from.aar) + }?.let { ndefs.add(it) } + + return CardConfig( + "Tangem", + "Tangem Test", + from.series, + from.startNumber, + 1000, + from.PIN, + from.PIN2, + from.PIN3, + from.hexCrExKey, + from.CVC, + from.pauseBeforePIN2.toInt(), + from.smartSecurityDelay, + EllipticCurve.byName(from.curveID) ?: EllipticCurve.Secp256k1, + signingMethod, + from.MaxSignatures.toInt(), + from.isReusable, + from.allowSwapPIN, + from.allowSwapPIN2, + from.useActivation, + from.useCVC, + from.useNDEF, + from.useDynamicNDEF, + from.oneApdu, + from.useBlock, + from.allowSelectBlockchain, + from.forbidPurgeWallet, + from.protocolAllowUnencrypted, + from.protocolAllowStaticEncryption, + from.protectIssuerDataAgainstReplay, + from.forbidDefaultPIN, + from.disablePrecomputedNDEF, + from.skipSecurityDelayIfValidatedByIssuer, + from.skipCheckPIN2andCVCIfValidatedByIssuer, + from.skipSecurityDelayIfValidatedByLinkedTerminal, + from.restrictOverwriteIssuerDataEx, + from.requireTerminalTxSignature, + from.requireTerminalCertSignature, + from.checkPIN3onCard, + from.createWallet, + cardData, + ndefs + ) + } +} \ No newline at end of file diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/converter/fromTo/ItemsToPersonalizationConfig.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/converter/fromTo/ItemsToPersonalizationConfig.kt deleted file mode 100644 index 95fd18908d..0000000000 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/converter/fromTo/ItemsToPersonalizationConfig.kt +++ /dev/null @@ -1,114 +0,0 @@ -package com.tangem.tangemtest.ucase.variants.personalize.converter.fromTo - -import com.tangem.tangemtest._arch.structure.Id -import com.tangem.tangemtest._arch.structure.abstraction.* -import com.tangem.tangemtest._arch.structure.impl.ListValueWrapper -import com.tangem.tangemtest.ucase.variants.personalize.* -import com.tangem.tangemtest.ucase.variants.personalize.converter.ConfigValuesHolder -import com.tangem.tangemtest.ucase.variants.personalize.dto.PersonalizationConfig -import ru.dev.gbixahue.eu4d.lib.android.global.log.Log - -/** -[REDACTED_AUTHOR] - */ -class ItemsToPersonalizationConfig : ItemsToModel { - protected val valuesHolder = ConfigValuesHolder() - - override fun convert(from: List, default: PersonalizationConfig): PersonalizationConfig { - valuesHolder.init(default) - mapListItems(from) - return createModel() - } - - private fun mapListItems(itemList: List) { - itemList.iterate { item -> mapItemToHolder(item) } - } - - private fun mapItemToHolder(item: Item) { - when (item) { - is Block -> mapListItems(item.itemList) - is BaseItem<*> -> { - val defValue = valuesHolder.get(item.id) ?: return - defValue.set(item.viewModel.data) - } - } - } - - private fun createModel(): PersonalizationConfig { - val export = PersonalizationConfig() - export.series = getTyped(CardNumber.Series) - export.startNumber = getTyped(CardNumber.Number) - export.curveID = getTyped(Common.Curve) - export.blockchain = getTyped(Common.Blockchain) - export.blockchainCustom = getTyped(Common.BlockchainCustom) - export.MaxSignatures = getTyped(Common.MaxSignatures) - export.createWallet = getTyped(Common.CreateWallet) - export.SigningMethod0 = getTyped(SigningMethod.SignTx) - export.SigningMethod1 = getTyped(SigningMethod.SignTxRaw) - export.SigningMethod2 = getTyped(SigningMethod.SignValidatedTx) - export.SigningMethod3 = getTyped(SigningMethod.SignValidatedTxRaw) - export.SigningMethod4 = getTyped(SigningMethod.SignValidatedTxIssuer) - export.SigningMethod5 = getTyped(SigningMethod.SignValidatedTxRawIssuer) - export.SigningMethod6 = getTyped(SigningMethod.SignExternal) - export.pinLessFloorLimit = getTyped(SignHashExProp.PinLessFloorLimit) - export.hexCrExKey = getTyped(SignHashExProp.CryptoExKey) - export.requireTerminalCertSignature = getTyped(SignHashExProp.RequireTerminalCertSig) - export.requireTerminalTxSignature = getTyped(SignHashExProp.RequireTerminalTxSig) - export.checkPIN3onCard = getTyped(SignHashExProp.CheckPin3) - export.writeOnPersonalization = getTyped(Denomination.WriteOnPersonalize) - export.denomination = getTyped(Denomination.Denomination) - export.itsToken = getTyped(Token.ItsToken) - export.symbol = getTyped(Token.Symbol) - export.contractAddress = getTyped(Token.ContractAddress) - 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.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) - export.allowSelectBlockchain = getTyped(SettingsMask.AllowSelectBlockchain) - export.useBlock = getTyped(SettingsMask.UseBlock) - export.oneApdu = getTyped(SettingsMask.OneApdu) - export.useCVC = getTyped(SettingsMask.UseCvc) - export.allowSwapPIN = getTyped(SettingsMask.AllowSwapPin) - export.allowSwapPIN2 = getTyped(SettingsMask.AllowSwapPin2) - export.forbidDefaultPIN = getTyped(SettingsMask.ForbidDefaultPin) - export.smartSecurityDelay = getTyped(SettingsMask.SmartSecurityDelay) - export.protectIssuerDataAgainstReplay = getTyped(SettingsMask.ProtectIssuerDataAgainstReplay) - export.skipSecurityDelayIfValidatedByIssuer = getTyped(SettingsMask.SkipSecurityDelayIfValidated) - export.skipCheckPIN2andCVCIfValidatedByIssuer = getTyped(SettingsMask.SkipPin2CvcIfValidated) - export.skipSecurityDelayIfValidatedByLinkedTerminal = getTyped(SettingsMask.SkipSecurityDelayOnLinkedTerminal) - export.restrictOverwriteIssuerDataEx = getTyped(SettingsMask.RestrictOverwriteExtraIssuerData) - export.protocolAllowUnencrypted = getTyped(SettingsMaskProtocolEnc.AllowUnencrypted) - export.protocolAllowStaticEncryption = getTyped(SettingsMaskProtocolEnc.AllowStaticEncryption) - export.useNDEF = getTyped(SettingsMaskNdef.UseNdef) - export.useDynamicNDEF = getTyped(SettingsMaskNdef.DynamicNdef) - export.disablePrecomputedNDEF = getTyped(SettingsMaskNdef.DisablePrecomputedNdef) - export.aar = getTyped(SettingsMaskNdef.Aar) - export.aarCustom = getTyped(SettingsMaskNdef.AarCustom) - export.uri = getTyped(SettingsMaskNdef.Uri) - export.PIN = getTyped(Pins.Pin) - export.PIN2 = getTyped(Pins.Pin2) - export.PIN3 = getTyped(Pins.Pin3) - export.CVC = getTyped(Pins.Cvc) - export.pauseBeforePIN2 = getTyped(Pins.PauseBeforePin2) - return export - } - - private inline fun getTyped(id: Id): Type { - return getTypedBy(valuesHolder, id)!! - } - - private inline fun getTypedBy(holder: ConfigValuesHolder, id: Id): Type? { - Log.d(this, "getTyped for id: $id") - var typedValue = holder.get(id)?.get() - - typedValue = when (typedValue) { - is ListValueWrapper -> typedValue.selectedItem as Type - else -> typedValue as? Type ?: null - } - return typedValue - } -} \ No newline at end of file diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/converter/fromTo/PersonalizeConfigTo.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/converter/fromTo/PersonalizeConfigTo.kt deleted file mode 100644 index 30a581378f..0000000000 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/converter/fromTo/PersonalizeConfigTo.kt +++ /dev/null @@ -1,305 +0,0 @@ -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.ConfigValuesHolder -import com.tangem.tangemtest.ucase.variants.personalize.dto.PersonalizationConfig -import ru.dev.gbixahue.eu4d.lib.kotlin.common.Converter -import java.util.* - -/** -[REDACTED_AUTHOR] - */ -class PersonalizationConfigToItems : ModelToItems { - private val valuesHolder = ConfigValuesHolder() - private val itemTypes = ItemTypes() - - override fun convert(from: PersonalizationConfig): List { - valuesHolder.init(from) - val blocList = mutableListOf() - 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, - SettingsMaskNdef.Uri - ).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: PersonalizationConfig) { - 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, 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 { - - override fun convert(from: PersonalizationConfig): 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() - if (from.uri.isNotEmpty()) { - ndefs.add(NdefRecord(NdefRecord.Type.URI, from.uri)) - } - when (from.aar) { - "None" -> null - "--- CUSTOM ---" -> NdefRecord(NdefRecord.Type.AAR, from.aarCustom) - else -> NdefRecord(NdefRecord.Type.AAR, from.aar) - }?.let { ndefs.add(it) } - - return CardConfig( - "Tangem", - "Tangem Test", - from.series, - from.startNumber, - 1000, - from.PIN, - from.PIN2, - from.PIN3, - from.hexCrExKey, - from.CVC, - from.pauseBeforePIN2.toInt(), - from.smartSecurityDelay, - EllipticCurve.byName(from.curveID) ?: EllipticCurve.Secp256k1, - signingMethod, - from.MaxSignatures.toInt(), - from.isReusable, - from.allowSwapPIN, - from.allowSwapPIN2, - from.useActivation, - from.useCVC, - from.useNDEF, - from.useDynamicNDEF, - from.oneApdu, - from.useBlock, - from.allowSelectBlockchain, - from.forbidPurgeWallet, - from.protocolAllowUnencrypted, - from.protocolAllowStaticEncryption, - from.protectIssuerDataAgainstReplay, - from.forbidDefaultPIN, - from.disablePrecomputedNDEF, - from.skipSecurityDelayIfValidatedByIssuer, - from.skipCheckPIN2andCVCIfValidatedByIssuer, - from.skipSecurityDelayIfValidatedByLinkedTerminal, - from.restrictOverwriteIssuerDataEx, - from.requireTerminalTxSignature, - from.requireTerminalCertSignature, - from.checkPIN3onCard, - from.createWallet, - cardData, - ndefs - ) - } -} \ No newline at end of file diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/BaseAppWidget.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/BaseAppWidget.kt new file mode 100644 index 0000000000..60ab69c243 --- /dev/null +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/BaseAppWidget.kt @@ -0,0 +1,28 @@ +package com.tangem.tangemtest.ucase.variants.personalize.ui.widgets + +import android.view.ViewGroup +import com.tangem.tangemtest._arch.structure.StringId +import com.tangem.tangemtest._arch.structure.StringResId +import com.tangem.tangemtest._arch.structure.abstraction.Item +import com.tangem.tangemtest._arch.widget.abstraction.BaseViewWidget +import com.tangem.tangemtest._arch.widget.abstraction.ViewWidget +import com.tangem.tangemtest.ucase.resources.MainResourceHolder +import com.tangem.tangemtest.ucase.resources.Resources +import ru.dev.gbixahue.eu4d.lib.android._android.views.stringFrom + +/** +[REDACTED_AUTHOR] + */ +abstract class BaseAppWidget(parent: ViewGroup, item: Item): BaseViewWidget(parent, item) { + override fun getName(): String { + return when (val id = item.id) { + is StringId -> id.value + is StringResId -> view.stringFrom(id.value) + else -> view.stringFrom(getResNameId()) + } + } +} + +fun ViewWidget.getResNameId(): Int = MainResourceHolder.safeGet(item.id).resName + +fun ViewWidget.getResDescription(): Int? = MainResourceHolder.safeGet(item.id).resDescription \ No newline at end of file diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/DescriptionWidget.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/DescriptionWidget.kt index 9dfff32c48..0e49f54ec5 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/DescriptionWidget.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/DescriptionWidget.kt @@ -5,29 +5,27 @@ import android.widget.TextView import androidx.transition.AutoTransition import androidx.transition.TransitionManager import com.tangem.tangemtest.R -import com.tangem.tangemtest._arch.structure.abstraction.BaseItem -import com.tangem.tangemtest._arch.widget.abstraction.BaseWidget -import com.tangem.tangemtest._arch.widget.abstraction.getResDescription +import com.tangem.tangemtest._arch.structure.abstraction.Item import ru.dev.gbixahue.eu4d.lib.android.global.log.Log /** [REDACTED_AUTHOR] */ -abstract class DescriptionWidget( +abstract class DescriptionWidget( parent: ViewGroup, - data: BaseItem -) : BaseWidget(parent, data) { + item: Item +) : BaseAppWidget(parent, item) { private val descriptionContainer: ViewGroup by lazy { view.findViewById(R.id.container_description) } private val tvDescription: TextView? by lazy { descriptionContainer.findViewById(R.id.tv_description) } init { - Log.d(this, "init id: ${dataItem.id}") + Log.d(this, "init id: ${item.id}") initDescriptionWidget() } private fun initDescriptionWidget() { - dataItem.viewModel.viewState.onDescriptionVisibilityChanged = { changeDescriptionVisibility(it) } + item.viewModel.viewState.onDescriptionVisibilityChanged = { changeDescriptionVisibility(it) } } protected open fun changeDescriptionVisibility(state: Int) { diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/EditTextWidget.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/EditTextWidget.kt index 9cb1061414..df0951947d 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/EditTextWidget.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/EditTextWidget.kt @@ -7,14 +7,17 @@ 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.EditTextItem -import com.tangem.tangemtest._arch.widget.abstraction.getName import ru.dev.gbixahue.eu4d.lib.android._android.views.moveCursorToEnd import ru.dev.gbixahue.eu4d.lib.kotlin.stringOf /** [REDACTED_AUTHOR] */ -class EditTextWidget(parent: ViewGroup, data: EditTextItem) : DescriptionWidget(parent, data) { +class EditTextWidget( + parent: ViewGroup, + private val typedItem: EditTextItem +) : DescriptionWidget(parent, typedItem) { + override fun getLayoutId(): Int = R.layout.w_personalize_item_edit_text private val tilItem = view.findViewById(R.id.til_item) @@ -22,7 +25,7 @@ class EditTextWidget(parent: ViewGroup, data: EditTextItem) : DescriptionWidget< private val watcher = object : TextWatcher { override fun afterTextChanged(s: Editable?) { - dataItem.viewModel.updateDataByView(stringOf(s)) + typedItem.viewModel.updateDataByView(stringOf(s)) } override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {} @@ -31,9 +34,9 @@ class EditTextWidget(parent: ViewGroup, data: EditTextItem) : DescriptionWidget< init { tilItem.hint = getName() - etItem.setText(dataItem.getData()) + etItem.setText(typedItem.getTypedData()) etItem.addTextChangedListener(watcher) - dataItem.viewModel.onDataUpdated = { silentUpdate(it) } + item.viewModel.onDataUpdated = { silentUpdate(it as? String) } } private fun silentUpdate(value: String?) { diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/BlockHeadWidget.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/GroupTitleWidget.kt similarity index 72% rename from tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/BlockHeadWidget.kt rename to tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/GroupTitleWidget.kt index 4b0837723b..27167508ad 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/BlockHeadWidget.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/GroupTitleWidget.kt @@ -4,12 +4,11 @@ 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._arch.widget.abstraction.getName /** [REDACTED_AUTHOR] */ -class BlockHeadWidget(parent: ViewGroup, data: TextItem) : DescriptionWidget(parent, data) { +class GroupTitleWidget(parent: ViewGroup, data: TextItem) : DescriptionWidget(parent, data) { override fun getLayoutId(): Int = R.layout.w_personalize_item_text private val tvName = view.findViewById(R.id.tv_name) diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/NumberWidget.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/NumberWidget.kt index e2702465a4..61cab1f430 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/NumberWidget.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/NumberWidget.kt @@ -8,7 +8,6 @@ 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._arch.widget.abstraction.getName import com.tangem.tangemtest.ucase.variants.personalize.CardNumber import ru.dev.gbixahue.eu4d.lib.android._android.views.addInputFilter import ru.dev.gbixahue.eu4d.lib.android._android.views.moveCursorToEnd @@ -17,7 +16,11 @@ import ru.dev.gbixahue.eu4d.lib.kotlin.stringOf /** [REDACTED_AUTHOR] */ -class NumberWidget(parent: ViewGroup, data: NumberItem) : DescriptionWidget(parent, data) { +class NumberWidget( + parent: ViewGroup, + private val typedItem: NumberItem +) : DescriptionWidget(parent, typedItem) { + override fun getLayoutId(): Int = R.layout.w_personalize_item_number private val tilItem = view.findViewById(R.id.til_item) @@ -25,7 +28,7 @@ class NumberWidget(parent: ViewGroup, data: NumberItem) : DescriptionWidget, parent: ViewGroup): ViewWidget? { + override fun build(item: BaseItem, parent: ViewGroup): ViewWidget? { return when (item) { - is TextItem -> BlockHeadWidget(parent, item) + is TextItem -> GroupTitleWidget(parent, item) is EditTextItem -> EditTextWidget(parent, item) is NumberItem -> NumberWidget(parent, item) is BoolItem -> SwitchWidget(parent, item) - is ListItem -> SpinnerWidget(parent, item) + is SpinnerItem -> SpinnerWidget(parent, item) else -> null } } diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/SpinnerWidget.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/SpinnerWidget.kt index d4e2b2fb40..757634df6d 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/SpinnerWidget.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/SpinnerWidget.kt @@ -7,20 +7,23 @@ import android.widget.BaseAdapter import android.widget.Spinner import android.widget.TextView import com.tangem.tangemtest.R -import com.tangem.tangemtest._arch.structure.impl.KeyValue -import com.tangem.tangemtest._arch.structure.impl.ListItem -import com.tangem.tangemtest._arch.structure.impl.ListValueWrapper -import com.tangem.tangemtest._arch.widget.abstraction.getName +import com.tangem.tangemtest._arch.structure.abstraction.KeyValue +import com.tangem.tangemtest._arch.structure.abstraction.ListViewModel +import com.tangem.tangemtest._arch.structure.impl.SpinnerItem import ru.dev.gbixahue.eu4d.lib.android._android.views.inflate import ru.dev.gbixahue.eu4d.lib.kotlin.stringOf /** [REDACTED_AUTHOR] */ -class SpinnerWidget(parent: ViewGroup, listItem: ListItem) : DescriptionWidget(parent, listItem) { +class SpinnerWidget( + parent: ViewGroup, + private val typedItem: SpinnerItem +) : DescriptionWidget(parent, typedItem) { + override fun getLayoutId(): Int = R.layout.w_personalize_item_spinner - private val data: ListValueWrapper = listItem.getData()!! + private val data: ListViewModel = typedItem.getTypedData()!! private val spItem = view.findViewById(R.id.sp_item) private val spAdapter = SpItemAdapter(data.itemList) @@ -30,7 +33,7 @@ class SpinnerWidget(parent: ViewGroup, listItem: ListItem) : DescriptionWidget?, view: View?, position: Int, id: Long) { data.selectedItem = data.itemList[position].value - dataItem.viewModel.updateDataByView(data) + typedItem.viewModel.updateDataByView(data) } } @@ -42,15 +45,14 @@ class SpinnerWidget(parent: ViewGroup, listItem: ListItem) : DescriptionWidget item.value == selectedItem }?.let { - val position = itemList.indexOf(it) - spItem.setSelection(position) - } - spItem.onItemSelectedListener = onItemSelectedListener + typedItem.viewModel.onDataUpdated = { + val selectedItem = it as? String + spItem.onItemSelectedListener = null + data.itemList.firstOrNull { item -> item.value == selectedItem }?.let { keyValue -> + val position = data.itemList.indexOf(keyValue) + spItem.setSelection(position) } + spItem.onItemSelectedListener = onItemSelectedListener } } } diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/SwitchWidget.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/SwitchWidget.kt index 015ea477b2..ffa0b6879b 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/SwitchWidget.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/SwitchWidget.kt @@ -5,27 +5,30 @@ import android.widget.CompoundButton import androidx.appcompat.widget.SwitchCompat import com.tangem.tangemtest.R import com.tangem.tangemtest._arch.structure.impl.BoolItem -import com.tangem.tangemtest._arch.widget.abstraction.getName /** [REDACTED_AUTHOR] */ -class SwitchWidget(parent: ViewGroup, data: BoolItem) : DescriptionWidget(parent, data) { +class SwitchWidget( + parent: ViewGroup, + private val typedItem: BoolItem +) : DescriptionWidget(parent, typedItem) { + override fun getLayoutId(): Int = R.layout.w_personalize_item_switch private val switchItem = view.findViewById(R.id.sw_item) private val changeListener = CompoundButton.OnCheckedChangeListener { buttonView, isChecked -> - dataItem.viewModel.updateDataByView(isChecked) + typedItem.viewModel.updateDataByView(isChecked) } init { switchItem.text = getName() - switchItem.isChecked = dataItem.getData() ?: false + switchItem.isChecked = typedItem.getTypedData() ?: false switchItem.setOnCheckedChangeListener(changeListener) - dataItem.viewModel.onDataUpdated = { + typedItem.viewModel.onDataUpdated = { switchItem.setOnCheckedChangeListener(null) - switchItem.isChecked = it ?: false + switchItem.isChecked = it as? Boolean ?: false switchItem.setOnCheckedChangeListener(changeListener) } } diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/responses/ResponseViewModel.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/responses/ResponseViewModel.kt index ced5ffa144..50e8df7aa2 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/responses/ResponseViewModel.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/responses/ResponseViewModel.kt @@ -2,7 +2,6 @@ package com.tangem.tangemtest.ucase.variants.responses import android.view.View import androidx.lifecycle.ViewModel -import com.tangem.tangemtest._arch.structure.abstraction.BaseItem import com.tangem.tangemtest._arch.structure.abstraction.Item import com.tangem.tangemtest._arch.structure.abstraction.ModelToItems import com.tangem.tangemtest._arch.structure.abstraction.iterate @@ -30,8 +29,7 @@ class ResponseViewModel : ViewModel() { fun toggleDescriptionVisibility(state: Boolean) { itemList?.iterate { - val baseItem = it as? BaseItem<*> ?: return@iterate - baseItem.viewModel.viewState.descriptionVisibility = if (state) View.VISIBLE else View.GONE + it.viewModel.viewState.descriptionVisibility = if (state) View.VISIBLE else View.GONE } } } 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 492989ebe1..007cf07543 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 @@ -6,8 +6,9 @@ import com.tangem.commands.SettingsMask import com.tangem.tangemtest._arch.structure.Id import com.tangem.tangemtest._arch.structure.StringId import com.tangem.tangemtest._arch.structure.abstraction.Item -import com.tangem.tangemtest._arch.structure.abstraction.ListItemBlock +import com.tangem.tangemtest._arch.structure.abstraction.ItemGroup import com.tangem.tangemtest._arch.structure.abstraction.ModelToItems +import com.tangem.tangemtest._arch.structure.abstraction.SimpleItemGroup import com.tangem.tangemtest._arch.structure.impl.BoolItem import com.tangem.tangemtest._arch.structure.impl.TextItem import com.tangem.tangemtest.ucase.variants.personalize.BlockId @@ -31,55 +32,53 @@ class CardConverter : ModelToItems { } private fun simpleFields(from: Card): Item { - val block = createBlock(BlockId.Common) - block.addItem(TextItem(CardId.cardId, from.cardId)) - block.addItem(TextItem(CardId.manufacturerName, from.manufacturerName)) - block.addItem(TextItem(CardId.status, stringOf(from.status))) - block.addItem(TextItem(CardId.firmwareVersion, from.firmwareVersion)) - block.addItem(TextItem(CardId.cardPublicKey, stringOf(from.cardPublicKey))) - block.addItem(TextItem(CardId.issuerPublicKey, stringOf(from.issuerPublicKey))) - block.addItem(TextItem(CardId.curve, stringOf(from.curve))) - block.addItem(TextItem(CardId.maxSignatures, stringOf(from.maxSignatures))) - block.addItem(TextItem(CardId.signingMethod, stringOf(from.signingMethod))) - block.addItem(TextItem(CardId.pauseBeforePin2, stringOf(from.pauseBeforePin2))) - block.addItem(TextItem(CardId.walletPublicKey, stringOf(from.walletPublicKey))) - block.addItem(TextItem(CardId.walletRemainingSignatures, stringOf(from.walletRemainingSignatures))) - block.addItem(TextItem(CardId.walletSignedHashes, stringOf(from.walletSignedHashes))) - block.addItem(TextItem(CardId.health, stringOf(from.health))) - block.addItem(TextItem(CardId.isActivated, stringOf(from.isActivated))) - block.addItem(TextItem(CardId.activationSeed, stringOf(from.activationSeed))) - block.addItem(TextItem(CardId.paymentFlowVersion, stringOf(from.paymentFlowVersion))) - block.addItem(TextItem(CardId.userCounter, stringOf(from.userCounter))) + val group = createGroup(BlockId.Common) + 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.firmwareVersion, from.firmwareVersion)) + group.addItem(TextItem(CardId.cardPublicKey, stringOf(from.cardPublicKey))) + group.addItem(TextItem(CardId.issuerPublicKey, stringOf(from.issuerPublicKey))) + group.addItem(TextItem(CardId.curve, stringOf(from.curve))) + group.addItem(TextItem(CardId.maxSignatures, stringOf(from.maxSignatures))) + group.addItem(TextItem(CardId.signingMethod, stringOf(from.signingMethod))) + group.addItem(TextItem(CardId.pauseBeforePin2, stringOf(from.pauseBeforePin2))) + group.addItem(TextItem(CardId.walletPublicKey, stringOf(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 block + return group } private fun cardData(from: Card): Item { - val block = createBlock(BlockId.Common) - val data = from.cardData ?: return block - - val itemList = block.itemList - itemList.add(TextItem(CardDataId.batchId, data.batchId)) + val group = createGroup(BlockId.Common) + val data = from.cardData ?: return group + group.addItem(TextItem(CardDataId.batchId, data.batchId)) // Format: Year (2 bytes) | Month (1 byte) | Day (1 byte) - itemList.add(TextItem(CardDataId.manufactureDateTime, stringOf(data.manufactureDateTime))) - itemList.add(TextItem(CardDataId.issuerName, data.issuerName)) - itemList.add(TextItem(CardDataId.blockchainName, data.blockchainName)) - itemList.add(TextItem(CardDataId.manufacturerSignature, stringOf(data.manufacturerSignature))) - itemList.add(TextItem(CardDataId.productMask, stringOf(data.productMask))) - itemList.add(TextItem(CardDataId.tokenSymbol, data.tokenSymbol)) - itemList.add(TextItem(CardDataId.tokenContractAddress, data.tokenContractAddress)) - itemList.add(TextItem(CardDataId.tokenDecimal, data.tokenSymbol)) + group.addItem(TextItem(CardDataId.manufactureDateTime, stringOf(data.manufactureDateTime))) + group.addItem(TextItem(CardDataId.issuerName, data.issuerName)) + group.addItem(TextItem(CardDataId.blockchainName, data.blockchainName)) + group.addItem(TextItem(CardDataId.manufacturerSignature, stringOf(data.manufacturerSignature))) + group.addItem(TextItem(CardDataId.productMask, stringOf(data.productMask))) + group.addItem(TextItem(CardDataId.tokenSymbol, data.tokenSymbol)) + group.addItem(TextItem(CardDataId.tokenContractAddress, data.tokenContractAddress)) + group.addItem(TextItem(CardDataId.tokenDecimal, data.tokenSymbol)) - return block + return group } private fun settingsMask(from: SettingsMask?): Item { - val block = createBlock(CardId.settingsMask) - val data = from ?: return block + val group = createGroup(CardId.settingsMask) + val data = from ?: return group - Settings.values().forEach { block.addItem(BoolItem(StringId(it.name), data.contains(it))) } - return block + Settings.values().forEach { group.addItem(BoolItem(StringId(it.name), data.contains(it))) } + return group } - private fun createBlock(id: Id): ListItemBlock = ListItemBlock(id).apply { addItem(TextItem(id)) } + private fun createGroup(id: Id): ItemGroup = SimpleItemGroup(id).apply { addItem(TextItem(id)) } } \ No newline at end of file diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/responses/ui/widget/CheckBoxWidget.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/responses/ui/widget/CheckBoxWidget.kt index 5350ab0135..a988a93948 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/responses/ui/widget/CheckBoxWidget.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/responses/ui/widget/CheckBoxWidget.kt @@ -4,21 +4,24 @@ import android.view.ViewGroup import com.google.android.material.checkbox.MaterialCheckBox import com.tangem.tangemtest.R import com.tangem.tangemtest._arch.structure.impl.BoolItem -import com.tangem.tangemtest._arch.widget.abstraction.getName import com.tangem.tangemtest.ucase.variants.personalize.ui.widgets.DescriptionWidget import ru.dev.gbixahue.eu4d.lib.android._android.views.colorFrom /** [REDACTED_AUTHOR] */ -class CheckBoxWidget(parent: ViewGroup, private val boolItem: BoolItem) : DescriptionWidget(parent, boolItem) { +class CheckBoxWidget( + parent: ViewGroup, + private val typedItem: BoolItem +) : DescriptionWidget(parent, typedItem) { + override fun getLayoutId(): Int = R.layout.w_response_item_checkbox private val switchItem = view.findViewById(R.id.sw_item) init { switchItem.text = getName() - switchItem.isChecked = dataItem.getData() ?: false + switchItem.isChecked = typedItem.getData() ?: false switchItem.isEnabled = false switchItem.setTextColor(switchItem.colorFrom(R.color.action_name)) } 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 cde044dc2c..051974e674 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 @@ -11,7 +11,7 @@ import com.tangem.tangemtest._arch.widget.abstraction.ViewWidget [REDACTED_AUTHOR] */ class ResponseItemBuilder : ItemWidgetBuilder { - override fun build(item: BaseItem<*>, parent: ViewGroup): ViewWidget? { + override fun build(item: BaseItem, parent: ViewGroup): ViewWidget? { return when (item) { is TextItem -> ResponseTextWidget(parent, item) is BoolItem -> CheckBoxWidget(parent, item) 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 468b9963e7..a0d546f973 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 @@ -8,13 +8,16 @@ 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._arch.widget.abstraction.getName import com.tangem.tangemtest.ucase.variants.personalize.ui.widgets.DescriptionWidget /** [REDACTED_AUTHOR] */ -class ResponseTextWidget(parent: ViewGroup, private val textItem: TextItem) : DescriptionWidget(parent, textItem) { +class ResponseTextWidget( + parent: ViewGroup, + private val typedItem: TextItem +) : DescriptionWidget(parent, typedItem) { + override fun getLayoutId(): Int = R.layout.w_response_item private val tvName: TextView = view.findViewById(R.id.tv_name) @@ -25,7 +28,7 @@ class ResponseTextWidget(parent: ViewGroup, private val textItem: TextItem) : De } private fun initWidgets() { - val data = textItem.getData() + val data = typedItem.getData() as? String if (data == null || data.isEmpty()) { view.visibility = View.GONE return @@ -38,7 +41,7 @@ class ResponseTextWidget(parent: ViewGroup, private val textItem: TextItem) : De val clipboard = view.context.getSystemService(Context.CLIPBOARD_SERVICE) as? ClipboardManager ?: return@setOnClickListener - val clip: ClipData = ClipData.newPlainText("FieldValue", "${dataItem.getData()}") + val clip: ClipData = ClipData.newPlainText("FieldValue", "$data") clipboard.setPrimaryClip(clip) } } From acd023b2f540d9546af64efb6984bd7250bcc81b Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 9 Apr 2020 20:11:20 +0300 Subject: [PATCH 19/39] Updated on 2026-08-14 --- .../structure/abstraction/ItemViewModel.kt | 15 ++++--- .../tangemtest/_arch/structure/impl/Items.kt | 38 ++++++++++++------ .../_arch/widget/abstraction/ViewWidget.kt | 20 +++++++++- .../PersonalizationConfigConverter.kt | 2 +- .../personalize/ui/widgets/SpinnerWidget.kt | 32 +++++++-------- .../responses/converter/CardConverter.kt | 40 ++++++++++++++----- .../responses/ui/widget/ResponseTextWidget.kt | 6 --- tangem-demo/src/main/res/values/colors.xml | 4 ++ 8 files changed, 106 insertions(+), 51 deletions(-) diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/structure/abstraction/ItemViewModel.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/structure/abstraction/ItemViewModel.kt index d298f84f93..8c548ee17c 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/structure/abstraction/ItemViewModel.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/structure/abstraction/ItemViewModel.kt @@ -13,8 +13,10 @@ typealias SafeValueChange = (V) -> Unit class KeyValue(val key: String, val value: Any) -class ViewState { - var isHiddenField = false +class ViewState( + var isHidden: Boolean = false, + var backgroundColor: Int? = -1 +) { var descriptionVisibility: Int = 0x00000008 set(value) { @@ -34,9 +36,11 @@ interface ItemViewModel : PayloadHolder { fun updateDataByView(data: Any?) } -open class BaseItemViewModel(value: Any? = null) : ItemViewModel { +open class BaseItemViewModel( + value: Any? = null, + override val viewState: ViewState = ViewState() +) : ItemViewModel { - override val viewState: ViewState = ViewState() override val payload: Payload = mutableMapOf() // Don't update it directly from a View. Use for it updateDataByView() @@ -75,6 +79,7 @@ open class BaseItemViewModel(value: Any? = null) : ItemViewModel { } class ListViewModel( + val itemList: List, var selectedItem: Any?, - val itemList: List + override val viewState: ViewState = ViewState() ) : BaseItemViewModel(selectedItem) \ No newline at end of file diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/structure/impl/Items.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/structure/impl/Items.kt index 923f58f94a..e402650802 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/structure/impl/Items.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/structure/impl/Items.kt @@ -1,25 +1,39 @@ package com.tangem.tangemtest._arch.structure.impl import com.tangem.tangemtest._arch.structure.Id -import com.tangem.tangemtest._arch.structure.abstraction.BaseItem -import com.tangem.tangemtest._arch.structure.abstraction.BaseItemViewModel -import com.tangem.tangemtest._arch.structure.abstraction.KeyValue -import com.tangem.tangemtest._arch.structure.abstraction.ListViewModel +import com.tangem.tangemtest._arch.structure.abstraction.* /** [REDACTED_AUTHOR] */ -open class TypedItem(id: Id, value: D? = null) : BaseItem(id, BaseItemViewModel(value)) { +open class TypedItem(id: Id, viewModel: ItemViewModel) : BaseItem(id, viewModel) { open fun getTypedData(): D? = viewModel.data as? D } -class TextItem(id: Id, value: String? = null) : TypedItem(id, value) -class NumberItem(id: Id, value: Number? = null) : TypedItem(id, value) -class BoolItem(id: Id, value: Boolean? = null) : TypedItem(id, value) +class TextItem(id: Id, viewModel: ItemViewModel) : TypedItem(id, viewModel) { + constructor(id: Id, value: String? = null, viewState: ViewState = ViewState()) + : this(id, BaseItemViewModel(value, viewState)) +} -class EditTextItem(id: Id, value: String? = null) : TypedItem(id, value) +class NumberItem(id: Id, viewModel: ItemViewModel) : TypedItem(id, viewModel) { + constructor(id: Id, value: Number? = null, viewState: ViewState = ViewState()) + : this(id, BaseItemViewModel(value, viewState)) +} -class SpinnerItem(id: Id, value: List, selectedValue: Any?) - : TypedItem(id, ListViewModel(selectedValue, value) -) \ No newline at end of file +class BoolItem(id: Id, viewModel: ItemViewModel) : TypedItem(id, viewModel) { + constructor(id: Id, value: Boolean? = null, viewState: ViewState = ViewState()) + : this(id, BaseItemViewModel(value, viewState)) +} + + +class EditTextItem(id: Id, viewModel: ItemViewModel) : TypedItem(id, viewModel) { + constructor(id: Id, value: String? = null, viewState: ViewState = ViewState()) + : this(id, BaseItemViewModel(value, viewState)) +} + + +class SpinnerItem(id: Id, viewModel: ListViewModel) : TypedItem(id, viewModel) { + constructor(id: Id, list: List, selectedValue: Any?, viewState: ViewState = ViewState()) + : this(id, ListViewModel(list, selectedValue, viewState)) +} \ No newline at end of file diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/widget/abstraction/ViewWidget.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/widget/abstraction/ViewWidget.kt index 8a220c0f1a..9047cdacc1 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/widget/abstraction/ViewWidget.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/widget/abstraction/ViewWidget.kt @@ -1,12 +1,16 @@ package com.tangem.tangemtest._arch.widget.abstraction +import android.graphics.drawable.ColorDrawable +import android.graphics.drawable.Drawable import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import androidx.annotation.ColorRes import com.tangem.tangemtest.R import com.tangem.tangemtest._arch.structure.StringId import com.tangem.tangemtest._arch.structure.StringResId import com.tangem.tangemtest._arch.structure.abstraction.Item +import ru.dev.gbixahue.eu4d.lib.android._android.views.colorFrom import ru.dev.gbixahue.eu4d.lib.kotlin.common.LayoutHolder /** @@ -17,6 +21,7 @@ interface ViewWidget : LayoutHolder { var item: Item fun getName(): String + fun setBackgroundColor(@ColorRes colorId: Int?) } abstract class BaseViewWidget( @@ -25,10 +30,14 @@ abstract class BaseViewWidget( ) : ViewWidget { override val view: View = inflate(getLayoutId(), parent) + private var defaultBackground: Drawable = view.background + init { - if (item.viewModel.viewState.isHiddenField) { + if (item.viewModel.viewState.isHidden) { view.visibility = View.GONE + } else { + setBackgroundColor(item.viewModel.viewState.backgroundColor) } } @@ -39,6 +48,15 @@ abstract class BaseViewWidget( else -> view.resources.getString(R.string.unknown) } } + + override fun setBackgroundColor(colorId: Int?) { + val background = when { + colorId == null -> null + colorId == -1 -> defaultBackground + else -> ColorDrawable(view.colorFrom(colorId)) + } + view.background = background + } } internal fun inflate(id: Int, parent: ViewGroup): View { diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/converter/PersonalizationConfigConverter.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/converter/PersonalizationConfigConverter.kt index c350690412..cd0c912920 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/converter/PersonalizationConfigConverter.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/converter/PersonalizationConfigConverter.kt @@ -147,7 +147,7 @@ class PersonalizationConfigToItems : ModelToItems { blocList.add(pins()) blocList.iterate { if (itemTypes.hiddenList.contains(it.id)) { - it.viewModel.viewState.isHiddenField = true + it.viewModel.viewState.isHidden = true } } return blocList diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/SpinnerWidget.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/SpinnerWidget.kt index 757634df6d..d3e06d96cf 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/SpinnerWidget.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/SpinnerWidget.kt @@ -18,41 +18,41 @@ import ru.dev.gbixahue.eu4d.lib.kotlin.stringOf */ class SpinnerWidget( parent: ViewGroup, - private val typedItem: SpinnerItem + typedItem: SpinnerItem ) : DescriptionWidget(parent, typedItem) { override fun getLayoutId(): Int = R.layout.w_personalize_item_spinner - private val data: ListViewModel = typedItem.getTypedData()!! + private val viewModel: ListViewModel = typedItem.viewModel as ListViewModel - private val spItem = view.findViewById(R.id.sp_item) - private val spAdapter = SpItemAdapter(data.itemList) + private val spinner = view.findViewById(R.id.sp_item) + private val spAdapter = SpItemAdapter(viewModel.itemList) private val onItemSelectedListener = object : AdapterView.OnItemSelectedListener { override fun onNothingSelected(parent: AdapterView<*>?) {} override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) { - data.selectedItem = data.itemList[position].value - typedItem.viewModel.updateDataByView(data) + viewModel.selectedItem = viewModel.itemList[position].value + item.viewModel.updateDataByView(viewModel) } } init { val name = view.findViewById(R.id.tv_name) name.text = getName() - spItem.adapter = spAdapter - spAdapter.getItemPosition(stringOf(data.selectedItem))?.let { - spItem.setSelection(it) + spinner.adapter = spAdapter + spAdapter.getItemPosition(stringOf(viewModel.selectedItem))?.let { + spinner.setSelection(it) } - spItem.onItemSelectedListener = onItemSelectedListener - typedItem.viewModel.onDataUpdated = { + spinner.onItemSelectedListener = onItemSelectedListener + item.viewModel.onDataUpdated = { val selectedItem = it as? String - spItem.onItemSelectedListener = null - data.itemList.firstOrNull { item -> item.value == selectedItem }?.let { keyValue -> - val position = data.itemList.indexOf(keyValue) - spItem.setSelection(position) + spinner.onItemSelectedListener = null + viewModel.itemList.firstOrNull { item -> item.value == selectedItem }?.let { keyValue -> + val position = viewModel.itemList.indexOf(keyValue) + spinner.setSelection(position) } - spItem.onItemSelectedListener = onItemSelectedListener + spinner.onItemSelectedListener = onItemSelectedListener } } } 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 007cf07543..40db39e261 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,14 +1,13 @@ 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.tangemtest.R import com.tangem.tangemtest._arch.structure.Id import com.tangem.tangemtest._arch.structure.StringId -import com.tangem.tangemtest._arch.structure.abstraction.Item -import com.tangem.tangemtest._arch.structure.abstraction.ItemGroup -import com.tangem.tangemtest._arch.structure.abstraction.ModelToItems -import com.tangem.tangemtest._arch.structure.abstraction.SimpleItemGroup +import com.tangem.tangemtest._arch.structure.abstraction.* import com.tangem.tangemtest._arch.structure.impl.BoolItem import com.tangem.tangemtest._arch.structure.impl.TextItem import com.tangem.tangemtest.ucase.variants.personalize.BlockId @@ -26,11 +25,26 @@ class CardConverter : ModelToItems { // itemList.add(TextItem(Additional.JSON_INCOMING, holder.gson.toJson(from))) itemList.add(simpleFields(from)) - itemList.add(cardData(from)) + itemList.add(cardData(from.cardData)) itemList.add(settingsMask(from.settingsMask)) + hideEmptyNullFields(itemList) + return itemList } + 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 (isHidden) it.viewModel.viewState.isHidden = true + } + } + private fun simpleFields(from: Card): Item { val group = createGroup(BlockId.Common) group.addItem(TextItem(CardId.cardId, from.cardId)) @@ -55,9 +69,11 @@ class CardConverter : ModelToItems { return group } - private fun cardData(from: Card): Item { - val group = createGroup(BlockId.Common) - val data = from.cardData ?: return group + private fun cardData(from: CardData?): Item { + val group = createGroup(BlockId.Common, R.color.group_card_data) + val data = from ?: return group + +// group.addItem(TextItem(StringResId(R.string.response_card_card_data))) 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))) @@ -73,12 +89,16 @@ class CardConverter : ModelToItems { } private fun settingsMask(from: SettingsMask?): Item { - val group = createGroup(CardId.settingsMask) + val group = createGroup(CardId.settingsMask, R.color.group_signing_method) val data = from ?: return group +// group.addItem(TextItem(StringResId(R.string.response_card_settings_mask))) Settings.values().forEach { group.addItem(BoolItem(StringId(it.name), data.contains(it))) } return group } - private fun createGroup(id: Id): ItemGroup = SimpleItemGroup(id).apply { addItem(TextItem(id)) } + private fun createGroup(id: Id, colorId: Int? = null): ItemGroup { + return if (colorId == null) SimpleItemGroup(id) + else SimpleItemGroup(id, BaseItemViewModel(viewState = ViewState(backgroundColor = colorId))) + } } \ No newline at end of file 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 a0d546f973..9367b0bc0c 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 @@ -3,7 +3,6 @@ package com.tangem.tangemtest.ucase.variants.responses.ui.widget import android.content.ClipData import android.content.ClipboardManager import android.content.Context -import android.view.View import android.view.ViewGroup import android.widget.TextView import com.tangem.tangemtest.R @@ -29,11 +28,6 @@ class ResponseTextWidget( private fun initWidgets() { val data = typedItem.getData() as? String - if (data == null || data.isEmpty()) { - view.visibility = View.GONE - return - } - tvName.text = getName() tvValue.text = data diff --git a/tangem-demo/src/main/res/values/colors.xml b/tangem-demo/src/main/res/values/colors.xml index c72e37bcd8..1c183064f5 100644 --- a/tangem-demo/src/main/res/values/colors.xml +++ b/tangem-demo/src/main/res/values/colors.xml @@ -13,6 +13,10 @@ #303030 @color/field_info + #D7E4F3 + #D7F3E6 + + #C6C6C6 #FFE391 #FFFFFF From 635f38bb8699ffc795277616c00c7ed3d20ad8ca Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 9 Apr 2020 20:55:12 +0300 Subject: [PATCH 20/39] Updated on 2026-08-14 --- .../com/tangem/tangemtest/Old_MainActivity.kt | 1 - .../_arch/widget/abstraction/ViewWidget.kt | 2 +- .../tangem/tangemtest/_main/MainActivity.kt | 10 +++--- .../tangem/tangemtest/_main/MainViewModel.kt | 5 +-- .../tangemtest/ucase/ui/BaseFragment.kt | 2 -- .../personalize/ui/widgets/BaseAppWidget.kt | 2 +- .../variants/responses/ResponseViewModel.kt | 19 ++++++++++- .../variants/responses/ui/ResponseFragment.kt | 32 ++++++++++--------- .../variants/scan/ui/ScanActionFragment.kt | 7 +--- tangem-demo/src/main/res/values/strings.xml | 5 ++- 10 files changed, 50 insertions(+), 35 deletions(-) diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/Old_MainActivity.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/Old_MainActivity.kt index b39081b6b0..f2c89bc9b4 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/Old_MainActivity.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/Old_MainActivity.kt @@ -5,7 +5,6 @@ import android.os.Bundle import androidx.appcompat.app.AppCompatActivity import com.tangem.CardManager import com.tangem.tangem_sdk_new.extensions.init -import com.tangem.tangemtest.extensions.create import com.tangem.tasks.ScanEvent import com.tangem.tasks.TaskError import com.tangem.tasks.TaskEvent diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/widget/abstraction/ViewWidget.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/widget/abstraction/ViewWidget.kt index 9047cdacc1..20fcb7f37f 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/widget/abstraction/ViewWidget.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/widget/abstraction/ViewWidget.kt @@ -30,8 +30,8 @@ abstract class BaseViewWidget( ) : ViewWidget { override val view: View = inflate(getLayoutId(), parent) - private var defaultBackground: Drawable = view.background + private var defaultBackground: Drawable? = view.background init { if (item.viewModel.viewState.isHidden) { diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/_main/MainActivity.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/_main/MainActivity.kt index 4e774f0be7..92e093d2c8 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/_main/MainActivity.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/_main/MainActivity.kt @@ -2,7 +2,6 @@ package com.tangem.tangemtest._main import android.content.res.Resources import android.os.Bundle -import android.util.Log import android.view.Menu import androidx.activity.viewModels import androidx.appcompat.app.AppCompatActivity @@ -12,8 +11,9 @@ import androidx.navigation.findNavController import androidx.navigation.fragment.NavHostFragment import androidx.navigation.ui.AppBarConfiguration import androidx.navigation.ui.navigateUp -import androidx.navigation.ui.setupActionBarWithNavController +import androidx.navigation.ui.setupWithNavController import com.tangem.tangemtest.R +import ru.dev.gbixahue.eu4d.lib.android.global.log.Log /** [REDACTED_AUTHOR] @@ -34,8 +34,8 @@ class MainActivity : AppCompatActivity() { .findFragmentById(R.id.nav_host_fragment) as NavHostFragment? ?: return val navController = host.navController - appBarConfiguration = AppBarConfiguration(setOf(R.id.nav_entry_point)) - setupActionBarWithNavController(navController, appBarConfiguration) + appBarConfiguration = AppBarConfiguration(navController.graph) + toolbar.setupWithNavController(navController, appBarConfiguration) navController.addOnDestinationChangedListener { _, destination, _ -> val dest: String = try { @@ -43,7 +43,7 @@ class MainActivity : AppCompatActivity() { } catch (e: Resources.NotFoundException) { destination.id.toString() } - Log.d("NavigationActivity", "Navigated to $dest") + Log.d(this, "Navigated to $dest") } } diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/_main/MainViewModel.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/_main/MainViewModel.kt index a7d2eef283..a599bcb479 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/_main/MainViewModel.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/_main/MainViewModel.kt @@ -10,7 +10,8 @@ import ru.dev.gbixahue.eu4d.lib.android.global.log.Log */ class MainViewModel : ViewModel() { val ldDescriptionSwitch = MutableLiveData(false) - val ldResponseEvent = MutableLiveData>() + + var responseEvent: TaskEvent<*>? = null fun switchToggled(state: Boolean) { ldDescriptionSwitch.postValue(state) @@ -20,6 +21,6 @@ class MainViewModel : ViewModel() { Log.d(this, "changeResponseEvent") val reqEvent = event ?: return - ldResponseEvent.value = reqEvent + responseEvent = reqEvent } } \ No newline at end of file diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/BaseFragment.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/BaseFragment.kt index a7fabc3df0..29dfeec6be 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/BaseFragment.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/BaseFragment.kt @@ -10,7 +10,6 @@ import androidx.navigation.fragment.NavHostFragment.findNavController import com.google.android.material.snackbar.Snackbar import com.tangem.tangemtest.ucase.getDefaultNavigationOptions import com.tangem.tangemtest.ucase.tunnel.SnackbarHolder -import ru.dev.gbixahue.eu4d.lib.android.global.log.Log import ru.dev.gbixahue.eu4d.lib.kotlin.common.LayoutHolder /** @@ -26,7 +25,6 @@ abstract class BaseFragment : Fragment(), LayoutHolder, SnackbarHolder { } protected fun navigateTo(navigationId: Int, bundle: Bundle? = null, options: NavOptions? = getDefaultNavigationOptions()) { - Log.d(this, "navigateTo id: $navigationId") findNavController(this).navigate(navigationId, bundle, options) } diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/BaseAppWidget.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/BaseAppWidget.kt index 60ab69c243..65f110c8c2 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/BaseAppWidget.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/BaseAppWidget.kt @@ -13,7 +13,7 @@ import ru.dev.gbixahue.eu4d.lib.android._android.views.stringFrom /** [REDACTED_AUTHOR] */ -abstract class BaseAppWidget(parent: ViewGroup, item: Item): BaseViewWidget(parent, item) { +abstract class BaseAppWidget(parent: ViewGroup, item: Item) : BaseViewWidget(parent, item) { override fun getName(): String { return when (val id = item.id) { is StringId -> id.value diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/responses/ResponseViewModel.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/responses/ResponseViewModel.kt index 50e8df7aa2..6216ffbddc 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/responses/ResponseViewModel.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/responses/ResponseViewModel.kt @@ -2,10 +2,15 @@ package com.tangem.tangemtest.ucase.variants.responses import android.view.View import androidx.lifecycle.ViewModel +import com.tangem.commands.Card +import com.tangem.commands.SignResponse +import com.tangem.commands.personalization.DepersonalizeResponse +import com.tangem.tangemtest.R import com.tangem.tangemtest._arch.structure.abstraction.Item import com.tangem.tangemtest._arch.structure.abstraction.ModelToItems import com.tangem.tangemtest._arch.structure.abstraction.iterate import com.tangem.tangemtest.ucase.variants.responses.converter.ConvertersStore +import com.tangem.tasks.ScanEvent import com.tangem.tasks.TaskEvent import ru.dev.gbixahue.eu4d.lib.android.global.log.Log @@ -17,7 +22,7 @@ class ResponseViewModel : ViewModel() { private val convertersHolder = ConvertersStore() private var itemList: List? = null - fun createItemList(taskEvent: TaskEvent<*>): List { + fun createItemList(taskEvent: TaskEvent<*>?): List { Log.d(this, "createItemList: itemList size: ${itemList?.size ?: 0}") val event = taskEvent as? TaskEvent.Event ?: return emptyList() @@ -32,6 +37,18 @@ class ResponseViewModel : ViewModel() { it.viewModel.viewState.descriptionVisibility = if (state) View.VISIBLE else View.GONE } } + + fun determineTitleId(taskEvent: TaskEvent<*>?): Int { + val event = taskEvent as? TaskEvent.Event ?: return R.string.unknown + + return when (event.data) { + is ScanEvent.OnReadEvent -> R.string.fg_name_response_scan + is SignResponse -> R.string.fg_name_response_sign + is Card -> R.string.fg_name_response_personalization + is DepersonalizeResponse -> R.string.fg_name_response_depersonalization + else -> R.string.unknown + } + } } diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/responses/ui/ResponseFragment.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/responses/ui/ResponseFragment.kt index e3009a8787..683ebd7e86 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/responses/ui/ResponseFragment.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/responses/ui/ResponseFragment.kt @@ -1,6 +1,7 @@ package com.tangem.tangemtest.ucase.variants.responses.ui import android.os.Bundle +import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.LinearLayout @@ -13,17 +14,12 @@ import com.tangem.tangemtest._main.MainViewModel import com.tangem.tangemtest.ucase.ui.BaseFragment import com.tangem.tangemtest.ucase.variants.responses.ResponseViewModel import com.tangem.tangemtest.ucase.variants.responses.ui.widget.ResponseItemBuilder -import ru.dev.gbixahue.eu4d.lib.android.global.log.Log /** [REDACTED_AUTHOR] */ open class ResponseFragment : BaseFragment() { - companion object Arg { - val response = "response" - } - private val mainActivityVM: MainViewModel by activityViewModels() private val selfVM: ResponseViewModel by viewModels() @@ -31,22 +27,28 @@ open class ResponseFragment : BaseFragment() { override fun getLayoutId(): Int = R.layout.fg_card_response + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + setTittle() + return super.onCreateView(inflater, container, savedInstanceState) + } + + private fun setTittle() { + val titleId = selfVM.determineTitleId(mainActivityVM.responseEvent) + activity?.setTitle(titleId) + } + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - mainActivityVM.ldResponseEvent.observe(viewLifecycleOwner, Observer { event -> - val builder = WidgetBuilder(ResponseItemBuilder()) - val itemList = selfVM.createItemList(event) - itemList.forEach { builder.build(it, itemContainer) } - }) - subscribeToViewModelChanges() - } - - private fun subscribeToViewModelChanges() { - Log.d(this, "subscribeToViewModelChanges") + buildWidgets() listenDescriptionSwitchChanges() } + private fun buildWidgets() { + val builder = WidgetBuilder(ResponseItemBuilder()) + val itemList = selfVM.createItemList(mainActivityVM.responseEvent) + itemList.forEach { builder.build(it, itemContainer) } + } private fun listenDescriptionSwitchChanges() { mainActivityVM.ldDescriptionSwitch.observe(viewLifecycleOwner, Observer { diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/scan/ui/ScanActionFragment.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/scan/ui/ScanActionFragment.kt index 3ec710a4b7..1e5ed5eb9b 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/scan/ui/ScanActionFragment.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/scan/ui/ScanActionFragment.kt @@ -2,13 +2,11 @@ package com.tangem.tangemtest.ucase.variants.scan.ui import android.os.Bundle import android.view.View -import androidx.core.os.bundleOf import androidx.lifecycle.Observer import com.tangem.tangemtest.R import com.tangem.tangemtest.ucase.domain.paramsManager.ItemsManager import com.tangem.tangemtest.ucase.domain.paramsManager.managers.ScanItemsManager import com.tangem.tangemtest.ucase.ui.BaseCardActionFragment -import com.tangem.tangemtest.ucase.variants.responses.ui.ResponseFragment /** [REDACTED_AUTHOR] @@ -26,10 +24,7 @@ class ScanActionFragment : BaseCardActionFragment() { override fun listenReadResponse() { actionVM.seReadResponse.observe(viewLifecycleOwner, Observer { - navigateTo( - R.id.action_nav_card_action_to_response_screen, - bundleOf(Pair(ResponseFragment.response, it)) - ) + navigateTo(R.id.action_nav_card_action_to_response_screen) }) } } \ No newline at end of file diff --git a/tangem-demo/src/main/res/values/strings.xml b/tangem-demo/src/main/res/values/strings.xml index df8e87a708..cb44b6cc03 100644 --- a/tangem-demo/src/main/res/values/strings.xml +++ b/tangem-demo/src/main/res/values/strings.xml @@ -2,7 +2,10 @@ Tangem Development Kit @string/app_name - "Current card ID: " + Scan response + Sign response + Personalization response + Depersonalization response unknown Docs From c72faf1c3544cf05a3df302cc4d78f681dd68238 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 9 Apr 2020 22:03:49 +0300 Subject: [PATCH 21/39] Updated on 2026-08-14 --- .../extensions/AndroidComponents.kt | 22 +++++++++++++ .../variants/responses/ui/ResponseFragment.kt | 27 ++++++++++++---- .../responses/ui/widget/CheckBoxWidget.kt | 5 ++- .../responses/ui/widget/ResponseTextWidget.kt | 14 +------- .../responses/ui/widget/ResponseWidget.kt | 32 +++++++++++++++++++ .../main/res/drawable/ic_share_white_18dp.xml | 9 ++++++ tangem-demo/src/main/res/menu/main_menu.xml | 8 +++++ .../src/main/res/navigation/navigation.xml | 2 +- tangem-demo/src/main/res/values/strings.xml | 1 + 9 files changed, 97 insertions(+), 23 deletions(-) create mode 100644 tangem-demo/src/main/java/com/tangem/tangemtest/extensions/AndroidComponents.kt create mode 100644 tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/responses/ui/widget/ResponseWidget.kt create mode 100644 tangem-demo/src/main/res/drawable/ic_share_white_18dp.xml diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/extensions/AndroidComponents.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/extensions/AndroidComponents.kt new file mode 100644 index 0000000000..3a9205574c --- /dev/null +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/extensions/AndroidComponents.kt @@ -0,0 +1,22 @@ +package com.tangem.tangemtest.extensions + +import android.content.Intent +import androidx.activity.ComponentActivity +import androidx.fragment.app.Fragment + +/** +[REDACTED_AUTHOR] + */ +fun Fragment.shareText(text: String) { + requireActivity().shareText(text) +} + +fun ComponentActivity.shareText(text: String) { + val sendIntent: Intent = Intent().apply { + action = Intent.ACTION_SEND + putExtra(Intent.EXTRA_TEXT, text) + type = "text/plain" + } + val shareIntent = Intent.createChooser(sendIntent, null) + startActivity(shareIntent) +} \ No newline at end of file diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/responses/ui/ResponseFragment.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/responses/ui/ResponseFragment.kt index 683ebd7e86..e5cc169f68 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/responses/ui/ResponseFragment.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/responses/ui/ResponseFragment.kt @@ -1,16 +1,16 @@ package com.tangem.tangemtest.ucase.variants.responses.ui import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup +import android.view.* import android.widget.LinearLayout import androidx.fragment.app.activityViewModels import androidx.fragment.app.viewModels import androidx.lifecycle.Observer +import com.google.gson.Gson import com.tangem.tangemtest.R import com.tangem.tangemtest._arch.widget.WidgetBuilder import com.tangem.tangemtest._main.MainViewModel +import com.tangem.tangemtest.extensions.shareText import com.tangem.tangemtest.ucase.ui.BaseFragment import com.tangem.tangemtest.ucase.variants.responses.ResponseViewModel import com.tangem.tangemtest.ucase.variants.responses.ui.widget.ResponseItemBuilder @@ -27,9 +27,9 @@ open class ResponseFragment : BaseFragment() { override fun getLayoutId(): Int = R.layout.fg_card_response - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) setTittle() - return super.onCreateView(inflater, container, savedInstanceState) } private fun setTittle() { @@ -40,6 +40,7 @@ open class ResponseFragment : BaseFragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + setHasOptionsMenu(true) buildWidgets() listenDescriptionSwitchChanges() } @@ -55,4 +56,18 @@ open class ResponseFragment : BaseFragment() { selfVM.toggleDescriptionVisibility(it) }) } -} + + override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { + super.onCreateOptionsMenu(menu, inflater) + + val menuItem = menu.findItem(R.id.action_share) + menuItem.isVisible = true + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + when (item.itemId) { + R.id.action_share -> shareText(Gson().toJson(mainActivityVM.responseEvent)) + } + return super.onOptionsItemSelected(item) + } +} \ No newline at end of file diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/responses/ui/widget/CheckBoxWidget.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/responses/ui/widget/CheckBoxWidget.kt index a988a93948..65dd1a5831 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/responses/ui/widget/CheckBoxWidget.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/responses/ui/widget/CheckBoxWidget.kt @@ -4,7 +4,6 @@ import android.view.ViewGroup import com.google.android.material.checkbox.MaterialCheckBox import com.tangem.tangemtest.R import com.tangem.tangemtest._arch.structure.impl.BoolItem -import com.tangem.tangemtest.ucase.variants.personalize.ui.widgets.DescriptionWidget import ru.dev.gbixahue.eu4d.lib.android._android.views.colorFrom /** @@ -13,7 +12,7 @@ import ru.dev.gbixahue.eu4d.lib.android._android.views.colorFrom class CheckBoxWidget( parent: ViewGroup, private val typedItem: BoolItem -) : DescriptionWidget(parent, typedItem) { +) : ResponseWidget(parent, typedItem) { override fun getLayoutId(): Int = R.layout.w_response_item_checkbox @@ -22,7 +21,7 @@ class CheckBoxWidget( init { switchItem.text = getName() switchItem.isChecked = typedItem.getData() ?: false - switchItem.isEnabled = false + switchItem.isClickable = false switchItem.setTextColor(switchItem.colorFrom(R.color.action_name)) } } \ No newline at end of file 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 9367b0bc0c..8859d78527 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 @@ -1,13 +1,9 @@ package com.tangem.tangemtest.ucase.variants.responses.ui.widget -import android.content.ClipData -import android.content.ClipboardManager -import android.content.Context 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.personalize.ui.widgets.DescriptionWidget /** [REDACTED_AUTHOR] @@ -15,7 +11,7 @@ import com.tangem.tangemtest.ucase.variants.personalize.ui.widgets.DescriptionWi class ResponseTextWidget( parent: ViewGroup, private val typedItem: TextItem -) : DescriptionWidget(parent, typedItem) { +) : ResponseWidget(parent, typedItem) { override fun getLayoutId(): Int = R.layout.w_response_item @@ -30,13 +26,5 @@ class ResponseTextWidget( val data = typedItem.getData() as? String tvName.text = getName() tvValue.text = data - - view.setOnClickListener { - val clipboard = view.context.getSystemService(Context.CLIPBOARD_SERVICE) as? ClipboardManager - ?: return@setOnClickListener - - val clip: ClipData = ClipData.newPlainText("FieldValue", "$data") - clipboard.setPrimaryClip(clip) - } } } \ No newline at end of file diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/responses/ui/widget/ResponseWidget.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/responses/ui/widget/ResponseWidget.kt new file mode 100644 index 0000000000..d08de11386 --- /dev/null +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/responses/ui/widget/ResponseWidget.kt @@ -0,0 +1,32 @@ +package com.tangem.tangemtest.ucase.variants.responses.ui.widget + +import android.content.ClipData +import android.content.ClipboardManager +import android.content.Context +import android.view.ViewGroup +import com.tangem.tangemtest.R +import com.tangem.tangemtest._arch.structure.abstraction.Item +import com.tangem.tangemtest.ucase.variants.personalize.ui.widgets.DescriptionWidget +import ru.dev.gbixahue.eu4d.lib.android._android.views.stringFrom +import ru.dev.gbixahue.eu4d.lib.android._android.views.toast +import ru.dev.gbixahue.eu4d.lib.kotlin.stringOf + +/** +[REDACTED_AUTHOR] + */ +abstract class ResponseWidget(parent: ViewGroup, item: Item): DescriptionWidget(parent, item) { + + init { + view.setOnClickListener { + val data = stringOf(item.getData()) + val clipboard = view.context.getSystemService(Context.CLIPBOARD_SERVICE) as? ClipboardManager + ?: return@setOnClickListener + + val clip: ClipData = ClipData.newPlainText("FieldValue", "$data") + clipboard.setPrimaryClip(clip) + + val copyMessage = view.stringFrom(R.string.copy_to_clipboard) + view.toast("$copyMessage: ${getName()} - $data") + } + } +} \ No newline at end of file diff --git a/tangem-demo/src/main/res/drawable/ic_share_white_18dp.xml b/tangem-demo/src/main/res/drawable/ic_share_white_18dp.xml new file mode 100644 index 0000000000..0075e4b78c --- /dev/null +++ b/tangem-demo/src/main/res/drawable/ic_share_white_18dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/tangem-demo/src/main/res/menu/main_menu.xml b/tangem-demo/src/main/res/menu/main_menu.xml index 6241fdec1d..7f4be83642 100644 --- a/tangem-demo/src/main/res/menu/main_menu.xml +++ b/tangem-demo/src/main/res/menu/main_menu.xml @@ -2,9 +2,17 @@ + + + \ No newline at end of file diff --git a/tangem-demo/src/main/res/navigation/navigation.xml b/tangem-demo/src/main/res/navigation/navigation.xml index 1cc5427895..7209eb2e22 100644 --- a/tangem-demo/src/main/res/navigation/navigation.xml +++ b/tangem-demo/src/main/res/navigation/navigation.xml @@ -128,7 +128,7 @@ diff --git a/tangem-demo/src/main/res/values/strings.xml b/tangem-demo/src/main/res/values/strings.xml index cb44b6cc03..064c3a64c8 100644 --- a/tangem-demo/src/main/res/values/strings.xml +++ b/tangem-demo/src/main/res/values/strings.xml @@ -8,6 +8,7 @@ Depersonalization response unknown + Copy to clipboard Docs Your card hasn\'t been personalized yet. You need to run personalize command first. From 8c8d223c727325f707c57e13ed4ea43321013195 Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 10 Apr 2020 12:09:01 +0300 Subject: [PATCH 22/39] Updated on 2026-08-14 --- .../tangem/{common => }/CardEnvironment.kt | 11 +- .../src/main/java/com/tangem/CardManager.kt | 330 --------------- .../src/main/java/com/tangem/CardSession.kt | 221 ++++++++++ ...Delegate.kt => CardSessionViewDelegate.kt} | 16 +- .../src/main/java/com/tangem/Config.kt | 22 + .../src/main/java/com/tangem/SessionError.kt | 139 +++++++ .../src/main/java/com/tangem/TangemSdk.kt | 383 ++++++++++++++++++ .../com/tangem/commands/CheckWalletCommand.kt | 65 ++- .../main/java/com/tangem/commands/Command.kt | 117 ++++++ .../com/tangem/commands/CommandSerializer.kt | 44 -- .../tangem/commands/CreateWalletCommand.kt | 41 +- .../com/tangem/commands/OpenSessionCommand.kt | 31 +- .../com/tangem/commands/PurgeWalletCommand.kt | 35 +- .../java/com/tangem/commands/ReadCommand.kt | 103 ++--- .../tangem/commands/ReadIssuerDataCommand.kt | 76 +++- .../commands/ReadIssuerExtraDataCommand.kt | 125 ++++-- .../tangem/commands/ReadUserDataCommand.kt | 39 +- .../java/com/tangem/commands/SignCommand.kt | 68 ++-- .../tangem/commands/WriteIssuerDataCommand.kt | 73 +++- .../commands/WriteIssuerExtraDataCommand.kt | 111 ++++- .../tangem/commands/WriteUserDataCommand.kt | 64 ++- .../personalization/DepersonalizeCommand.kt | 11 +- .../personalization/PersonalizeCommand.kt | 92 ++--- .../personalization/entities/Acquirer.kt | 2 +- .../personalization/entities/Issuer.kt | 2 +- .../personalization/entities/Manufacturer.kt | 2 +- .../com/tangem/common/CompletionResult.kt | 4 +- .../com/tangem/common/TerminalKeysService.kt | 6 +- .../com/tangem/common/apdu/CommandApdu.kt | 2 +- .../java/com/tangem/common/apdu/StatusWord.kt | 17 +- .../tlv/{TlvMapper.kt => TlvDecoder.kt} | 28 +- .../java/com/tangem/common/tlv/TlvEncoder.kt | 7 +- .../java/com/tangem/tasks/CreateWalletTask.kt | 43 ++ .../com/tangem/tasks/ReadIssuerDataTask.kt | 53 --- .../tangem/tasks/ReadIssuerExtraDataTask.kt | 100 ----- .../main/java/com/tangem/tasks/ScanTask.kt | 72 +--- .../com/tangem/tasks/SingleCommandTask.kt | 39 -- .../src/main/java/com/tangem/tasks/Task.kt | 358 ---------------- .../com/tangem/tasks/WriteIssuerDataTask.kt | 67 --- .../tangem/tasks/WriteIssuerExtraDataTask.kt | 110 ----- .../{TlvMapperTest.kt => TlvDecoderTest.kt} | 70 ++-- ...e.kt => DefaultCardSessionViewDelegate.kt} | 28 +- .../tangem_sdk_new/TerminalKeysStorage.kt | 4 +- .../{CardManager.kt => TangemSdk.kt} | 10 +- .../tangem/tangem_sdk_new/nfc/NfcReader.kt | 10 +- .../tangem_sdk_new/nfc/SlixTagReader.kt | 6 +- 46 files changed, 1657 insertions(+), 1600 deletions(-) rename tangem-core/src/main/java/com/tangem/{common => }/CardEnvironment.kt (76%) delete mode 100644 tangem-core/src/main/java/com/tangem/CardManager.kt create mode 100644 tangem-core/src/main/java/com/tangem/CardSession.kt rename tangem-core/src/main/java/com/tangem/{CardManagerDelegate.kt => CardSessionViewDelegate.kt} (78%) create mode 100644 tangem-core/src/main/java/com/tangem/SessionError.kt create mode 100644 tangem-core/src/main/java/com/tangem/TangemSdk.kt create mode 100644 tangem-core/src/main/java/com/tangem/commands/Command.kt delete mode 100644 tangem-core/src/main/java/com/tangem/commands/CommandSerializer.kt rename tangem-core/src/main/java/com/tangem/common/tlv/{TlvMapper.kt => TlvDecoder.kt} (86%) create mode 100644 tangem-core/src/main/java/com/tangem/tasks/CreateWalletTask.kt delete mode 100644 tangem-core/src/main/java/com/tangem/tasks/ReadIssuerDataTask.kt delete mode 100644 tangem-core/src/main/java/com/tangem/tasks/ReadIssuerExtraDataTask.kt delete mode 100644 tangem-core/src/main/java/com/tangem/tasks/SingleCommandTask.kt delete mode 100644 tangem-core/src/main/java/com/tangem/tasks/Task.kt delete mode 100644 tangem-core/src/main/java/com/tangem/tasks/WriteIssuerDataTask.kt delete mode 100644 tangem-core/src/main/java/com/tangem/tasks/WriteIssuerExtraDataTask.kt rename tangem-core/src/test/java/com/tangem/common/tlv/{TlvMapperTest.kt => TlvDecoderTest.kt} (71%) rename tangem-sdk/src/main/java/com/tangem/tangem_sdk_new/{DefaultCardManagerDelegate.kt => DefaultCardSessionViewDelegate.kt} (87%) rename tangem-sdk/src/main/java/com/tangem/tangem_sdk_new/extensions/{CardManager.kt => TangemSdk.kt} (62%) diff --git a/tangem-core/src/main/java/com/tangem/common/CardEnvironment.kt b/tangem-core/src/main/java/com/tangem/CardEnvironment.kt similarity index 76% rename from tangem-core/src/main/java/com/tangem/common/CardEnvironment.kt rename to tangem-core/src/main/java/com/tangem/CardEnvironment.kt index 09cfd736b4..e4b2c36c55 100644 --- a/tangem-core/src/main/java/com/tangem/common/CardEnvironment.kt +++ b/tangem-core/src/main/java/com/tangem/CardEnvironment.kt @@ -1,5 +1,6 @@ -package com.tangem.common +package com.tangem +import com.tangem.commands.Card import com.tangem.commands.EllipticCurve import com.tangem.crypto.CryptoUtils.generatePublicKey @@ -7,11 +8,14 @@ import com.tangem.crypto.CryptoUtils.generatePublicKey /** * Contains data relating to a Tangem card. It is used in constructing all the commands, * and commands can return modified [CardEnvironment]. + * + * @property card Current card, read by preflight [com.tangem.commands.ReadCommand]. + * @property terminalKeys generated terminal keys used in Linked Terminal feature. */ data class CardEnvironment( val pin1: String = DEFAULT_PIN, val pin2: String = DEFAULT_PIN2, - val cardId: String? = null, + var card: Card? = null, val terminalKeys: KeyPair? = null, var encryptionMode: EncryptionMode = EncryptionMode.NONE, var encryptionKey: ByteArray? = null, @@ -24,6 +28,9 @@ data class CardEnvironment( } } +/** + * All possible encryption modes. + */ enum class EncryptionMode(val code: Byte) { NONE(0x0), FAST(0x1), diff --git a/tangem-core/src/main/java/com/tangem/CardManager.kt b/tangem-core/src/main/java/com/tangem/CardManager.kt deleted file mode 100644 index c5a1f99e77..0000000000 --- a/tangem-core/src/main/java/com/tangem/CardManager.kt +++ /dev/null @@ -1,330 +0,0 @@ -package com.tangem - -import com.tangem.commands.* -import com.tangem.commands.personalization.DepersonalizeCommand -import com.tangem.commands.personalization.DepersonalizeResponse -import com.tangem.commands.personalization.PersonalizeCommand -import com.tangem.commands.personalization.entities.Acquirer -import com.tangem.commands.personalization.entities.CardConfig -import com.tangem.commands.personalization.entities.Issuer -import com.tangem.commands.personalization.entities.Manufacturer -import com.tangem.common.CardEnvironment -import com.tangem.common.TerminalKeysService -import com.tangem.crypto.CryptoUtils -import com.tangem.tasks.* - -/** - * The main interface of Tangem SDK that allows your app to communicate with Tangem cards. - * - * @property reader is an interface that is responsible for NFC connection and - * transfer of data to and from the Tangem Card. - * Its default implementation, NfcCardReader, is in our tangem-sdk module. - * @property cardManagerDelegate An interface that allows interaction with users and shows relevant UI. - * Its default implementation, DefaultCardManagerDelegate, is in our tangem-sdk module. - */ -class CardManager( - private val reader: CardReader, - private val cardManagerDelegate: CardManagerDelegate? = null, - private val config: Config = Config() -) { - - private var terminalKeysService: TerminalKeysService? = null - private var isBusy = false - - init { - CryptoUtils.initCrypto() - } - - /** - * To start using any card, you first need to read it using the scanCard() method. - * This method launches an NFC session, and once it’s connected with the card, - * it obtains the card data. Optionally, if the card contains a wallet (private and public key pair), - * it proves that the wallet owns a private key that corresponds to a public one. - * - * It launches on the new thread a [ScanTask] that will send the following events in a callback: - * [ScanEvent.OnReadEvent] after completing [com.tangem.commands.ReadCommand] - * [ScanEvent.OnVerifyEvent] after completing [com.tangem.commands.CheckWalletCommand] - * [TaskEvent.Completion] with an error field null after successful completion of a task or - * [TaskEvent.Completion] with a [TaskError] if some error occurs. - * @param callback is triggered on events during a performance of the task, - * provides data in form of [ScanEvent] subclasses. - */ - fun scanCard(callback: (result: TaskEvent) -> Unit) { - val task = ScanTask() - runTask(task, callback = callback) - } - - /** - * This method allows you to sign one or multiple hashes. - * Simultaneous signing of array of hashes in a single [SignCommand] is required to support - * Bitcoin-type multi-input blockchains (UTXO). - * The [SignCommand] will return a corresponding array of signatures. - * - * This method launches on the new thread [SignCommand] that will send the following events in a callback: - * [SignResponse] after completing [SignCommand] - * [TaskEvent.Completion] with an error field null after successful completion of a task or - * [TaskEvent.Completion] with a [TaskError] if some error occurs. - * Please note that Tangem cards usually protect the signing with a security delay - * that may last up to 90 seconds, depending on a card. - * It is for [CardManagerDelegate] to notify users of security delay. - * @param hashes Array of transaction hashes. It can be from one or up to ten hashes of the same length. - * @param cardId CID, Unique Tangem card ID number - * @param callback is triggered on the completion of the [SignCommand], - * provides card response in the form of [SignResponse]. - */ - fun sign(hashes: Array, cardId: String, - callback: (result: TaskEvent) -> Unit) { - val signCommand = SignCommand(hashes) - val task = SingleCommandTask(signCommand) - runTask(task, cardId, callback) - } - - /** - * This command returns 512-byte Issuer Data field and its issuer’s signature. - * Issuer Data is never changed or parsed from within the Tangem COS. The issuer defines purpose of use, - * format and payload of Issuer Data. For example, this field may contain information about - * wallet balance signed by the issuer or additional issuer’s attestation data. - * @param cardId CID, Unique Tangem card ID number. - * @param callback is triggered on the completion of the [ReadIssuerDataCommand], - * provides card response in the form of [ReadIssuerDataResponse]. - */ - fun readIssuerData(cardId: String, - callback: (result: TaskEvent) -> Unit) { - val task = ReadIssuerDataTask(config.issuerPublicKey) - runTask(task, cardId, callback) - } - - /** - * This task retrieves Issuer Extra Data field and its issuer’s signature. - * Issuer Extra Data is never changed or parsed from within the Tangem COS. The issuer defines purpose of use, - * format and payload of Issuer Data. . For example, this field may contain photo or - * biometric information for ID card product. Because of the large size of Issuer_Extra_Data, - * a series of these commands have to be executed to read the entire Issuer_Extra_Data. - * @param cardId CID, Unique Tangem card ID number. - * @param callback is triggered on the completion of the [ReadIssuerExtraDataTask], - * provides card response in the form of [ReadIssuerExtraDataResponse]. - */ - fun readIssuerExtraData(cardId: String, - callback: (result: TaskEvent) -> Unit) { - val task = ReadIssuerExtraDataTask(config.issuerPublicKey) - runTask(task, cardId, callback) - } - - /** - * This command writes 512-byte Issuer Data field and its issuer’s signature. - * Issuer Data is never changed or parsed from within the Tangem COS. The issuer defines purpose of use, - * format and payload of Issuer Data. For example, this field may contain information about - * wallet balance signed by the issuer or additional issuer’s attestation data. - * @param cardId CID, Unique Tangem card ID number. - * @param issuerData Data provided by issuer. - * @param issuerDataSignature Issuer’s signature of [issuerData] with Issuer Data Private Key. - * @param issuerDataCounter An optional counter that protect issuer data against replay attack. - * @param callback is triggered on the completion of the [WriteIssuerDataCommand], - * provides card response in the form of [WriteIssuerDataResponse]. - */ - fun writeIssuerData(cardId: String, - issuerData: ByteArray, - issuerDataSignature: ByteArray, - issuerDataCounter: Int? = null, - callback: (result: TaskEvent) -> Unit) { - val task = WriteIssuerDataTask( - issuerData, - issuerDataSignature, - issuerDataCounter, - config.issuerPublicKey - ) - runTask(task, cardId, callback) - } - - /** - * This task writes Issuer Extra Data field and its issuer’s signature. - * Issuer Extra Data is never changed or parsed from within the Tangem COS. - * The issuer defines purpose of use, format and payload of Issuer Data. - * For example, this field may contain a photo or biometric information for ID card products. - * Because of the large size of Issuer_Extra_Data, a series of these commands have to be executed - * to write entire Issuer_Extra_Data. - * @param cardId CID, Unique Tangem card ID number. - * @param issuerData Data provided by issuer. - * @param startingSignature Issuer’s signature with Issuer Data Private Key of [cardId], - * [issuerDataCounter] (if flags Protect_Issuer_Data_Against_Replay and - * Restrict_Overwrite_Issuer_Extra_Data are set in [SettingsMask]) and size of [issuerData]. - * @param finalizingSignature Issuer’s signature with Issuer Data Private Key of [cardId], - * [issuerData] and [issuerDataCounter] (the latter one only if flags Protect_Issuer_Data_Against_Replay - * andRestrict_Overwrite_Issuer_Extra_Data are set in [SettingsMask]). - * @param issuerDataCounter An optional counter that protect issuer data against replay attack. - * @param callback is triggered on the completion of the [WriteIssuerDataCommand], - * provides card response in the form of [WriteIssuerDataResponse]. - */ - fun writeIssuerExtraData(cardId: String, - issuerData: ByteArray, - startingSignature: ByteArray, - finalizingSignature: ByteArray, - issuerDataCounter: Int? = null, - callback: (result: TaskEvent) -> Unit) { - val task = WriteIssuerExtraDataTask( - issuerData, - startingSignature, finalizingSignature, - config.issuerPublicKey, - issuerDataCounter - ) - runTask(task, cardId, callback) - } - - /** - * This command write some of User_Data, User_ProtectedData, User_Counter and User_ProtectedCounter fields. - * User_Data and User_ProtectedData are never changed or parsed by the executable code the Tangem COS. - * The App defines purpose of use, format and it's payload. For example, this field may contain cashed information - * from blockchain to accelerate preparing new transaction. - * User_Counter and User_ProtectedCounter are counters, that initial values can be set by App and increased on every signing - * of new transaction (on SIGN command that calculate new signatures). The App defines purpose of use. - * For example, this fields may contain blockchain nonce value. - * - * Writing of User_Counter and User_Data protected only by PIN1. - * User_ProtectedCounter and User_ProtectedData additionaly need PIN2 to confirmation. - */ - fun writeUserData( - cardId: String, - userData: ByteArray? = null, - userProtectedData: ByteArray? = null, - userCounter: Int? = null, - userProtectedCounter: Int? = null, - callback: (result: TaskEvent) -> Unit - ) { - val writeUserDataCommand = WriteUserDataCommand(userData, userProtectedData, userCounter, userProtectedCounter) - val task = SingleCommandTask(writeUserDataCommand) - runTask(task, cardId, callback) - } - - /** - * This command returns two up to 512-byte User_Data, User_Protected_Data and two counters User_Counter and - * User_Protected_Counter fields. - * User_Data and User_ProtectedData are never changed or parsed by the executable code the Tangem COS. - * The App defines purpose of use, format and it's payload. For example, this field may contain cashed information - * from blockchain to accelerate preparing new transaction. - * User_Counter and User_ProtectedCounter are counters, that initial values can be set by App and increased on every signing - * of new transaction (on SIGN command that calculate new signatures). The App defines purpose of use. - * For example, this fields may contain blockchain nonce value. - */ - fun readUserData(cardId: String, callback: (result: TaskEvent) -> Unit) { - val task = SingleCommandTask(ReadUserDataCommand()) - runTask(task, cardId, callback) - } - - /** - * This command will create a new wallet on the card having ‘Empty’ state. - * A key pair WalletPublicKey / WalletPrivateKey is generated and securely stored in the card. - * App will need to obtain Wallet_PublicKey from the response of [CreateWalletCommand] or [ReadCommand] - * and then transform it into an address of corresponding blockchain wallet - * according to a specific blockchain algorithm. - * WalletPrivateKey is never revealed by the card and will be used by [SignCommand] and [CheckWalletCommand]. - * RemainingSignature is set to MaxSignatures. - * @param cardId CID, Unique Tangem card ID number. - */ - fun createWallet(cardId: String, - callback: (result: TaskEvent) -> Unit) { - val createWalletCommand = CreateWalletCommand() - val task = SingleCommandTask(createWalletCommand) - runTask(task, cardId, callback) - } - - /** - * This command deletes all wallet data. If Is_Reusable flag is enabled during personalization, - - * If Is_Reusable flag is disabled, the card switches to ‘Purged’ state. - * ‘Purged’ state is final, it makes the card useless. - * @param cardId CID, Unique Tangem card ID number. - */ - fun purgeWallet(cardId: String, - callback: (result: TaskEvent) -> Unit) { - val purgeWalletCommand = PurgeWalletCommand() - val task = SingleCommandTask(purgeWalletCommand) - runTask(task, cardId, callback) - } - - /** - * Command available on SDK cards only - * - * This command resets card to initial state, - * erasing all data written during personalization and usage. - * @param cardId CID, Unique Tangem card ID number. - */ - fun depersonalize(cardId: String, - callback: (result: TaskEvent) -> Unit) { - val depersonalizeCommand = DepersonalizeCommand() - val task = SingleCommandTask(depersonalizeCommand) - runTask(task, cardId, callback) - } - - /** - * Command available on SDK cards only - * - * Personalization is an initialization procedure, required before starting using a card. - * During this procedure a card setting is set up. - * During this procedure all data exchange is encrypted. - * @param config is a configuration file with all the card settings that are written on the card - * during personalization. - * @param issuer Issuer is a third-party team or company wishing to use Tangem cards. - * @param manufacturer Tangem Card Manufacturer. - * @param acquirer Acquirer is a trusted third-party company that operates proprietary - * (non-EMV) POS terminal infrastructure and transaction processing back-end. - */ - fun personalize(config: CardConfig, - issuer: Issuer, manufacturer: Manufacturer, acquirer: Acquirer? = null, - callback: (result: TaskEvent) -> Unit) { - val personalizationCommand = PersonalizeCommand(config, issuer, manufacturer, acquirer) - val task = SingleCommandTask(personalizationCommand) - task.performPreflightRead = false - runTask(task, callback = callback) - } - - /** - - */ - fun runTask(task: Task, cardId: String? = null, callback: (result: TaskEvent) -> Unit) { - if (isBusy) { - callback(TaskEvent.Completion(TaskError.Busy())) - return - } - - val environment = prepareCardEnvironment(cardId) - isBusy = true - - task.reader = reader - task.delegate = cardManagerDelegate - - Thread().run { - task.run(environment) { taskEvent -> - if (taskEvent is TaskEvent.Completion) isBusy = false - callback(taskEvent) - } - } - } - - /** - - */ - fun runCommand(command: CommandSerializer, - cardId: String? = null, - callback: (result: TaskEvent) -> Unit) { - val task = SingleCommandTask(command) - runTask(task, cardId, callback) - } - - /** - * Allows to set a particular [TerminalKeysService] to retrieve terminal keys. - * Default implementation is provided in tangem-sdk module: [TerminalKeysStorage]. - */ - fun setTerminalKeysService(terminalKeysService: TerminalKeysService) { - this.terminalKeysService = terminalKeysService - } - - private fun prepareCardEnvironment(cardId: String?): CardEnvironment { - val terminalKeys = if (config.linkedTerminal) terminalKeysService?.getKeys() else null - return CardEnvironment( - cardId = cardId, - terminalKeys = terminalKeys - ) - } - - companion object -} \ No newline at end of file diff --git a/tangem-core/src/main/java/com/tangem/CardSession.kt b/tangem-core/src/main/java/com/tangem/CardSession.kt new file mode 100644 index 0000000000..f8455ace3e --- /dev/null +++ b/tangem-core/src/main/java/com/tangem/CardSession.kt @@ -0,0 +1,221 @@ +package com.tangem + +import com.tangem.commands.Card +import com.tangem.commands.CommandResponse +import com.tangem.commands.OpenSessionCommand +import com.tangem.commands.ReadCommand +import com.tangem.common.CompletionResult +import com.tangem.common.apdu.CommandApdu +import com.tangem.common.apdu.ResponseApdu +import com.tangem.common.extensions.calculateSha256 +import com.tangem.crypto.EncryptionHelper +import com.tangem.crypto.FastEncryptionHelper +import com.tangem.crypto.StrongEncryptionHelper +import com.tangem.crypto.pbkdf2Hash + +/** + * Basic interface for running tasks and [com.tangem.commands.Command] in a [CardSession] + */ +interface CardSessionRunnable { + + /** + * The starting point for custom business logic. + * Implement this interface and use [TangemSdk.startSession] to run. + * @param session run commands in this [CardSession]. + * @param callback trigger the callback to complete the task. + */ + fun run(session: CardSession, callback: (result: CompletionResult) -> Unit) +} + +/** + * Allows interaction with Tangem cards. Should be opened before sending commands. + * + * @property environment + * @property reader is an interface that is responsible for NFC connection and + * transfer of data to and from the Tangem Card. + * @property viewDelegate is an interface that allows interaction with users and shows relevant UI. + * @property cardId ID, Unique Tangem card ID number. If not null, the SDK will check that you the card + * with which you tapped a phone has this [cardId] and SDK will return + * the [SessionError.WrongCard] otherwise. + * @property initialMessage A custom description that will be shown at the beginning of the NFC session. + * If null, a default header and text body will be used. + */ +class CardSession( + val environment: CardEnvironment, + val reader: CardReader, + val viewDelegate: CardSessionViewDelegate, + private var cardId: String? = null, + private val initialMessage: Message? = null +) { + + /** + * True if some operation is still in progress. + */ + private var isBusy = false + + /** + * This metod starts a card session, performs preflight [ReadCommand], + * invokes [CardSessionRunnable.run] and closes the session. + * @param runnable [CardSessionRunnable] that will be performed in the session. + * @param callback will be triggered with a [CompletionResult] of a session. + */ + fun , R : CommandResponse> startWithRunnable( + runnable: T, callback: (result: CompletionResult) -> Unit) { + + start { session, error -> + if (error != null) { + callback(CompletionResult.Failure(error)) + return@start + } + if (runnable is ReadCommand) { + callback(CompletionResult.Success(environment.card as R)) + return@start + } + + runnable.run(this) { result -> + stop() + callback(result) + } + } + } + + /** + * Starts a card session and performs preflight [ReadCommand]. + * @param callback: callback with the card session. Can contain [SessionError] if something goes wrong. + */ + fun start(callback: (session: CardSession, error: SessionError?) -> Unit) { + try { + startSession() + } catch (error: SessionError) { + callback(this, error) + } + + preflightRead() { result -> + when (result) { + is CompletionResult.Failure -> { + callback(this, result.error) + stopWithError(result.error) + } + is CompletionResult.Success -> { + callback(this, null) + + } + } + } + } + + private fun startSession() { + if (isBusy) throw SessionError.Busy() + isBusy = true + viewDelegate.onNfcSessionStarted(cardId, initialMessage) + reader.openSession() + } + + private fun preflightRead(callback: (result: CompletionResult) -> Unit) { + val readCommand = ReadCommand() + readCommand.run(this) { result -> + when (result) { + is CompletionResult.Failure -> { + tryHandleError(result.error) { handleErrorResult -> + when (handleErrorResult) { + is CompletionResult.Success -> preflightRead(callback) + is CompletionResult.Failure -> { + stopWithError(result.error) + callback(CompletionResult.Failure(result.error)) + } + } + } + } + is CompletionResult.Success -> { + val receivedCardId = result.data.cardId + if (cardId != null && receivedCardId != cardId) { + stopWithError(SessionError.WrongCard()) + callback(CompletionResult.Failure(SessionError.WrongCard())) + return@run + } + environment.card = result.data + cardId = receivedCardId + callback(CompletionResult.Success(result.data)) + } + } + } + } + + /** + * Stops the current session with the text message. + * @param message If null, the default message will be shown. + */ + private fun stop(message: Message? = null) { + reader.closeSession() + viewDelegate.onNfcSessionCompleted(message) + isBusy = false + } + + /** + * Stops the current session on error. + * @param error An error that will be shown. + */ + private fun stopWithError(error: Exception) { + reader.closeSession() + isBusy = false + + val errorMessage = if (error is SessionError) { + "${error::class.simpleName}: ${error.code}" + } else { + error.localizedMessage + } + if (error !is SessionError.UserCancelled) viewDelegate.onError(errorMessage) + } + + fun send(apdu: CommandApdu, callback: (result: CompletionResult) -> Unit) { + reader.transceiveApdu(apdu, callback) + } + + private fun tryHandleError( + error: SessionError, callback: (result: CompletionResult) -> Unit) { + + when (error) { + is SessionError.NeedEncryption -> { + when (environment.encryptionMode) { + EncryptionMode.NONE -> { + environment.encryptionKey = null + environment.encryptionMode = EncryptionMode.FAST + } + EncryptionMode.FAST -> { + environment.encryptionKey = null + environment.encryptionMode = EncryptionMode.STRONG + } + EncryptionMode.STRONG -> { + Log.e(this::class.simpleName!!, "Encryption doesn't work") + callback(CompletionResult.Failure(SessionError.NeedEncryption())) + } + } + return establishEncryption(callback) + } + else -> callback(CompletionResult.Failure(SessionError.UnknownError())) + } + } + + private fun establishEncryption(callback: (result: CompletionResult) -> Unit) { + val encryptionHelper: EncryptionHelper = + if (environment.encryptionMode == EncryptionMode.STRONG) { + StrongEncryptionHelper() + } else { + FastEncryptionHelper() + } + val openSesssionCommand = OpenSessionCommand(encryptionHelper.keyA) + openSesssionCommand.run(this) { result -> + when (result) { + is CompletionResult.Success -> { + val uid = result.data.uid + val protocolKey = environment.pin1.calculateSha256().pbkdf2Hash(uid, 50) + val secret = encryptionHelper.generateSecret(result.data.sessionKeyB) + val sessionKey = (secret + protocolKey).calculateSha256() + environment.encryptionKey = sessionKey + callback(CompletionResult.Success(true)) + } + is CompletionResult.Failure -> callback(CompletionResult.Failure(result.error)) + } + } + } +} \ No newline at end of file diff --git a/tangem-core/src/main/java/com/tangem/CardManagerDelegate.kt b/tangem-core/src/main/java/com/tangem/CardSessionViewDelegate.kt similarity index 78% rename from tangem-core/src/main/java/com/tangem/CardManagerDelegate.kt rename to tangem-core/src/main/java/com/tangem/CardSessionViewDelegate.kt index 5fce77186d..da5f26e020 100644 --- a/tangem-core/src/main/java/com/tangem/CardManagerDelegate.kt +++ b/tangem-core/src/main/java/com/tangem/CardSessionViewDelegate.kt @@ -1,19 +1,18 @@ package com.tangem import com.tangem.common.CompletionResult -import com.tangem.tasks.TaskError /** * Allows interaction with users and shows visual elements. * * Its default implementation, DefaultCardManagerDelegate, is in our tangem-sdk module. */ -interface CardManagerDelegate { +interface CardSessionViewDelegate { /** * It is called when user is expected to scan a Tangem Card with an Android device. */ - fun onNfcSessionStarted(cardId: String?) + fun onNfcSessionStarted(cardId: String?, message: Message? = null) /** * It is called when security delay is triggered by the card. @@ -36,16 +35,21 @@ interface CardManagerDelegate { /** * It is called when NFC session was completed and a user can take the card away from the Android device. */ - fun onNfcSessionCompleted() + fun onNfcSessionCompleted(message: Message? = null) /** * It is called when some error occur during NFC session. */ - fun onError(error: TaskError) + fun onError(errorMessage: String) /** * It is called when a user is expected to enter pin code. */ fun onPinRequested(callback: (result: CompletionResult) -> Unit) -} \ No newline at end of file +} + +/** + * Wrapper for a message that can be shown to user after a start of NFC session. + */ +data class Message(val header: String? = null, val body: String? = null) \ No newline at end of file diff --git a/tangem-core/src/main/java/com/tangem/Config.kt b/tangem-core/src/main/java/com/tangem/Config.kt index 2d1193e176..7403a4c771 100644 --- a/tangem-core/src/main/java/com/tangem/Config.kt +++ b/tangem-core/src/main/java/com/tangem/Config.kt @@ -1,6 +1,28 @@ package com.tangem class Config( + /** + * Enables or disables Linked Terminal feature. + + App can optionally generate ECDSA key pair Terminal_PrivateKey / Terminal_PublicKey. + And then submit Terminal_PublicKey to the card in any SIGN command. + Once SIGN is successfully executed by COS (Card Operation System), + including PIN2 verification and/or completion of security delay, the submitted + Terminal_PublicKey key is stored by COS. After that, the App instance is deemed trusted + by COS and COS will allow skipping security delay for subsequent SIGN operations + thus improving convenience without sacrificing security. + + In order to skip security delay, App should use Terminal_PrivateKey to compute the signature + of the data being submitted to SIGN command for signing and transmit this signature in + Terminal_Transaction_Signature parameter in the same SIGN command. COS will verify + the correctness of Terminal_Transaction_Signature using previously stored Terminal_PublicKey + and, if correct, will skip security delay for the current SIGN operation. + */ val linkedTerminal: Boolean = true, + + /** + * If not null, it will be used to validate Issuer data and issuer extra data. + * If null, issuerPublicKey from current card will be used. + */ val issuerPublicKey: ByteArray? = null ) \ No newline at end of file diff --git a/tangem-core/src/main/java/com/tangem/SessionError.kt b/tangem-core/src/main/java/com/tangem/SessionError.kt new file mode 100644 index 0000000000..07209b6a3f --- /dev/null +++ b/tangem-core/src/main/java/com/tangem/SessionError.kt @@ -0,0 +1,139 @@ +package com.tangem + +import com.tangem.commands.Card +import com.tangem.commands.ReadCommand +import com.tangem.common.apdu.StatusWord +import com.tangem.tasks.ScanTask + +/** + * An error class that represent typical errors that may occur when performing Tangem SDK tasks. + * Errors are propagated back to the caller in callbacks. + */ +sealed class SessionError(val code: Int) : Exception() { + + //Errors in serializing APDU + /** + * This error is returned when there [CommandSerializer] cannot deserialize [com.tangem.common.tlv.Tlv] + * (this error is a wrapper around internal [com.tangem.common.tlv.TlvDecoder] errors). + */ + class SerializeCommandError : SessionError(1000) + + class DeserializeApduFailed : SessionError(1001) + class EncodingFailedTypeMismatch : SessionError(1002) + class EncodingFailed : SessionError(1003) + + class DecodingFailedMissingTag : SessionError(1004) + class DecodingFailedTypeMismatch : SessionError(1005) + class DecodingFailed : SessionError(1005) + + /** + * This error is returned when unknown [StatusWord] is received from a card. + */ + class UnknownStatus : SessionError(2001) + + /** + * This error is returned when a card's reply is [StatusWord.ErrorProcessingCommand]. + * The card sends this status in case of internal card error. + */ + class ErrorProcessingCommand : SessionError(2002) + + /** + * This error is returned when a task (such as [ScanTask]) requires that [ReadCommand] + * is executed before performing other commands. + */ + class MissingPreflightRead : SessionError(2003) + + /** + * This error is returned when a card's reply is [StatusWord.InvalidState]. + * The card sends this status when command can not be executed in the current state of a card. + */ + class InvalidState : SessionError(2004) + + /** + * This error is returned when a card's reply is [StatusWord.InsNotSupported]. + * The card sends this status when the card cannot process the [com.tangem.common.apdu.Instruction]. + */ + class InsNotSupported : SessionError(2005) + + /** + * This error is returned when a card's reply is [StatusWord.InvalidParams]. + * The card sends this status when there are wrong or not sufficient parameters in TLV request, + * or wrong PIN1/PIN2. + * The error may be caused, for example, by wrong parameters of the [Task], [CommandSerializer], + * mapping or serialization errors. + */ + class InvalidParams : SessionError(2006) + + /** + * This error is returned when a card's reply is [StatusWord.NeedEncryption] + * and the encryption was not established by TangemSdk. + */ + class NeedEncryption : SessionError(2007) + + //Scan errors + /** + * This error is returned when a [Task] checks unsuccessfully either + * a card's ability to sign with its private key, or the validity of issuer data. + */ + class VerificationFailed : SessionError(3000) + + /** + * This error is returned when a [ScanTask] returns a [Card] without some of the essential fields. + */ + class CardError : SessionError(3001) + + /** + * This error is returned when a [Task] expects a user to use a particular card, + * and a user tries to use a different card. + */ + class WrongCard : SessionError(3002) + + /** + * Tangem cards can sign currently up to 10 hashes during one [com.tangem.commands.SignCommand]. + * This error is returned when a [com.tangem.commands.SignCommand] receives more than 10 hashes to sign. + */ + class TooMuchHashesInOneTransaction : SessionError(3003) + + /** + * This error is returned when a [com.tangem.commands.SignCommand] + * receives only empty hashes for signature. + */ + class EmptyHashes : SessionError(3004) + + /** + * This error is returned when a [com.tangem.commands.SignCommand] + * receives hashes of different lengths for signature. + */ + class HashSizeMustBeEqual : SessionError(3005) + + /** + * This error is returned when [com.tangem.TangemSdk] was called with a new [Task], + * while a previous [Task] is still in progress. + */ + class Busy : SessionError(4000) + + /** + * This error is returned when a user manually closes NFC Reading Bottom Sheet Dialog. + */ + class UserCancelled : SessionError(4001) + + //NFC errors + class NfcReaderError : SessionError(5002) + + /** + * This error is returned when Android NFC reader loses a tag + * (e.g. a user detaches card from the phone's NFC module) while the NFC session is in progress. + */ + class TagLost : SessionError(5003) + + class UnknownError : SessionError(6000) + + //Specific Command Errors + /** + * This error is returned when [ReadIssuerDataTask] or [ReadIssuerExtraDataTask] expects a counter + * (when the card's requires it), but the counter is missing. + */ + class MissingCounter : SessionError(7001) + + class MissingIssuerPubicKey : SessionError(7002) +} \ No newline at end of file diff --git a/tangem-core/src/main/java/com/tangem/TangemSdk.kt b/tangem-core/src/main/java/com/tangem/TangemSdk.kt new file mode 100644 index 0000000000..786249d22b --- /dev/null +++ b/tangem-core/src/main/java/com/tangem/TangemSdk.kt @@ -0,0 +1,383 @@ +package com.tangem + +import com.tangem.commands.* +import com.tangem.commands.personalization.DepersonalizeCommand +import com.tangem.commands.personalization.DepersonalizeResponse +import com.tangem.commands.personalization.PersonalizeCommand +import com.tangem.commands.personalization.entities.Acquirer +import com.tangem.commands.personalization.entities.CardConfig +import com.tangem.commands.personalization.entities.Issuer +import com.tangem.commands.personalization.entities.Manufacturer +import com.tangem.common.CompletionResult +import com.tangem.common.TerminalKeysService +import com.tangem.crypto.CryptoUtils +import com.tangem.tasks.CreateWalletTask +import com.tangem.tasks.ScanTask + +/** + * The main interface of Tangem SDK that allows your app to communicate with Tangem cards. + * + * @property reader is an interface that is responsible for NFC connection and + * transfer of data to and from the Tangem Card. + * Its default implementation, NfcCardReader, is in our tangem-sdk module. + * @property viewDelegate An interface that allows interaction with users and shows relevant UI. + * Its default implementation, DefaultCardSessionViewDelegate, is in our tangem-sdk module. + * @property config allows to change a number of parameters for communication with Tangem cards. + * Do not change the default values unless you know what you are doing. + */ +class TangemSdk( + private val reader: CardReader, + private val viewDelegate: CardSessionViewDelegate, + var config: Config = Config() +) { + + private var terminalKeysService: TerminalKeysService? = null + private var cardSession: CardSession? = null + + init { + CryptoUtils.initCrypto() + } + + /** + * This method launches a [ScanTask] on a new thread. + * + * To start using any card, you first need to read it using the scanCard() method. + * This method launches an NFC session, and once it’s connected with the card, + * it obtains the card data. Optionally, if the card contains a wallet (private and public key pair), + * it proves that the wallet owns a private key that corresponds to a public one. + * + * @param callback is triggered on the completion of the [ScanTask] and provides card response + * in the form of [Card] if the task was performed successfully or [SessionError] in case of an error. + */ + fun scanCard(callback: (result: CompletionResult) -> Unit) { + cardSession = CardSession(buildEnvironment(), reader, viewDelegate) + cardSession!!.startWithRunnable(ScanTask(), callback) + } + + /** + * This method launches a [SignCommand] on a new thread. + * + * It allows you to sign one or multiple hashes. + * Simultaneous signing of array of hashes in a single [SignCommand] is required to support + * Bitcoin-type multi-input blockchains (UTXO). + * The [SignCommand] will return a corresponding array of signatures. + * + * Please note that Tangem cards usually protect the signing with a security delay + * that may last up to 90 seconds, depending on a card. + * It is for [CardSessionViewDelegate] to notify users of security delay. + * + * @param hashes Array of transaction hashes. It can be from one or up to ten hashes of the same length. + * @param cardId CID, Unique Tangem card ID number + * @param callback is triggered on the completion of the [SignCommand] and provides card response + * in the form of [SignResponse] if the task was performed successfully + * or [SessionError] in case of an error. + */ + fun sign(hashes: Array, cardId: String, + callback: (result: CompletionResult) -> Unit) { + cardSession = CardSession(buildEnvironment(), reader, viewDelegate, cardId) + cardSession!!.startWithRunnable(SignCommand(hashes), callback) + } + + /** + * This method launches a [ReadIssuerDataCommand] on a new thread. + + * This command returns 512-byte Issuer Data field and its issuer’s signature. + * Issuer Data is never changed or parsed from within the Tangem COS. The issuer defines purpose of use, + * format and payload of Issuer Data. For example, this field may contain information about + * wallet balance signed by the issuer or additional issuer’s attestation data. + * + * @param cardId CID, Unique Tangem card ID number. + * @param callback is triggered on the completion of the [ReadIssuerDataCommand] and provides + * card response in the form of [ReadIssuerDataResponse] if the task was performed successfully + * or [SessionError] in case of an error. + */ + fun readIssuerData(cardId: String, + callback: (result: CompletionResult) -> Unit) { + cardSession = CardSession(buildEnvironment(), reader, viewDelegate, cardId) + cardSession!!.startWithRunnable(ReadIssuerDataCommand(config.issuerPublicKey), callback) + } + + /** + * This method launches a [ReadIssuerExtraDataCommand] on a new thread. + * + * This command retrieves Issuer Extra Data field and its issuer’s signature. + * Issuer Extra Data is never changed or parsed from within the Tangem COS. The issuer defines purpose of use, + * format and payload of Issuer Data. . For example, this field may contain photo or + * biometric information for ID card product. Because of the large size of Issuer_Extra_Data, + * a series of these commands have to be executed to read the entire Issuer_Extra_Data. + * + * @param cardId CID, Unique Tangem card ID number. + * @param callback is triggered on the completion of the [ReadIssuerExtraDataCommand] and provides + * card response in the form of [ReadIssuerExtraDataResponse] if the task was performed successfully + * or [SessionError] in case of an error. + */ + fun readIssuerExtraData(cardId: String, + callback: (result: CompletionResult) -> Unit) { + cardSession = CardSession(buildEnvironment(), reader, viewDelegate, cardId) + cardSession!!.startWithRunnable(ReadIssuerExtraDataCommand(config.issuerPublicKey), callback) + } + + /** + * This method launches a [WriteIssuerDataCommand] on a new thread. + * + * This command writes 512-byte Issuer Data field and its issuer’s signature. + * Issuer Data is never changed or parsed from within the Tangem COS. The issuer defines purpose of use, + * format and payload of Issuer Data. For example, this field may contain information about + * wallet balance signed by the issuer or additional issuer’s attestation data. + * + * @param cardId CID, Unique Tangem card ID number. + * @param issuerData Data provided by issuer. + * @param issuerDataSignature Issuer’s signature of [issuerData] with Issuer Data Private Key. + * @param issuerDataCounter An optional counter that protect issuer data against replay attack. + * @param callback is triggered on the completion of the [WriteIssuerDataCommand] and provides + * card response in the form of [WriteIssuerDataResponse] if the task was performed successfully + * or [SessionError] in case of an error. + */ + fun writeIssuerData(cardId: String, + issuerData: ByteArray, + issuerDataSignature: ByteArray, + issuerDataCounter: Int? = null, + callback: (result: CompletionResult) -> Unit) { + val command = WriteIssuerDataCommand( + issuerData, + issuerDataSignature, + issuerDataCounter, + config.issuerPublicKey + ) + cardSession = CardSession(buildEnvironment(), reader, viewDelegate, cardId) + cardSession!!.startWithRunnable(command, callback) + } + + /** + * This method launches a [WriteIssuerExtraDataCommand] on a new thread. + * + * This command writes Issuer Extra Data field and its issuer’s signature. + * Issuer Extra Data is never changed or parsed from within the Tangem COS. + * The issuer defines purpose of use, format and payload of Issuer Data. + * For example, this field may contain a photo or biometric information for ID card products. + * Because of the large size of IssuerExtraData, a series of these commands have to be executed + * to write entire IssuerExtraData. + * + * @param cardId CID, Unique Tangem card ID number. + * @param issuerData Data provided by issuer. + * @param startingSignature Issuer’s signature with Issuer Data Private Key of [cardId], + * [issuerDataCounter] (if flags Protect_Issuer_Data_Against_Replay and + * Restrict_Overwrite_Issuer_Extra_Data are set in [SettingsMask]) and size of [issuerData]. + * @param finalizingSignature Issuer’s signature with Issuer Data Private Key of [cardId], + * [issuerData] and [issuerDataCounter] (the latter one only if flags Protect_Issuer_Data_Against_Replay + * andRestrict_Overwrite_Issuer_Extra_Data are set in [SettingsMask]). + * @param issuerDataCounter An optional counter that protect issuer data against replay attack. + * @param callback is triggered on the completion of the [WriteIssuerExtraDataCommand] and provides + * card response in the form of [WriteIssuerDataResponse] if the task was performed successfully + * or [SessionError] in case of an error. + */ + fun writeIssuerExtraData(cardId: String, + issuerData: ByteArray, + startingSignature: ByteArray, + finalizingSignature: ByteArray, + issuerDataCounter: Int? = null, + callback: (result: CompletionResult) -> Unit) { + val command = WriteIssuerExtraDataCommand( + issuerData, + startingSignature, finalizingSignature, + issuerDataCounter, + config.issuerPublicKey + ) + cardSession = CardSession(buildEnvironment(), reader, viewDelegate, cardId) + cardSession!!.startWithRunnable(command, callback) + } + + /** + * This method launches a [WriteUserDataCommand] on a new thread. + * + * This command writes some of UserData, UserProtectedData, UserCounter and UserProtectedCounter fields. + * User_Data and User_ProtectedData are never changed or parsed by the executable code the Tangem COS. + * The App defines purpose of use, format and it's payload. For example, this field may contain cashed information + * from blockchain to accelerate preparing new transaction. + * User_Counter and User_ProtectedCounter are counters, that initial values can be set by App and increased on every signing + * of new transaction (on SIGN command that calculate new signatures). The App defines purpose of use. + * For example, this fields may contain blockchain nonce value. + * + * Writing of UserCounter and UserData is protected only by PIN1. + * UserProtectedCounter and UserProtectedData need additionally PIN2 to confirmation. + */ + fun writeUserData( + cardId: String, + userData: ByteArray? = null, + userProtectedData: ByteArray? = null, + userCounter: Int? = null, + userProtectedCounter: Int? = null, + callback: (result: CompletionResult) -> Unit + ) { + val command = WriteUserDataCommand(userData, userProtectedData, userCounter, userProtectedCounter) + cardSession = CardSession(buildEnvironment(), reader, viewDelegate, cardId) + cardSession!!.startWithRunnable(command, callback) + } + + /** + * This method launches a [ReadUserDataCommand] on a new thread. + * + * This command returns two up to 512-byte User_Data, User_Protected_Data and two counters User_Counter and + * User_Protected_Counter fields. + * User_Data and User_ProtectedData are never changed or parsed by the executable code the Tangem COS. + * The App defines purpose of use, format and it's payload. For example, this field may contain cashed information + * from blockchain to accelerate preparing new transaction. + * User_Counter and User_ProtectedCounter are counters, that initial values can be set by App and increased on every signing + * of new transaction (on SIGN command that calculate new signatures). The App defines purpose of use. + * For example, this fields may contain blockchain nonce value. + * + * @param cardId CID, Unique Tangem card ID number. + * @param callback is triggered on the completion of the [ReadUserDataCommand] and provides + * card response in the form of [ReadUserDataResponse] if the task was performed successfully + * or [SessionError] in case of an error. + */ + fun readUserData(cardId: String, callback: (result: CompletionResult) -> Unit) { + cardSession = CardSession(buildEnvironment(), reader, viewDelegate, cardId) + cardSession!!.startWithRunnable(ReadUserDataCommand(), callback) + } + + /** + * This method launches a [CreateWalletTask] on a new thread. + * + * This this will create a new wallet on the card having ‘Empty’ state with [CreateWalletCommand] + * and will check the success of the operation by performing [CheckWalletCommand]. + * A key pair WalletPublicKey / WalletPrivateKey is generated and securely stored in the card. + * App will need to obtain Wallet_PublicKey from the [CreateWalletResponse] or from the + * response of [ReadCommand] and then transform it into an address of corresponding + * blockchain wallet according to a specific blockchain algorithm. + * WalletPrivateKey is never revealed by the card and will be used by [SignCommand] and [CheckWalletCommand]. + * RemainingSignature is set to MaxSignatures. + * + * @param cardId CID, Unique Tangem card ID number. + * @param callback is triggered on the completion of the [CreateWalletTask] and provides + * card response in the form of [CreateWalletResponse] if the task was performed successfully + * or [SessionError] in case of an error. + */ + fun createWallet(cardId: String, + callback: (result: CompletionResult) -> Unit) { + cardSession = CardSession(buildEnvironment(), reader, viewDelegate, cardId) + cardSession!!.startWithRunnable(CreateWalletTask(), callback) + } + + /** + * This method launches a [PurgeWalletCommand] on a new thread. + * + * This command deletes all wallet data. If IsReusable flag is enabled during personalization, + + * or [CreateWalletCommand]. + * If IsReusable flag is disabled, the card switches to ‘Purged’ state. + * ‘Purged’ state is final, it makes the card useless. + * + * @param cardId CID, Unique Tangem card ID number. + * @param callback is triggered on the completion of the [PurgeWalletCommand] and provides + * card response in the form of [PurgeWalletResponse] if the task was performed successfully + * or [SessionError] in case of an error. + */ + fun purgeWallet(cardId: String, + callback: (result: CompletionResult) -> Unit) { + cardSession = CardSession(buildEnvironment(), reader, viewDelegate, cardId) + cardSession!!.startWithRunnable(PurgeWalletCommand(), callback) + } + + /** + * Command available on SDK cards only + * + * This method launches a [DepersonalizeCommand] on a new thread. + * + * This command resets card to initial state, + * erasing all data written during personalization and usage. + * + * @param cardId CID, Unique Tangem card ID number. + * @param callback is triggered on the completion of the [DepersonalizeCommand] and provides + * card response in the form of [DepersonalizeResponse] if the task was performed successfully + * or [SessionError] in case of an error. + * */ + fun depersonalize(cardId: String, + callback: (result: CompletionResult) -> Unit) { + cardSession = CardSession(buildEnvironment(), reader, viewDelegate, cardId) + cardSession!!.startWithRunnable(DepersonalizeCommand(), callback) + } + + /** + * Command available on SDK cards only + * + * This method launches a [PersonalizeCommand] on a new thread. + * + * Personalization is an initialization procedure, required before starting using a card. + * During this procedure a card setting is set up. + * During this procedure all data exchange is encrypted. + * @param config is a configuration file with all the card settings that are written on the card + * during personalization. + * @param issuer Issuer is a third-party team or company wishing to use Tangem cards. + * @param manufacturer Tangem Card Manufacturer. + * @param acquirer Acquirer is a trusted third-party company that operates proprietary + * (non-EMV) POS terminal infrastructure and transaction processing back-end. + * @param callback is triggered on the completion of the [PersonalizeCommand] and provides + * card response in the form of [Card] if the command was performed successfully + * or [SessionError] in case of an error. + */ + fun personalize(config: CardConfig, + issuer: Issuer, manufacturer: Manufacturer, acquirer: Acquirer? = null, + callback: (result: CompletionResult) -> Unit) { + val command = PersonalizeCommand(config, issuer, manufacturer, acquirer) + cardSession = CardSession(buildEnvironment(), reader, viewDelegate) + cardSession!!.startWithRunnable(command, callback) + } + + /** + * Allows running a custom bunch of commands in one [CardSession] by creating a custom task. + * [TangemSdk] will start a card session, perform preflight [ReadCommand], + * invoke [CardSessionRunnable.run] and close the session. + * You can find the current card in the [CardSession.environment]. + + * @runnable: A custom task, adopting [CardSessionRunnable] protocol + * @cardId: CID, Unique Tangem card ID number. If not null, the SDK will check that you the card + * with which you tapped a phone has this [cardId] and SDK will return + * the [SessionError.WrongCard] otherwise. + * @initialMessage: A custom description that shows at the beginning of the NFC session. + * If null, default message will be used. + * @callback: Standard [TangemSdk] callback. + */ + fun startSession( + runnable: CardSessionRunnable, cardId: String? = null, initialMessage: Message? = null, + callback: (result: CompletionResult) -> Unit) { + cardSession = CardSession(buildEnvironment(), reader, viewDelegate, cardId, initialMessage) + Thread().run { cardSession!!.startWithRunnable(runnable, callback) } + } + + /** + * Allows running a custom bunch of commands in one [CardSession] with lightweight closure syntax. + * Tangem SDK will start a card sesion and perform preflight [ReadCommand]. + + * @cardId: CID, Unique Tangem card ID number. If not null, the SDK will check that you the card + * with which you tapped a phone has this [cardId] and SDK will return + * the [SessionError.WrongCard] otherwise. + * @initialMessage: A custom description that shows at the beginning of the NFC session. + * If null, default message will be used. + * @callback: At first, you should check that the [SessionError] is not null, + * then you can use the [CardSession] to interact with a card. + */ + fun startSession( + cardId: String? = null, initialMessage: Message? = null, + callback: (session: CardSession, error: SessionError?) -> Unit) { + cardSession = CardSession(buildEnvironment(), reader, viewDelegate, cardId, initialMessage) + Thread().run { cardSession!!.start(callback) } + } + + /** + * Allows to set a particular [TerminalKeysService] to retrieve terminal keys. + * Default implementation is provided in tangem-sdk module: [TerminalKeysStorage]. + */ + fun setTerminalKeysService(terminalKeysService: TerminalKeysService) { + this.terminalKeysService = terminalKeysService + } + + private fun buildEnvironment(): CardEnvironment { + val terminalKeys = if (config.linkedTerminal) terminalKeysService?.getKeys() else null + return CardEnvironment( + terminalKeys = terminalKeys + ) + } + + companion object +} \ No newline at end of file diff --git a/tangem-core/src/main/java/com/tangem/commands/CheckWalletCommand.kt b/tangem-core/src/main/java/com/tangem/commands/CheckWalletCommand.kt index 28f0cd2d43..d6df4c4204 100644 --- a/tangem-core/src/main/java/com/tangem/commands/CheckWalletCommand.kt +++ b/tangem-core/src/main/java/com/tangem/commands/CheckWalletCommand.kt @@ -1,14 +1,16 @@ package com.tangem.commands -import com.tangem.common.CardEnvironment +import com.tangem.CardEnvironment +import com.tangem.CardSession +import com.tangem.SessionError +import com.tangem.common.CompletionResult import com.tangem.common.apdu.CommandApdu import com.tangem.common.apdu.Instruction import com.tangem.common.apdu.ResponseApdu import com.tangem.common.tlv.TlvBuilder -import com.tangem.common.tlv.TlvMapper +import com.tangem.common.tlv.TlvDecoder import com.tangem.common.tlv.TlvTag import com.tangem.crypto.CryptoUtils -import com.tangem.tasks.TaskError /** * Deserialized response from the Tangem card after [CheckWalletCommand]. @@ -40,33 +42,54 @@ class CheckWalletResponse( * @property cardId Unique Tangem card ID number * @property challenge Random challenge generated by application */ -class CheckWalletCommand : CommandSerializer() { +class CheckWalletCommand( + private val curve: EllipticCurve, private val publicKey: ByteArray +) : Command() { - val challenge = CryptoUtils.generateRandomBytes(16) + private val challenge = CryptoUtils.generateRandomBytes(16) - override fun serialize(cardEnvironment: CardEnvironment): CommandApdu { + override fun run(session: CardSession, callback: (result: CompletionResult) -> Unit) { + transceive(session) { result -> + when (result) { + is CompletionResult.Failure -> { + callback(CompletionResult.Failure(result.error)) + } + is CompletionResult.Success -> { + val verified = result.data.verify( + curve, + publicKey, + challenge + ) + if (verified) { + callback(CompletionResult.Success(result.data)) + } else { + callback(CompletionResult.Failure(SessionError.VerificationFailed())) + } + } + } + } + } + + override fun serialize(environment: CardEnvironment): CommandApdu { val tlvBuilder = TlvBuilder() - tlvBuilder.append(TlvTag.Pin, cardEnvironment.pin1) - tlvBuilder.append(TlvTag.CardId, cardEnvironment.cardId) + tlvBuilder.append(TlvTag.Pin, environment.pin1) + tlvBuilder.append(TlvTag.CardId, environment.card?.cardId) tlvBuilder.append(TlvTag.Challenge, challenge) return CommandApdu( Instruction.CheckWallet, tlvBuilder.serialize(), - cardEnvironment.encryptionMode, cardEnvironment.encryptionKey + environment.encryptionMode, environment.encryptionKey ) } - override fun deserialize(cardEnvironment: CardEnvironment, responseApdu: ResponseApdu): CheckWalletResponse? { - val tlvData = responseApdu.getTlvData(cardEnvironment.encryptionKey) ?: return null + override fun deserialize(environment: CardEnvironment, apdu: ResponseApdu): CheckWalletResponse { + val tlvData = apdu.getTlvData(environment.encryptionKey) + ?: throw SessionError.DeserializeApduFailed() - return try { - val mapper = TlvMapper(tlvData) - CheckWalletResponse( - cardId = mapper.map(TlvTag.CardId), - salt = mapper.map(TlvTag.Salt), - walletSignature = mapper.map(TlvTag.Signature) - ) - } catch (exception: Exception) { - throw TaskError.SerializeCommandError() - } + val decoder = TlvDecoder(tlvData) + return CheckWalletResponse( + cardId = decoder.decode(TlvTag.CardId), + salt = decoder.decode(TlvTag.Salt), + walletSignature = decoder.decode(TlvTag.Signature) + ) } } \ No newline at end of file diff --git a/tangem-core/src/main/java/com/tangem/commands/Command.kt b/tangem-core/src/main/java/com/tangem/commands/Command.kt new file mode 100644 index 0000000000..9d06c5fe7f --- /dev/null +++ b/tangem-core/src/main/java/com/tangem/commands/Command.kt @@ -0,0 +1,117 @@ +package com.tangem.commands + +import com.tangem.* +import com.tangem.common.CompletionResult +import com.tangem.common.apdu.CommandApdu +import com.tangem.common.apdu.ResponseApdu +import com.tangem.common.apdu.StatusWord +import com.tangem.common.apdu.toSessionError +import com.tangem.common.extensions.toInt +import com.tangem.common.tlv.TlvTag + +/** + * Basic interface for a parsed response from [Command]. + */ +interface CommandResponse + +/** + * Basic class for Tangem card commands + */ +abstract class Command : CardSessionRunnable { + + /** + * Serializes data into an array of [com.tangem.common.tlv.Tlv], + * then creates [CommandApdu] with this data. + * @param environment [CardEnvironment] of the current card + * @return command data converted to [CommandApdu] that allows to convert it to [ByteArray] + * that can be sent to a Tangem card + */ + abstract fun serialize(environment: CardEnvironment): CommandApdu + + /** + * Deserializes data received from a card and stored in [ResponseApdu] + * into an array of [com.tangem.common.tlv.Tlv]. Then maps it into a [CommandResponse]. + * @param environment [CardEnvironment] of the current card. + * @param apdu received data. + * @return Card response converted to a [CommandResponse] of a type [T] + */ + abstract fun deserialize(environment: CardEnvironment, apdu: ResponseApdu): T + + override fun run(session: CardSession, callback: (result: CompletionResult) -> Unit) { + transceive(session, callback) + } + + fun transceive(session: CardSession, callback: (result: CompletionResult) -> Unit) { + try { + val apdu = serialize(session.environment) + transceiveApdu(apdu, session) { result -> + when (result) { + is CompletionResult.Failure -> callback(CompletionResult.Failure(result.error)) + is CompletionResult.Success -> { + val response = deserialize(session.environment, result.data) + callback(CompletionResult.Success(response)) + } + } + } + } catch (error: SessionError) { + callback(CompletionResult.Failure(error)) + } + } + + private fun transceiveApdu(apdu: CommandApdu, session: CardSession, callback: (result: CompletionResult) -> Unit) { + session.send(apdu) { result -> + + when (result) { + is CompletionResult.Success -> { + val responseApdu = result.data + when (responseApdu.statusWord) { + StatusWord.ProcessCompleted, StatusWord.Pin1Changed, StatusWord.Pin2Changed, StatusWord.PinsChanged + -> callback(CompletionResult.Success(responseApdu)) + StatusWord.NeedPause -> { + // NeedPause is returned from the card whenever security delay is triggered. + val remainingTime = deserializeSecurityDelay(responseApdu, session.environment) + if (remainingTime != null) { + session.viewDelegate.onSecurityDelay( + remainingTime, + session.environment.card?.pauseBeforePin2 ?: 0) + } + Log.i(this::class.simpleName!!, "Nfc command ${this::class.simpleName!!} " + + "triggered security delay of $remainingTime milliseconds") + transceiveApdu(apdu, session, callback) + } + else -> { + val error = responseApdu.statusWord.toSessionError() + if (error != null && !tryHandleError(error)) { + callback(CompletionResult.Failure(error)) + } else { + callback(CompletionResult.Failure(SessionError.UnknownError())) + } + } + } + } + is CompletionResult.Failure -> + if (result.error == SessionError.TagLost()) { + session.viewDelegate.onTagLost() + } else if (result.error is SessionError.UserCancelled) { + callback(CompletionResult.Failure(SessionError.UserCancelled())) + session.reader.closeSession() + } + } + } + } + + /** + * Helper method to parse security delay information received from a card. + * + * @return Remaining security delay in milliseconds. + */ + private fun deserializeSecurityDelay(responseApdu: ResponseApdu, cardEnvironment: CardEnvironment): Int? { + val tlv = responseApdu.getTlvData() + return tlv?.find { it.tag == TlvTag.Pause }?.value?.toInt() + } + + private fun tryHandleError(error: SessionError) : Boolean { + return false + } + +} \ No newline at end of file diff --git a/tangem-core/src/main/java/com/tangem/commands/CommandSerializer.kt b/tangem-core/src/main/java/com/tangem/commands/CommandSerializer.kt deleted file mode 100644 index 134474fb23..0000000000 --- a/tangem-core/src/main/java/com/tangem/commands/CommandSerializer.kt +++ /dev/null @@ -1,44 +0,0 @@ -package com.tangem.commands - -import com.tangem.common.CardEnvironment -import com.tangem.common.apdu.CommandApdu -import com.tangem.common.apdu.ResponseApdu -import com.tangem.common.extensions.toInt -import com.tangem.common.tlv.TlvTag - -/** - * Simple interface for responses received after sending commands to Tangem cards. - */ -interface CommandResponse - -/** - * Abstract class for all Tangem card commands. - */ -abstract class CommandSerializer { - - /** - * Serializes data into a [List] of [com.tangem.common.tlv.Tlv], - * then creates [CommandApdu] with this data. - * - * @return Command data that can be converted to raw bytes with a method [CommandApdu.toBytes]. - */ - abstract fun serialize(cardEnvironment: CardEnvironment): CommandApdu - - /** - * Deserializes data, received from a card and stored in [ResponseApdu], - * into a [List] of [com.tangem.common.tlv.Tlv]. Then this method maps it into a [CommandResponse]. - * - * @return Card response, converted to a [CommandResponse] of a type [T]. - */ - abstract fun deserialize(cardEnvironment: CardEnvironment, responseApdu: ResponseApdu): T? - - /** - * Helper method to parse security delay information received from a card. - * - * @return Remaining security delay in milliseconds. - */ - fun deserializeSecurityDelay(responseApdu: ResponseApdu, cardEnvironment: CardEnvironment): Int? { - val tlv = responseApdu.getTlvData() - return tlv?.find { it.tag == TlvTag.Pause }?.value?.toInt() - } -} \ No newline at end of file diff --git a/tangem-core/src/main/java/com/tangem/commands/CreateWalletCommand.kt b/tangem-core/src/main/java/com/tangem/commands/CreateWalletCommand.kt index 18b20f1394..d93da64242 100644 --- a/tangem-core/src/main/java/com/tangem/commands/CreateWalletCommand.kt +++ b/tangem-core/src/main/java/com/tangem/commands/CreateWalletCommand.kt @@ -1,13 +1,13 @@ package com.tangem.commands -import com.tangem.common.CardEnvironment +import com.tangem.CardEnvironment +import com.tangem.SessionError import com.tangem.common.apdu.CommandApdu import com.tangem.common.apdu.Instruction import com.tangem.common.apdu.ResponseApdu import com.tangem.common.tlv.TlvBuilder -import com.tangem.common.tlv.TlvMapper +import com.tangem.common.tlv.TlvDecoder import com.tangem.common.tlv.TlvTag -import com.tangem.tasks.TaskError class CreateWalletResponse( /** @@ -35,32 +35,29 @@ class CreateWalletResponse( * * @property cardId CID, Unique Tangem card ID number. */ -class CreateWalletCommand : CommandSerializer() { +class CreateWalletCommand : Command() { - override fun serialize(cardEnvironment: CardEnvironment): CommandApdu { + override fun serialize(environment: CardEnvironment): CommandApdu { val tlvBuilder = TlvBuilder() - tlvBuilder.append(TlvTag.Pin, cardEnvironment.pin1) - tlvBuilder.append(TlvTag.CardId, cardEnvironment.cardId) - tlvBuilder.append(TlvTag.Pin2, cardEnvironment.pin2) - tlvBuilder.append(TlvTag.Cvc, cardEnvironment.cvc) + tlvBuilder.append(TlvTag.Pin, environment.pin1) + tlvBuilder.append(TlvTag.CardId, environment.card?.cardId) + tlvBuilder.append(TlvTag.Pin2, environment.pin2) + tlvBuilder.append(TlvTag.Cvc, environment.cvc) return CommandApdu( Instruction.CreateWallet, tlvBuilder.serialize(), - cardEnvironment.encryptionMode, cardEnvironment.encryptionKey + environment.encryptionMode, environment.encryptionKey ) } - override fun deserialize(cardEnvironment: CardEnvironment, responseApdu: ResponseApdu): CreateWalletResponse? { - val tlvData = responseApdu.getTlvData(cardEnvironment.encryptionKey) ?: return null + override fun deserialize(environment: CardEnvironment, apdu: ResponseApdu): CreateWalletResponse { + val tlvData = apdu.getTlvData(environment.encryptionKey) + ?: throw SessionError.DeserializeApduFailed() - return try { - val mapper = TlvMapper(tlvData) - CreateWalletResponse( - cardId = mapper.map(TlvTag.CardId), - status = mapper.map(TlvTag.Status), - walletPublicKey = mapper.map(TlvTag.WalletPublicKey) - ) - } catch (exception: Exception) { - throw TaskError.SerializeCommandError() - } + val decoder = TlvDecoder(tlvData) + return CreateWalletResponse( + cardId = decoder.decode(TlvTag.CardId), + status = decoder.decode(TlvTag.Status), + walletPublicKey = decoder.decode(TlvTag.WalletPublicKey) + ) } } \ No newline at end of file diff --git a/tangem-core/src/main/java/com/tangem/commands/OpenSessionCommand.kt b/tangem-core/src/main/java/com/tangem/commands/OpenSessionCommand.kt index 85d3ddf866..48b3d9a80e 100644 --- a/tangem-core/src/main/java/com/tangem/commands/OpenSessionCommand.kt +++ b/tangem-core/src/main/java/com/tangem/commands/OpenSessionCommand.kt @@ -1,13 +1,13 @@ package com.tangem.commands -import com.tangem.common.CardEnvironment +import com.tangem.CardEnvironment +import com.tangem.SessionError import com.tangem.common.apdu.CommandApdu import com.tangem.common.apdu.Instruction import com.tangem.common.apdu.ResponseApdu import com.tangem.common.tlv.TlvBuilder -import com.tangem.common.tlv.TlvMapper +import com.tangem.common.tlv.TlvDecoder import com.tangem.common.tlv.TlvTag -import com.tangem.tasks.TaskError class OpenSessionResponse( val sessionKeyB: ByteArray, @@ -20,27 +20,24 @@ class OpenSessionResponse( * to encrypt and decrypt commands’ payload. */ -class OpenSessionCommand(private val sessionKeyA: ByteArray) : CommandSerializer() { - override fun serialize(cardEnvironment: CardEnvironment): CommandApdu { +class OpenSessionCommand(private val sessionKeyA: ByteArray) : Command() { + override fun serialize(environment: CardEnvironment): CommandApdu { val tlvBuilder = TlvBuilder() tlvBuilder.append(TlvTag.SessionKeyA, sessionKeyA) return CommandApdu( Instruction.OpenSession, tlvBuilder.serialize(), - encryptionMode = cardEnvironment.encryptionMode + encryptionMode = environment.encryptionMode ) } - override fun deserialize(cardEnvironment: CardEnvironment, responseApdu: ResponseApdu): OpenSessionResponse? { - val tlvData = responseApdu.getTlvData() ?: return null + override fun deserialize(environment: CardEnvironment, apdu: ResponseApdu): OpenSessionResponse { + val tlvData = apdu.getTlvData() + ?: throw SessionError.DeserializeApduFailed() - return try { - val mapper = TlvMapper(tlvData) - OpenSessionResponse( - sessionKeyB = mapper.map(TlvTag.SessionKeyB), - uid = mapper.map(TlvTag.Uid) - ) - } catch (exception: Exception) { - throw TaskError.SerializeCommandError() - } + val decoder = TlvDecoder(tlvData) + return OpenSessionResponse( + sessionKeyB = decoder.decode(TlvTag.SessionKeyB), + uid = decoder.decode(TlvTag.Uid) + ) } } \ No newline at end of file diff --git a/tangem-core/src/main/java/com/tangem/commands/PurgeWalletCommand.kt b/tangem-core/src/main/java/com/tangem/commands/PurgeWalletCommand.kt index 4eb379e521..cfdcfd5d04 100644 --- a/tangem-core/src/main/java/com/tangem/commands/PurgeWalletCommand.kt +++ b/tangem-core/src/main/java/com/tangem/commands/PurgeWalletCommand.kt @@ -1,13 +1,13 @@ package com.tangem.commands -import com.tangem.common.CardEnvironment +import com.tangem.CardEnvironment +import com.tangem.SessionError import com.tangem.common.apdu.CommandApdu import com.tangem.common.apdu.Instruction import com.tangem.common.apdu.ResponseApdu import com.tangem.common.tlv.TlvBuilder -import com.tangem.common.tlv.TlvMapper +import com.tangem.common.tlv.TlvDecoder import com.tangem.common.tlv.TlvTag -import com.tangem.tasks.TaskError class PurgeWalletResponse( /** @@ -27,29 +27,26 @@ class PurgeWalletResponse( * ‘Purged’ state is final, it makes the card useless. * @property cardId CID, Unique Tangem card ID number. */ -class PurgeWalletCommand : CommandSerializer() { +class PurgeWalletCommand : Command() { - override fun serialize(cardEnvironment: CardEnvironment): CommandApdu { + override fun serialize(environment: CardEnvironment): CommandApdu { val tlvBuilder = TlvBuilder() - tlvBuilder.append(TlvTag.Pin, cardEnvironment.pin1) - tlvBuilder.append(TlvTag.CardId, cardEnvironment.cardId) - tlvBuilder.append(TlvTag.Pin2, cardEnvironment.pin2) + tlvBuilder.append(TlvTag.Pin, environment.pin1) + tlvBuilder.append(TlvTag.CardId, environment.card?.cardId) + tlvBuilder.append(TlvTag.Pin2, environment.pin2) return CommandApdu( Instruction.PurgeWallet, tlvBuilder.serialize(), - cardEnvironment.encryptionMode, cardEnvironment.encryptionKey + environment.encryptionMode, environment.encryptionKey ) } - override fun deserialize(cardEnvironment: CardEnvironment, responseApdu: ResponseApdu): PurgeWalletResponse? { - val tlvData = responseApdu.getTlvData(cardEnvironment.encryptionKey) ?: return null + override fun deserialize(environment: CardEnvironment, apdu: ResponseApdu): PurgeWalletResponse { + val tlvData = apdu.getTlvData(environment.encryptionKey) + ?: throw SessionError.DeserializeApduFailed() - return try { - val mapper = TlvMapper(tlvData) - PurgeWalletResponse( - cardId = mapper.map(TlvTag.CardId), - status = mapper.map(TlvTag.Status)) - } catch (exception: Exception) { - throw TaskError.SerializeCommandError() - } + val decoder = TlvDecoder(tlvData) + return PurgeWalletResponse( + cardId = decoder.decode(TlvTag.CardId), + status = decoder.decode(TlvTag.Status)) } } \ No newline at end of file diff --git a/tangem-core/src/main/java/com/tangem/commands/ReadCommand.kt b/tangem-core/src/main/java/com/tangem/commands/ReadCommand.kt index c861d73469..e0deea6cb8 100644 --- a/tangem-core/src/main/java/com/tangem/commands/ReadCommand.kt +++ b/tangem-core/src/main/java/com/tangem/commands/ReadCommand.kt @@ -1,14 +1,14 @@ package com.tangem.commands -import com.tangem.common.CardEnvironment +import com.tangem.CardEnvironment +import com.tangem.SessionError import com.tangem.common.apdu.CommandApdu import com.tangem.common.apdu.Instruction import com.tangem.common.apdu.ResponseApdu import com.tangem.common.tlv.Tlv import com.tangem.common.tlv.TlvBuilder -import com.tangem.common.tlv.TlvMapper +import com.tangem.common.tlv.TlvDecoder import com.tangem.common.tlv.TlvTag -import com.tangem.tasks.TaskError import java.util.* /** @@ -73,7 +73,7 @@ data class SigningMethod(val rawValue: Int) { if (signRawValidatedByIssuerAndWriteIssuerData) signingMethod += 0x01 shl SigningMethod.signRawValidatedByIssuerAndWriteIssuerData if (signPos) signingMethod += 0x01 shl SigningMethod.signPos } - return SigningMethod(signingMethod) + return SigningMethod(signingMethod) } } } @@ -354,6 +354,13 @@ class Card( */ val userCounter: Int?, + /** + * This value can be initialized by App (with PIN2 confirmation) and will be increased by COS + * with the execution of each [SignCommand]. For example, this field can store blockchain “nonce” + * for a quick one-touch transaction on POS terminals. Returned only if [SigningMethod.SignPos]. + */ + val userProtectedCounter: Int?, + /** * When this value is true, it means that the application is linked to the card, * and COS will not enforce security delay if [SignCommand] will be called @@ -373,9 +380,9 @@ class Card( * This command receives from the Tangem Card all the data about the card and the wallet, * including unique card number (CID or cardId) that has to be submitted while calling all other commands. */ -class ReadCommand : CommandSerializer() { +class ReadCommand : Command() { - override fun serialize(cardEnvironment: CardEnvironment): CommandApdu { + override fun serialize(environment: CardEnvironment): CommandApdu { val tlvBuilder = TlvBuilder() /** * [CardEnvironment] stores the pin1 value. If no pin1 value was set, it will contain @@ -383,48 +390,46 @@ class ReadCommand : CommandSerializer() { * In order to obtain card’s data, [ReadCommand] should use the correct pin 1 value. * The card will not respond if wrong pin 1 has been submitted. */ - tlvBuilder.append(TlvTag.Pin, cardEnvironment.pin1) - tlvBuilder.append(TlvTag.TerminalPublicKey, cardEnvironment.terminalKeys?.publicKey) + tlvBuilder.append(TlvTag.Pin, environment.pin1) + tlvBuilder.append(TlvTag.TerminalPublicKey, environment.terminalKeys?.publicKey) return CommandApdu( Instruction.Read, tlvBuilder.serialize(), - cardEnvironment.encryptionMode, cardEnvironment.encryptionKey + environment.encryptionMode, environment.encryptionKey ) } - override fun deserialize(cardEnvironment: CardEnvironment, responseApdu: ResponseApdu): Card? { - val tlvData = responseApdu.getTlvData(cardEnvironment.encryptionKey) ?: return null + override fun deserialize(environment: CardEnvironment, apdu: ResponseApdu): Card { + val tlvData = apdu.getTlvData(environment.encryptionKey) + ?: throw SessionError.DeserializeApduFailed() - return try { - val tlvMapper = TlvMapper(tlvData) + val decoder = TlvDecoder(tlvData) - Card( - cardId = tlvMapper.mapOptional(TlvTag.CardId) ?: "", - manufacturerName = tlvMapper.mapOptional(TlvTag.ManufactureId) ?: "", - status = tlvMapper.mapOptional(TlvTag.Status), + return Card( + cardId = decoder.decodeOptional(TlvTag.CardId) ?: "", + manufacturerName = decoder.decodeOptional(TlvTag.ManufactureId) ?: "", + status = decoder.decodeOptional(TlvTag.Status), - firmwareVersion = tlvMapper.mapOptional(TlvTag.Firmware), - cardPublicKey = tlvMapper.mapOptional(TlvTag.CardPublicKey), - settingsMask = tlvMapper.mapOptional(TlvTag.SettingsMask), - issuerPublicKey = tlvMapper.mapOptional(TlvTag.IssuerDataPublicKey), - curve = tlvMapper.mapOptional(TlvTag.CurveId), - maxSignatures = tlvMapper.mapOptional(TlvTag.MaxSignatures), - signingMethod = tlvMapper.mapOptional(TlvTag.SigningMethod), - pauseBeforePin2 = tlvMapper.mapOptional(TlvTag.PauseBeforePin2), - walletPublicKey = tlvMapper.mapOptional(TlvTag.WalletPublicKey), - walletRemainingSignatures = tlvMapper.mapOptional(TlvTag.RemainingSignatures), - walletSignedHashes = tlvMapper.mapOptional(TlvTag.SignedHashes), - health = tlvMapper.mapOptional(TlvTag.Health), - isActivated = tlvMapper.map(TlvTag.IsActivated), - activationSeed = tlvMapper.mapOptional(TlvTag.ActivationSeed), - paymentFlowVersion = tlvMapper.mapOptional(TlvTag.PaymentFlowVersion), - userCounter = tlvMapper.mapOptional(TlvTag.UserCounter), - terminalIsLinked = tlvMapper.map(TlvTag.TerminalIsLinked), + firmwareVersion = decoder.decodeOptional(TlvTag.Firmware), + cardPublicKey = decoder.decodeOptional(TlvTag.CardPublicKey), + settingsMask = decoder.decodeOptional(TlvTag.SettingsMask), + issuerPublicKey = decoder.decodeOptional(TlvTag.IssuerDataPublicKey), + curve = decoder.decodeOptional(TlvTag.CurveId), + maxSignatures = decoder.decodeOptional(TlvTag.MaxSignatures), + signingMethod = decoder.decodeOptional(TlvTag.SigningMethod), + pauseBeforePin2 = decoder.decodeOptional(TlvTag.PauseBeforePin2), + walletPublicKey = decoder.decodeOptional(TlvTag.WalletPublicKey), + walletRemainingSignatures = decoder.decodeOptional(TlvTag.RemainingSignatures), + walletSignedHashes = decoder.decodeOptional(TlvTag.SignedHashes), + health = decoder.decodeOptional(TlvTag.Health), + isActivated = decoder.decode(TlvTag.IsActivated), + activationSeed = decoder.decodeOptional(TlvTag.ActivationSeed), + paymentFlowVersion = decoder.decodeOptional(TlvTag.PaymentFlowVersion), + userCounter = decoder.decodeOptional(TlvTag.UserCounter), + userProtectedCounter = decoder.decodeOptional(TlvTag.UserProtectedCounter), + terminalIsLinked = decoder.decode(TlvTag.TerminalIsLinked), - cardData = deserializeCardData(tlvData) - ) - } catch (exception: Exception) { - throw TaskError.SerializeCommandError() - } + cardData = deserializeCardData(tlvData) + ) } private fun deserializeCardData(tlvData: List): CardData? { @@ -433,18 +438,18 @@ class ReadCommand : CommandSerializer() { } if (cardDataTlvs.isNullOrEmpty()) return null - val tlvMapper = TlvMapper(cardDataTlvs) + val decoder = TlvDecoder(cardDataTlvs) return CardData( - batchId = tlvMapper.mapOptional(TlvTag.Batch), - manufactureDateTime = tlvMapper.mapOptional(TlvTag.ManufactureDateTime), - issuerName = tlvMapper.mapOptional(TlvTag.IssuerId), - blockchainName = tlvMapper.mapOptional(TlvTag.BlockchainId), - manufacturerSignature = tlvMapper.mapOptional(TlvTag.ManufacturerSignature), - productMask = tlvMapper.mapOptional(TlvTag.ProductMask), + batchId = decoder.decodeOptional(TlvTag.Batch), + manufactureDateTime = decoder.decodeOptional(TlvTag.ManufactureDateTime), + issuerName = decoder.decodeOptional(TlvTag.IssuerId), + blockchainName = decoder.decodeOptional(TlvTag.BlockchainId), + manufacturerSignature = decoder.decodeOptional(TlvTag.ManufacturerSignature), + productMask = decoder.decodeOptional(TlvTag.ProductMask), - tokenSymbol = tlvMapper.mapOptional(TlvTag.TokenSymbol), - tokenContractAddress = tlvMapper.mapOptional(TlvTag.TokenContractAddress), - tokenDecimal = tlvMapper.mapOptional(TlvTag.TokenDecimal) + tokenSymbol = decoder.decodeOptional(TlvTag.TokenSymbol), + tokenContractAddress = decoder.decodeOptional(TlvTag.TokenContractAddress), + tokenDecimal = decoder.decodeOptional(TlvTag.TokenDecimal) ) } } \ No newline at end of file diff --git a/tangem-core/src/main/java/com/tangem/commands/ReadIssuerDataCommand.kt b/tangem-core/src/main/java/com/tangem/commands/ReadIssuerDataCommand.kt index cb95fd0899..3909e0cbb6 100644 --- a/tangem-core/src/main/java/com/tangem/commands/ReadIssuerDataCommand.kt +++ b/tangem-core/src/main/java/com/tangem/commands/ReadIssuerDataCommand.kt @@ -1,16 +1,19 @@ package com.tangem.commands +import com.tangem.CardEnvironment +import com.tangem.CardSession +import com.tangem.SessionError import com.tangem.commands.common.DefaultIssuerDataVerifier import com.tangem.commands.common.IssuerDataMode +import com.tangem.commands.common.IssuerDataToVerify import com.tangem.commands.common.IssuerDataVerifier -import com.tangem.common.CardEnvironment +import com.tangem.common.CompletionResult import com.tangem.common.apdu.CommandApdu import com.tangem.common.apdu.Instruction import com.tangem.common.apdu.ResponseApdu import com.tangem.common.tlv.TlvBuilder -import com.tangem.common.tlv.TlvMapper +import com.tangem.common.tlv.TlvDecoder import com.tangem.common.tlv.TlvTag -import com.tangem.tasks.TaskError class ReadIssuerDataResponse( @@ -51,34 +54,63 @@ class ReadIssuerDataResponse( * @property cardId CID, Unique Tangem card ID number. */ class ReadIssuerDataCommand( + val issuerPublicKey: ByteArray? = null, verifier: IssuerDataVerifier = DefaultIssuerDataVerifier() -) : CommandSerializer(), - IssuerDataVerifier by verifier { +) : Command(), IssuerDataVerifier by verifier { - override fun serialize(cardEnvironment: CardEnvironment): CommandApdu { + override fun run(session: CardSession, callback: (result: CompletionResult) -> Unit) { + val card = session.environment.card + if (card == null) { + callback(CompletionResult.Failure(SessionError.MissingPreflightRead())) + return + } + val publicKey = issuerPublicKey ?: card.issuerPublicKey + if (publicKey == null) { + callback(CompletionResult.Failure(SessionError.MissingIssuerPubicKey())) + return + } + super.run(session) { result -> + when (result) { + is CompletionResult.Failure -> callback(result) + is CompletionResult.Success -> { + if (result.data.issuerData.isEmpty()) { + callback(result) + return@run + } + val issuerDataToVerify = IssuerDataToVerify( + card.cardId, result.data.issuerData, result.data.issuerDataCounter + ) + if (verify(publicKey, result.data.issuerDataSignature, issuerDataToVerify)) { + callback(result) + } else { + callback(CompletionResult.Failure(SessionError.VerificationFailed())) + } + } + } + } + } + + override fun serialize(environment: CardEnvironment): CommandApdu { val tlvBuilder = TlvBuilder() - tlvBuilder.append(TlvTag.Pin, cardEnvironment.pin1) - tlvBuilder.append(TlvTag.CardId, cardEnvironment.cardId) + tlvBuilder.append(TlvTag.Pin, environment.pin1) + tlvBuilder.append(TlvTag.CardId, environment.card?.cardId) tlvBuilder.append(TlvTag.Mode, IssuerDataMode.ReadData) return CommandApdu( Instruction.ReadIssuerData, tlvBuilder.serialize(), - cardEnvironment.encryptionMode, cardEnvironment.encryptionKey + environment.encryptionMode, environment.encryptionKey ) } - override fun deserialize(cardEnvironment: CardEnvironment, responseApdu: ResponseApdu): ReadIssuerDataResponse? { - val tlvData = responseApdu.getTlvData(cardEnvironment.encryptionKey) ?: return null + override fun deserialize(environment: CardEnvironment, apdu: ResponseApdu): ReadIssuerDataResponse { + val tlvData = apdu.getTlvData(environment.encryptionKey) + ?: throw SessionError.DeserializeApduFailed() - return try { - val mapper = TlvMapper(tlvData) - ReadIssuerDataResponse( - cardId = mapper.map(TlvTag.CardId), - issuerData = mapper.map(TlvTag.IssuerData), - issuerDataSignature = mapper.map(TlvTag.IssuerDataSignature), - issuerDataCounter = mapper.mapOptional(TlvTag.IssuerDataCounter) - ) - } catch (exception: Exception) { - throw TaskError.SerializeCommandError() - } + val decoder = TlvDecoder(tlvData) + return ReadIssuerDataResponse( + cardId = decoder.decode(TlvTag.CardId), + issuerData = decoder.decode(TlvTag.IssuerData), + issuerDataSignature = decoder.decode(TlvTag.IssuerDataSignature), + issuerDataCounter = decoder.decodeOptional(TlvTag.IssuerDataCounter) + ) } } \ No newline at end of file diff --git a/tangem-core/src/main/java/com/tangem/commands/ReadIssuerExtraDataCommand.kt b/tangem-core/src/main/java/com/tangem/commands/ReadIssuerExtraDataCommand.kt index 5672003477..83120f9c41 100644 --- a/tangem-core/src/main/java/com/tangem/commands/ReadIssuerExtraDataCommand.kt +++ b/tangem-core/src/main/java/com/tangem/commands/ReadIssuerExtraDataCommand.kt @@ -1,16 +1,20 @@ package com.tangem.commands +import com.tangem.CardEnvironment +import com.tangem.CardSession +import com.tangem.SessionError import com.tangem.commands.common.DefaultIssuerDataVerifier import com.tangem.commands.common.IssuerDataMode +import com.tangem.commands.common.IssuerDataToVerify import com.tangem.commands.common.IssuerDataVerifier -import com.tangem.common.CardEnvironment +import com.tangem.common.CompletionResult import com.tangem.common.apdu.CommandApdu import com.tangem.common.apdu.Instruction import com.tangem.common.apdu.ResponseApdu import com.tangem.common.tlv.TlvBuilder -import com.tangem.common.tlv.TlvMapper +import com.tangem.common.tlv.TlvDecoder import com.tangem.common.tlv.TlvTag -import com.tangem.tasks.TaskError +import java.io.ByteArrayOutputStream class ReadIssuerExtraDataResponse( @@ -56,38 +60,113 @@ class ReadIssuerExtraDataResponse( * a series of these commands have to be executed to read the entire Issuer_Extra_Data. */ class ReadIssuerExtraDataCommand( + private val issuerPublicKey: ByteArray? = null, verifier: IssuerDataVerifier = DefaultIssuerDataVerifier() -) : CommandSerializer(), IssuerDataVerifier by verifier { +) : Command(), IssuerDataVerifier by verifier { - var offset: Int = 0 + private val issuerData = ByteArrayOutputStream() + private var offset: Int = 0 + private var issuerDataSize: Int = 0 - override fun serialize(cardEnvironment: CardEnvironment): CommandApdu { + override fun run(session: CardSession, callback: (result: CompletionResult) -> Unit) { + val card = session.environment.card + if (card == null) { + callback(CompletionResult.Failure(SessionError.MissingPreflightRead())) + return + } + val publicKey = issuerPublicKey ?: card.issuerPublicKey + if (publicKey == null) { + callback(CompletionResult.Failure(SessionError.MissingIssuerPubicKey())) + return + } + + readIssuerData(session, card.cardId, publicKey, callback) + } + + + private fun readIssuerData( + session: CardSession, + cardId: String, publicKey: ByteArray, + callback: (result: CompletionResult) -> Unit) { + + if (issuerDataSize != 0) { + session.viewDelegate.onDelay( + issuerDataSize, offset, WriteIssuerExtraDataCommand.SINGLE_WRITE_SIZE + ) + } + + transceive(session) { result -> + when (result) { + is CompletionResult.Success -> { + if (result.data.size != null) { + if (result.data.size == 0) { + callback(CompletionResult.Success(result.data)) + return@transceive + } + issuerDataSize = result.data.size + } + issuerData.write(result.data.issuerData) + if (result.data.issuerDataSignature == null) { + offset = issuerData.size() + readIssuerData(session, cardId, publicKey, callback) + } else { + completeTask(result.data, cardId, publicKey, callback) + } + } + is CompletionResult.Failure -> { + callback(CompletionResult.Failure(result.error)) + } + } + } + } + + private fun completeTask(data: ReadIssuerExtraDataResponse, + cardId: String, publicKey: ByteArray, + callback: (result: CompletionResult) -> Unit) { + val dataToVerify = IssuerDataToVerify( + cardId, + issuerData.toByteArray(), + data.issuerDataCounter + ) + if (verify(publicKey, data.issuerDataSignature!!, dataToVerify)) { + val finalResult = ReadIssuerExtraDataResponse( + data.cardId, + issuerDataSize, + issuerData.toByteArray(), + data.issuerDataSignature, + data.issuerDataCounter + ) + callback(CompletionResult.Success(finalResult)) + } else { + callback(CompletionResult.Failure(SessionError.VerificationFailed())) + } + } + + override fun serialize(environment: CardEnvironment): CommandApdu { val tlvBuilder = TlvBuilder() - tlvBuilder.append(TlvTag.Pin, cardEnvironment.pin1) - tlvBuilder.append(TlvTag.CardId, cardEnvironment.cardId) + tlvBuilder.append(TlvTag.Pin, environment.pin1) + tlvBuilder.append(TlvTag.CardId, environment.card?.cardId) tlvBuilder.append(TlvTag.Mode, IssuerDataMode.ReadExtraData) tlvBuilder.append(TlvTag.Offset, offset) return CommandApdu( Instruction.ReadIssuerData, tlvBuilder.serialize(), - cardEnvironment.encryptionMode, cardEnvironment.encryptionKey + environment.encryptionMode, environment.encryptionKey ) } - override fun deserialize(cardEnvironment: CardEnvironment, responseApdu: ResponseApdu): ReadIssuerExtraDataResponse? { - val tlvData = responseApdu.getTlvData(cardEnvironment.encryptionKey) ?: return null + override fun deserialize(environment: CardEnvironment, apdu: ResponseApdu): ReadIssuerExtraDataResponse { + val tlvData = apdu.getTlvData(environment.encryptionKey) + ?: throw SessionError.DeserializeApduFailed() - return try { - val mapper = TlvMapper(tlvData) - ReadIssuerExtraDataResponse( - cardId = mapper.map(TlvTag.CardId), - size = mapper.mapOptional(TlvTag.Size), - issuerData = mapper.mapOptional(TlvTag.IssuerData) ?: byteArrayOf(), - issuerDataSignature = mapper.mapOptional(TlvTag.IssuerDataSignature), - issuerDataCounter = mapper.mapOptional(TlvTag.IssuerDataCounter) - ) - } catch (exception: Exception) { - throw TaskError.SerializeCommandError() - } + + val decoder = TlvDecoder(tlvData) + return ReadIssuerExtraDataResponse( + cardId = decoder.decode(TlvTag.CardId), + size = decoder.decodeOptional(TlvTag.Size), + issuerData = decoder.decodeOptional(TlvTag.IssuerData) ?: byteArrayOf(), + issuerDataSignature = decoder.decodeOptional(TlvTag.IssuerDataSignature), + issuerDataCounter = decoder.decodeOptional(TlvTag.IssuerDataCounter) + ) } companion object { diff --git a/tangem-core/src/main/java/com/tangem/commands/ReadUserDataCommand.kt b/tangem-core/src/main/java/com/tangem/commands/ReadUserDataCommand.kt index 656c1750f0..7209d424df 100644 --- a/tangem-core/src/main/java/com/tangem/commands/ReadUserDataCommand.kt +++ b/tangem-core/src/main/java/com/tangem/commands/ReadUserDataCommand.kt @@ -1,13 +1,13 @@ package com.tangem.commands -import com.tangem.common.CardEnvironment +import com.tangem.CardEnvironment +import com.tangem.SessionError import com.tangem.common.apdu.CommandApdu import com.tangem.common.apdu.Instruction import com.tangem.common.apdu.ResponseApdu import com.tangem.common.tlv.TlvBuilder -import com.tangem.common.tlv.TlvMapper +import com.tangem.common.tlv.TlvDecoder import com.tangem.common.tlv.TlvTag -import com.tangem.tasks.TaskError class ReadUserDataResponse( /** @@ -47,33 +47,30 @@ class ReadUserDataResponse( * of new transaction (on SIGN command that calculate new signatures). The App defines purpose of use. * For example, this fields may contain blockchain nonce value. */ -class ReadUserDataCommand: CommandSerializer() { +class ReadUserDataCommand: Command() { - override fun serialize(cardEnvironment: CardEnvironment): CommandApdu { + override fun serialize(environment: CardEnvironment): CommandApdu { val builder = TlvBuilder() - builder.append(TlvTag.CardId, cardEnvironment.cardId) - builder.append(TlvTag.Pin, cardEnvironment.pin1) + builder.append(TlvTag.CardId, environment.card?.cardId) + builder.append(TlvTag.Pin, environment.pin1) return CommandApdu( Instruction.ReadUserData, builder.serialize(), - cardEnvironment.encryptionMode, cardEnvironment.encryptionKey + environment.encryptionMode, environment.encryptionKey ) } - override fun deserialize(cardEnvironment: CardEnvironment, responseApdu: ResponseApdu): ReadUserDataResponse? { - val tlvData = responseApdu.getTlvData(cardEnvironment.encryptionKey) ?: return null + override fun deserialize(environment: CardEnvironment, apdu: ResponseApdu): ReadUserDataResponse { + val tlvData = apdu.getTlvData(environment.encryptionKey) + ?: throw SessionError.DeserializeApduFailed() - return try { - val mapper = TlvMapper(tlvData) - ReadUserDataResponse( - cardId = mapper.map(TlvTag.CardId), - userData = mapper.map(TlvTag.UserData), - userProtectedData = mapper.map(TlvTag.UserProtectedData), - userCounter = mapper.map(TlvTag.UserCounter), - userProtectedCounter = mapper.map(TlvTag.UserProtectedCounter) + val decoder = TlvDecoder(tlvData) + return ReadUserDataResponse( + cardId = decoder.decode(TlvTag.CardId), + userData = decoder.decode(TlvTag.UserData), + userProtectedData = decoder.decode(TlvTag.UserProtectedData), + userCounter = decoder.decode(TlvTag.UserCounter), + userProtectedCounter = decoder.decode(TlvTag.UserProtectedCounter) ) - } catch (exception: Exception) { - throw TaskError.SerializeCommandError() - } } } \ No newline at end of file diff --git a/tangem-core/src/main/java/com/tangem/commands/SignCommand.kt b/tangem-core/src/main/java/com/tangem/commands/SignCommand.kt index 6f0b7bfacc..0a48b022d6 100644 --- a/tangem-core/src/main/java/com/tangem/commands/SignCommand.kt +++ b/tangem-core/src/main/java/com/tangem/commands/SignCommand.kt @@ -1,14 +1,14 @@ package com.tangem.commands -import com.tangem.common.CardEnvironment +import com.tangem.CardEnvironment +import com.tangem.SessionError import com.tangem.common.apdu.CommandApdu import com.tangem.common.apdu.Instruction import com.tangem.common.apdu.ResponseApdu import com.tangem.common.tlv.TlvBuilder -import com.tangem.common.tlv.TlvMapper +import com.tangem.common.tlv.TlvDecoder import com.tangem.common.tlv.TlvTag import com.tangem.crypto.sign -import com.tangem.tasks.TaskError /** * @param cardId CID, Unique Tangem card ID number @@ -31,10 +31,26 @@ class SignResponse( * @property cardId CID, Unique Tangem card ID number */ class SignCommand(private val hashes: Array) - : CommandSerializer() { + : Command() { private val hashSizes = if (hashes.isNotEmpty()) hashes.first().size else 0 - private val dataToSign = flattenHashes() + + override fun serialize(environment: CardEnvironment): CommandApdu { + val dataToSign = flattenHashes() + val tlvBuilder = TlvBuilder() + tlvBuilder.append(TlvTag.Pin, environment.pin1) + tlvBuilder.append(TlvTag.Pin2, environment.pin2) + tlvBuilder.append(TlvTag.CardId, environment.card?.cardId) + tlvBuilder.append(TlvTag.TransactionOutHashSize, byteArrayOf(hashSizes.toByte())) + tlvBuilder.append(TlvTag.TransactionOutHash, dataToSign) + tlvBuilder.append(TlvTag.Cvc, environment.cvc) + + addTerminalSignature(environment, dataToSign, tlvBuilder) + return CommandApdu( + Instruction.Sign, tlvBuilder.serialize(), + environment.encryptionMode, environment.encryptionKey + ) + } private fun flattenHashes(): ByteArray { checkForErrors() @@ -42,25 +58,9 @@ class SignCommand(private val hashes: Array) } private fun checkForErrors() { - if (hashes.isEmpty()) throw TaskError.EmptyHashes() - if (hashes.size > 10) throw TaskError.TooMuchHashesInOneTransaction() - if (hashes.any { it.size != hashSizes }) throw TaskError.HashSizeMustBeEqual() - } - - override fun serialize(cardEnvironment: CardEnvironment): CommandApdu { - val tlvBuilder = TlvBuilder() - tlvBuilder.append(TlvTag.Pin, cardEnvironment.pin1) - tlvBuilder.append(TlvTag.Pin2, cardEnvironment.pin2) - tlvBuilder.append(TlvTag.CardId, cardEnvironment.cardId) - tlvBuilder.append(TlvTag.TransactionOutHashSize, byteArrayOf(hashSizes.toByte())) - tlvBuilder.append(TlvTag.TransactionOutHash, dataToSign) - tlvBuilder.append(TlvTag.Cvc, cardEnvironment.cvc) - - addTerminalSignature(cardEnvironment, tlvBuilder) - return CommandApdu( - Instruction.Sign, tlvBuilder.serialize(), - cardEnvironment.encryptionMode, cardEnvironment.encryptionKey - ) + if (hashes.isEmpty()) throw SessionError.EmptyHashes() + if (hashes.size > 10) throw SessionError.TooMuchHashesInOneTransaction() + if (hashes.any { it.size != hashSizes }) throw SessionError.HashSizeMustBeEqual() } /** @@ -70,23 +70,25 @@ class SignCommand(private val hashes: Array) * TerminalTransactionSignature parameter containing a correct signature of raw data to be signed made with TerminalPrivateKey * (this key should be generated and securily stored by the application). */ - private fun addTerminalSignature(cardEnvironment: CardEnvironment, tlvBuilder: TlvBuilder) { - cardEnvironment.terminalKeys?.let { terminalKeyPair -> + private fun addTerminalSignature( + environment: CardEnvironment, dataToSign: ByteArray, tlvBuilder: TlvBuilder) { + environment.terminalKeys?.let { terminalKeyPair -> val signedData = dataToSign.sign(terminalKeyPair.privateKey) tlvBuilder.append(TlvTag.TerminalTransactionSignature, signedData) tlvBuilder.append(TlvTag.TerminalPublicKey, terminalKeyPair.publicKey) } } - override fun deserialize(cardEnvironment: CardEnvironment, responseApdu: ResponseApdu): SignResponse? { - val tlvData = responseApdu.getTlvData(cardEnvironment.encryptionKey) ?: return null + override fun deserialize(environment: CardEnvironment, apdu: ResponseApdu): SignResponse { + val tlvData = apdu.getTlvData(environment.encryptionKey) + ?: throw SessionError.DeserializeApduFailed() - val tlvMapper = TlvMapper(tlvData) + val decoder = TlvDecoder(tlvData) return SignResponse( - cardId = tlvMapper.map(TlvTag.CardId), - signature = tlvMapper.map(TlvTag.Signature), - walletRemainingSignatures = tlvMapper.map(TlvTag.RemainingSignatures), - walletSignedHashes = tlvMapper.map(TlvTag.SignedHashes) + cardId = decoder.decode(TlvTag.CardId), + signature = decoder.decode(TlvTag.Signature), + walletRemainingSignatures = decoder.decode(TlvTag.RemainingSignatures), + walletSignedHashes = decoder.decode(TlvTag.SignedHashes) ) } } \ No newline at end of file diff --git a/tangem-core/src/main/java/com/tangem/commands/WriteIssuerDataCommand.kt b/tangem-core/src/main/java/com/tangem/commands/WriteIssuerDataCommand.kt index dbc7f15640..0345eb300f 100644 --- a/tangem-core/src/main/java/com/tangem/commands/WriteIssuerDataCommand.kt +++ b/tangem-core/src/main/java/com/tangem/commands/WriteIssuerDataCommand.kt @@ -1,16 +1,19 @@ package com.tangem.commands +import com.tangem.CardEnvironment +import com.tangem.CardSession +import com.tangem.SessionError import com.tangem.commands.common.DefaultIssuerDataVerifier import com.tangem.commands.common.IssuerDataMode +import com.tangem.commands.common.IssuerDataToVerify import com.tangem.commands.common.IssuerDataVerifier -import com.tangem.common.CardEnvironment +import com.tangem.common.CompletionResult import com.tangem.common.apdu.CommandApdu import com.tangem.common.apdu.Instruction import com.tangem.common.apdu.ResponseApdu import com.tangem.common.tlv.TlvBuilder -import com.tangem.common.tlv.TlvMapper +import com.tangem.common.tlv.TlvDecoder import com.tangem.common.tlv.TlvTag -import com.tangem.tasks.TaskError class WriteIssuerDataResponse( /** @@ -33,13 +36,50 @@ class WriteIssuerDataCommand( private val issuerData: ByteArray, private val issuerDataSignature: ByteArray, private val issuerDataCounter: Int? = null, + private val issuerPublicKey: ByteArray? = null, verifier: IssuerDataVerifier = DefaultIssuerDataVerifier() -) : CommandSerializer(), IssuerDataVerifier by verifier { +) : Command(), IssuerDataVerifier by verifier { - override fun serialize(cardEnvironment: CardEnvironment): CommandApdu { + override fun run(session: CardSession, callback: (result: CompletionResult) -> Unit) { + + val card = session.environment.card + if (card == null) { + callback(CompletionResult.Failure(SessionError.MissingPreflightRead())) + return + } + val publicKey = issuerPublicKey ?: card.issuerPublicKey + if (publicKey == null) { + callback(CompletionResult.Failure(SessionError.MissingIssuerPubicKey())) + return + } + + if (!isCounterValid(issuerDataCounter, card)) { + callback(CompletionResult.Failure(SessionError.MissingCounter())) + } else if (!verifySignature(publicKey, card.cardId)) { + callback(CompletionResult.Failure(SessionError.VerificationFailed())) + } else { + super.run(session, callback) + } + } + + private fun isCounterValid(issuerDataCounter: Int?, card: Card): Boolean = + if (isCounterRequired(card)) issuerDataCounter != null else true + + private fun isCounterRequired(card: Card): Boolean = + card.settingsMask?.contains(Settings.ProtectIssuerDataAgainstReplay) != false + + private fun verifySignature(publicKey: ByteArray, cardId: String): Boolean { + return verify( + publicKey, + issuerDataSignature, + IssuerDataToVerify(cardId, issuerData, issuerDataCounter) + ) + } + + override fun serialize(environment: CardEnvironment): CommandApdu { val tlvBuilder = TlvBuilder() - tlvBuilder.append(TlvTag.Pin, cardEnvironment.pin1) - tlvBuilder.append(TlvTag.CardId, cardEnvironment.cardId) + tlvBuilder.append(TlvTag.Pin, environment.pin1) + tlvBuilder.append(TlvTag.CardId, environment.card?.cardId) tlvBuilder.append(TlvTag.Mode, IssuerDataMode.WriteData) tlvBuilder.append(TlvTag.IssuerData, issuerData) tlvBuilder.append(TlvTag.IssuerDataSignature, issuerDataSignature) @@ -47,20 +87,17 @@ class WriteIssuerDataCommand( return CommandApdu( Instruction.WriteIssuerData, tlvBuilder.serialize(), - cardEnvironment.encryptionMode, cardEnvironment.encryptionKey + environment.encryptionMode, environment.encryptionKey ) } - override fun deserialize(cardEnvironment: CardEnvironment, responseApdu: ResponseApdu): WriteIssuerDataResponse? { - val tlvData = responseApdu.getTlvData(cardEnvironment.encryptionKey) ?: return null + override fun deserialize(environment: CardEnvironment, apdu: ResponseApdu): WriteIssuerDataResponse { + val tlvData = apdu.getTlvData(environment.encryptionKey) + ?: throw SessionError.DeserializeApduFailed() - return try { - val mapper = TlvMapper(tlvData) - WriteIssuerDataResponse( - cardId = mapper.map(TlvTag.CardId) - ) - } catch (exception: Exception) { - throw TaskError.SerializeCommandError() - } + val decoder = TlvDecoder(tlvData) + return WriteIssuerDataResponse( + cardId = decoder.decode(TlvTag.CardId) + ) } } \ No newline at end of file diff --git a/tangem-core/src/main/java/com/tangem/commands/WriteIssuerExtraDataCommand.kt b/tangem-core/src/main/java/com/tangem/commands/WriteIssuerExtraDataCommand.kt index 753607f0a9..29bf3cd0d5 100644 --- a/tangem-core/src/main/java/com/tangem/commands/WriteIssuerExtraDataCommand.kt +++ b/tangem-core/src/main/java/com/tangem/commands/WriteIssuerExtraDataCommand.kt @@ -1,16 +1,19 @@ package com.tangem.commands +import com.tangem.CardEnvironment +import com.tangem.CardSession +import com.tangem.SessionError import com.tangem.commands.common.DefaultIssuerDataVerifier import com.tangem.commands.common.IssuerDataMode +import com.tangem.commands.common.IssuerDataToVerify import com.tangem.commands.common.IssuerDataVerifier -import com.tangem.common.CardEnvironment +import com.tangem.common.CompletionResult import com.tangem.common.apdu.CommandApdu import com.tangem.common.apdu.Instruction import com.tangem.common.apdu.ResponseApdu import com.tangem.common.tlv.TlvBuilder -import com.tangem.common.tlv.TlvMapper +import com.tangem.common.tlv.TlvDecoder import com.tangem.common.tlv.TlvTag -import com.tangem.tasks.TaskError /** * This command writes Issuer Extra Data field and its issuer’s signature. @@ -33,17 +36,94 @@ class WriteIssuerExtraDataCommand( private val startingSignature: ByteArray, private val finalizingSignature: ByteArray, private val issuerDataCounter: Int? = null, + private val issuerPublicKey: ByteArray? = null, verifier: IssuerDataVerifier = DefaultIssuerDataVerifier() -) : CommandSerializer(), IssuerDataVerifier by verifier { +) : Command(), IssuerDataVerifier by verifier { var mode: IssuerDataMode = IssuerDataMode.InitializeWritingExtraData var offset: Int = 0 - override fun serialize(cardEnvironment: CardEnvironment): CommandApdu { + override fun run(session: CardSession, callback: (result: CompletionResult) -> Unit) { + val card = session.environment.card + if (card == null) { + callback(CompletionResult.Failure(SessionError.MissingPreflightRead())) + return + } + val publicKey = issuerPublicKey ?: card.issuerPublicKey + if (publicKey == null) { + callback(CompletionResult.Failure(SessionError.MissingIssuerPubicKey())) + return + } + + if (!isCounterValid(issuerDataCounter, card)) { + callback(CompletionResult.Failure(SessionError.MissingCounter())) + } else if (!verifySignatures(card.cardId, publicKey)) { + callback(CompletionResult.Failure(SessionError.VerificationFailed())) + } else { + writeIssuerData(session, card.cardId, publicKey, callback) + } + } + + private fun isCounterValid(issuerDataCounter: Int?, card: Card): Boolean = + if (isCounterRequired(card)) issuerDataCounter != null else true + + private fun isCounterRequired(card: Card): Boolean = + card.settingsMask?.contains(Settings.ProtectIssuerDataAgainstReplay) != false + + private fun verifySignatures(cardId: String, publicKey: ByteArray): Boolean { + + val firstData = IssuerDataToVerify(cardId, null, issuerDataCounter, issuerData.size) + val secondData = IssuerDataToVerify(cardId, issuerData, issuerDataCounter) + + return verify(publicKey, startingSignature, firstData) && + verify(publicKey, finalizingSignature, secondData) + } + + private fun writeIssuerData( + session: CardSession, + cardId: String, publicKey: ByteArray, + callback: (result: CompletionResult) -> Unit + ) { + + if (mode == IssuerDataMode.WriteExtraData) { + session.viewDelegate.onDelay(issuerData.size, offset, WriteIssuerExtraDataCommand.SINGLE_WRITE_SIZE) + } + transceive(session) { result -> + when (result) { + is CompletionResult.Success -> { + when (mode) { + IssuerDataMode.InitializeWritingExtraData -> { + mode = IssuerDataMode.WriteExtraData + writeIssuerData(session, cardId, publicKey, callback) + return@transceive + } + IssuerDataMode.WriteExtraData -> { + offset += WriteIssuerExtraDataCommand.SINGLE_WRITE_SIZE + if (offset >= issuerData.size) { + mode = IssuerDataMode.FinalizeExtraData + } + writeIssuerData(session, cardId, publicKey, callback) + return@transceive + } + IssuerDataMode.FinalizeExtraData -> { + callback(CompletionResult.Success(result.data)) + } + } + } + is CompletionResult.Failure -> { + callback(CompletionResult.Failure(result.error)) + } + } + } + + + } + + override fun serialize(environment: CardEnvironment): CommandApdu { val tlvBuilder = TlvBuilder() - tlvBuilder.append(TlvTag.Pin, cardEnvironment.pin1) - tlvBuilder.append(TlvTag.CardId, cardEnvironment.cardId) + tlvBuilder.append(TlvTag.Pin, environment.pin1) + tlvBuilder.append(TlvTag.CardId, environment.card?.cardId) tlvBuilder.append(TlvTag.Mode, mode) when (mode) { @@ -62,7 +142,7 @@ class WriteIssuerExtraDataCommand( } return CommandApdu( Instruction.WriteIssuerData, tlvBuilder.serialize(), - cardEnvironment.encryptionMode, cardEnvironment.encryptionKey + environment.encryptionMode, environment.encryptionKey ) } @@ -74,17 +154,12 @@ class WriteIssuerExtraDataCommand( return if (bytesLeft < SINGLE_WRITE_SIZE) bytesLeft else SINGLE_WRITE_SIZE } - override fun deserialize(cardEnvironment: CardEnvironment, responseApdu: ResponseApdu): WriteIssuerDataResponse? { - val tlvData = responseApdu.getTlvData(cardEnvironment.encryptionKey) ?: return null + override fun deserialize(environment: CardEnvironment, apdu: ResponseApdu): WriteIssuerDataResponse { + val tlvData = apdu.getTlvData(environment.encryptionKey) + ?: throw SessionError.DeserializeApduFailed() - return try { - val mapper = TlvMapper(tlvData) - WriteIssuerDataResponse( - cardId = mapper.map(TlvTag.CardId) - ) - } catch (exception: Exception) { - throw TaskError.SerializeCommandError() - } + return WriteIssuerDataResponse(cardId = TlvDecoder(tlvData).decode(TlvTag.CardId) + ) } companion object { diff --git a/tangem-core/src/main/java/com/tangem/commands/WriteUserDataCommand.kt b/tangem-core/src/main/java/com/tangem/commands/WriteUserDataCommand.kt index ac79cba45c..912c57f7e6 100644 --- a/tangem-core/src/main/java/com/tangem/commands/WriteUserDataCommand.kt +++ b/tangem-core/src/main/java/com/tangem/commands/WriteUserDataCommand.kt @@ -1,20 +1,20 @@ package com.tangem.commands -import com.tangem.common.CardEnvironment +import com.tangem.CardEnvironment +import com.tangem.SessionError import com.tangem.common.apdu.CommandApdu import com.tangem.common.apdu.Instruction import com.tangem.common.apdu.ResponseApdu import com.tangem.common.tlv.TlvBuilder -import com.tangem.common.tlv.TlvMapper +import com.tangem.common.tlv.TlvDecoder import com.tangem.common.tlv.TlvTag -import com.tangem.tasks.TaskError class WriteUserDataResponse( - /** - * CID, Unique Tangem card ID number. - */ - val cardId: String -): CommandResponse + /** + * CID, Unique Tangem card ID number. + */ + val cardId: String +) : CommandResponse /** * This command write some of User_Data, User_ProtectedData, User_Counter and User_ProtectedCounter fields. @@ -29,33 +29,29 @@ class WriteUserDataResponse( * User_ProtectedCounter and User_ProtectedData additionaly need PIN2 to confirmation. */ class WriteUserDataCommand(private val userData: ByteArray? = null, private val userProtectedData: ByteArray? = null, - private val userCounter: Int? = null, - private val userProtectedCounter: Int? = null): CommandSerializer() { + private val userCounter: Int? = null, + private val userProtectedCounter: Int? = null) : Command() { - override fun serialize(cardEnvironment: CardEnvironment): CommandApdu { - val builder = TlvBuilder() - builder.append(TlvTag.CardId, cardEnvironment.cardId) - builder.append(TlvTag.Pin, cardEnvironment.pin1) - builder.append(TlvTag.UserData, userData) - builder.append(TlvTag.UserCounter, userCounter) - builder.append(TlvTag.UserProtectedData, userProtectedData) - builder.append(TlvTag.UserProtectedCounter, userProtectedCounter) - if (userProtectedCounter != null || userProtectedData != null) - builder.append(TlvTag.Pin2, cardEnvironment.pin2) + override fun serialize(environment: CardEnvironment): CommandApdu { + val builder = TlvBuilder() + builder.append(TlvTag.CardId, environment.card?.cardId) + builder.append(TlvTag.Pin, environment.pin1) + builder.append(TlvTag.UserData, userData) + builder.append(TlvTag.UserCounter, userCounter) + builder.append(TlvTag.UserProtectedData, userProtectedData) + builder.append(TlvTag.UserProtectedCounter, userProtectedCounter) + if (userProtectedCounter != null || userProtectedData != null) + builder.append(TlvTag.Pin2, environment.pin2) - return CommandApdu( - Instruction.WriteUserData, builder.serialize(), - cardEnvironment.encryptionMode, cardEnvironment.encryptionKey - ) - } - - override fun deserialize(cardEnvironment: CardEnvironment, responseApdu: ResponseApdu): WriteUserDataResponse? { - val tlvData = responseApdu.getTlvData(cardEnvironment.encryptionKey) ?: return null - - return try { - WriteUserDataResponse(TlvMapper(tlvData).map(TlvTag.CardId)) - } catch (exception: Exception) { - throw TaskError.SerializeCommandError() + return CommandApdu( + Instruction.WriteUserData, builder.serialize(), + environment.encryptionMode, environment.encryptionKey + ) + } + + override fun deserialize(environment: CardEnvironment, apdu: ResponseApdu): WriteUserDataResponse { + val tlvData = apdu.getTlvData(environment.encryptionKey) + ?: throw SessionError.DeserializeApduFailed() + return WriteUserDataResponse(TlvDecoder(tlvData).decode(TlvTag.CardId)) } - } } \ No newline at end of file diff --git a/tangem-core/src/main/java/com/tangem/commands/personalization/DepersonalizeCommand.kt b/tangem-core/src/main/java/com/tangem/commands/personalization/DepersonalizeCommand.kt index 034b28dce5..2fcd962966 100644 --- a/tangem-core/src/main/java/com/tangem/commands/personalization/DepersonalizeCommand.kt +++ b/tangem-core/src/main/java/com/tangem/commands/personalization/DepersonalizeCommand.kt @@ -1,8 +1,8 @@ package com.tangem.commands.personalization +import com.tangem.CardEnvironment +import com.tangem.commands.Command import com.tangem.commands.CommandResponse -import com.tangem.commands.CommandSerializer -import com.tangem.common.CardEnvironment import com.tangem.common.apdu.CommandApdu import com.tangem.common.apdu.Instruction import com.tangem.common.apdu.ResponseApdu @@ -14,17 +14,16 @@ data class DepersonalizeResponse(val success: Boolean) : CommandResponse * * This command resets card to initial state, * erasing all data written during personalization and usage. - * @param cardId CID, Unique Tangem card ID number. */ -class DepersonalizeCommand : CommandSerializer() { +class DepersonalizeCommand : Command() { - override fun serialize(cardEnvironment: CardEnvironment): CommandApdu { + override fun serialize(environment: CardEnvironment): CommandApdu { return CommandApdu( Instruction.Depersonalize, byteArrayOf() ) } - override fun deserialize(cardEnvironment: CardEnvironment, responseApdu: ResponseApdu): DepersonalizeResponse? { + override fun deserialize(environment: CardEnvironment, apdu: ResponseApdu): DepersonalizeResponse { return DepersonalizeResponse(true) } } \ No newline at end of file diff --git a/tangem-core/src/main/java/com/tangem/commands/personalization/PersonalizeCommand.kt b/tangem-core/src/main/java/com/tangem/commands/personalization/PersonalizeCommand.kt index 035963c21a..2d81d479a2 100644 --- a/tangem-core/src/main/java/com/tangem/commands/personalization/PersonalizeCommand.kt +++ b/tangem-core/src/main/java/com/tangem/commands/personalization/PersonalizeCommand.kt @@ -1,10 +1,11 @@ package com.tangem.commands.personalization +import com.tangem.CardEnvironment +import com.tangem.SessionError import com.tangem.commands.Card import com.tangem.commands.CardData -import com.tangem.commands.CommandSerializer +import com.tangem.commands.Command import com.tangem.commands.personalization.entities.* -import com.tangem.common.CardEnvironment import com.tangem.common.apdu.CommandApdu import com.tangem.common.apdu.Instruction import com.tangem.common.apdu.ResponseApdu @@ -12,10 +13,9 @@ import com.tangem.common.extensions.calculateSha256 import com.tangem.common.extensions.hexToBytes import com.tangem.common.tlv.Tlv import com.tangem.common.tlv.TlvBuilder -import com.tangem.common.tlv.TlvMapper +import com.tangem.common.tlv.TlvDecoder import com.tangem.common.tlv.TlvTag import com.tangem.crypto.sign -import com.tangem.tasks.TaskError /** * Command available on SDK cards only @@ -34,9 +34,9 @@ class PersonalizeCommand( private val config: CardConfig, private val issuer: Issuer, private val manufacturer: Manufacturer, private val acquirer: Acquirer? = null -) : CommandSerializer() { +) : Command() { - override fun serialize(cardEnvironment: CardEnvironment): CommandApdu { + override fun serialize(environment: CardEnvironment): CommandApdu { return CommandApdu( Instruction.Personalize, serializePersonalizationData(config), @@ -44,39 +44,37 @@ class PersonalizeCommand( ) } - override fun deserialize(cardEnvironment: CardEnvironment, responseApdu: ResponseApdu): Card? { - val tlvData = responseApdu.getTlvData(devPersonalizationKey) ?: return null + override fun deserialize(environment: CardEnvironment, apdu: ResponseApdu): Card { + val tlvData = apdu.getTlvData(devPersonalizationKey) + ?: throw SessionError.DeserializeApduFailed() - return try { - val tlvMapper = TlvMapper(tlvData) - Card( - cardId = tlvMapper.mapOptional(TlvTag.CardId) ?: "", - manufacturerName = tlvMapper.mapOptional(TlvTag.ManufactureId) ?: "", - status = tlvMapper.mapOptional(TlvTag.Status), + val decoder = TlvDecoder(tlvData) + return Card( + cardId = decoder.decodeOptional(TlvTag.CardId) ?: "", + manufacturerName = decoder.decodeOptional(TlvTag.ManufactureId) ?: "", + status = decoder.decodeOptional(TlvTag.Status), - firmwareVersion = tlvMapper.mapOptional(TlvTag.Firmware), - cardPublicKey = tlvMapper.mapOptional(TlvTag.CardPublicKey), - settingsMask = tlvMapper.mapOptional(TlvTag.SettingsMask), - issuerPublicKey = tlvMapper.mapOptional(TlvTag.IssuerDataPublicKey), - curve = tlvMapper.mapOptional(TlvTag.CurveId), - maxSignatures = tlvMapper.mapOptional(TlvTag.MaxSignatures), - signingMethod = tlvMapper.mapOptional(TlvTag.SigningMethod), - pauseBeforePin2 = tlvMapper.mapOptional(TlvTag.PauseBeforePin2), - walletPublicKey = tlvMapper.mapOptional(TlvTag.WalletPublicKey), - walletRemainingSignatures = tlvMapper.mapOptional(TlvTag.RemainingSignatures), - walletSignedHashes = tlvMapper.mapOptional(TlvTag.SignedHashes), - health = tlvMapper.mapOptional(TlvTag.Health), - isActivated = tlvMapper.map(TlvTag.IsActivated), - activationSeed = tlvMapper.mapOptional(TlvTag.ActivationSeed), - paymentFlowVersion = tlvMapper.mapOptional(TlvTag.PaymentFlowVersion), - userCounter = tlvMapper.mapOptional(TlvTag.UserCounter), - terminalIsLinked = tlvMapper.map(TlvTag.TerminalIsLinked), + firmwareVersion = decoder.decodeOptional(TlvTag.Firmware), + cardPublicKey = decoder.decodeOptional(TlvTag.CardPublicKey), + settingsMask = decoder.decodeOptional(TlvTag.SettingsMask), + issuerPublicKey = decoder.decodeOptional(TlvTag.IssuerDataPublicKey), + curve = decoder.decodeOptional(TlvTag.CurveId), + maxSignatures = decoder.decodeOptional(TlvTag.MaxSignatures), + signingMethod = decoder.decodeOptional(TlvTag.SigningMethod), + pauseBeforePin2 = decoder.decodeOptional(TlvTag.PauseBeforePin2), + walletPublicKey = decoder.decodeOptional(TlvTag.WalletPublicKey), + walletRemainingSignatures = decoder.decodeOptional(TlvTag.RemainingSignatures), + walletSignedHashes = decoder.decodeOptional(TlvTag.SignedHashes), + health = decoder.decodeOptional(TlvTag.Health), + isActivated = decoder.decode(TlvTag.IsActivated), + activationSeed = decoder.decodeOptional(TlvTag.ActivationSeed), + paymentFlowVersion = decoder.decodeOptional(TlvTag.PaymentFlowVersion), + userCounter = decoder.decodeOptional(TlvTag.UserCounter), + userProtectedCounter = decoder.decodeOptional(TlvTag.UserProtectedCounter), + terminalIsLinked = decoder.decode(TlvTag.TerminalIsLinked), - cardData = deserializeCardData(tlvData) - ) - } catch (exception: Exception) { - throw TaskError.SerializeCommandError() - } + cardData = deserializeCardData(tlvData) + ) } private fun deserializeCardData(tlvData: List): CardData? { @@ -85,23 +83,23 @@ class PersonalizeCommand( } if (cardDataTlvs.isNullOrEmpty()) return null - val tlvMapper = TlvMapper(cardDataTlvs) + val decoder = TlvDecoder(cardDataTlvs) return CardData( - batchId = tlvMapper.mapOptional(TlvTag.Batch), - manufactureDateTime = tlvMapper.mapOptional(TlvTag.ManufactureDateTime), - issuerName = tlvMapper.mapOptional(TlvTag.IssuerId), - blockchainName = tlvMapper.mapOptional(TlvTag.BlockchainId), - manufacturerSignature = tlvMapper.mapOptional(TlvTag.ManufacturerSignature), - productMask = tlvMapper.mapOptional(TlvTag.ProductMask), + batchId = decoder.decodeOptional(TlvTag.Batch), + manufactureDateTime = decoder.decodeOptional(TlvTag.ManufactureDateTime), + issuerName = decoder.decodeOptional(TlvTag.IssuerId), + blockchainName = decoder.decodeOptional(TlvTag.BlockchainId), + manufacturerSignature = decoder.decodeOptional(TlvTag.ManufacturerSignature), + productMask = decoder.decodeOptional(TlvTag.ProductMask), - tokenSymbol = tlvMapper.mapOptional(TlvTag.TokenSymbol), - tokenContractAddress = tlvMapper.mapOptional(TlvTag.TokenContractAddress), - tokenDecimal = tlvMapper.mapOptional(TlvTag.TokenDecimal) + tokenSymbol = decoder.decodeOptional(TlvTag.TokenSymbol), + tokenContractAddress = decoder.decodeOptional(TlvTag.TokenContractAddress), + tokenDecimal = decoder.decodeOptional(TlvTag.TokenDecimal) ) } private fun serializePersonalizationData(config: CardConfig): ByteArray { - val cardId = config.createCardId() ?: throw TaskError.SerializeCommandError() + val cardId = config.createCardId() ?: throw SessionError.SerializeCommandError() val tlvBuilder = TlvBuilder() tlvBuilder.append(TlvTag.CardId, cardId) diff --git a/tangem-core/src/main/java/com/tangem/commands/personalization/entities/Acquirer.kt b/tangem-core/src/main/java/com/tangem/commands/personalization/entities/Acquirer.kt index cda239b145..ec4cff04b0 100644 --- a/tangem-core/src/main/java/com/tangem/commands/personalization/entities/Acquirer.kt +++ b/tangem-core/src/main/java/com/tangem/commands/personalization/entities/Acquirer.kt @@ -1,6 +1,6 @@ package com.tangem.commands.personalization.entities -import com.tangem.common.KeyPair +import com.tangem.KeyPair data class Acquirer( val keyPair: KeyPair, diff --git a/tangem-core/src/main/java/com/tangem/commands/personalization/entities/Issuer.kt b/tangem-core/src/main/java/com/tangem/commands/personalization/entities/Issuer.kt index f82889be6a..ee7ab58fa7 100644 --- a/tangem-core/src/main/java/com/tangem/commands/personalization/entities/Issuer.kt +++ b/tangem-core/src/main/java/com/tangem/commands/personalization/entities/Issuer.kt @@ -1,6 +1,6 @@ package com.tangem.commands.personalization.entities -import com.tangem.common.KeyPair +import com.tangem.KeyPair data class Issuer( val name: String, diff --git a/tangem-core/src/main/java/com/tangem/commands/personalization/entities/Manufacturer.kt b/tangem-core/src/main/java/com/tangem/commands/personalization/entities/Manufacturer.kt index bffdde9384..9e30287bb0 100644 --- a/tangem-core/src/main/java/com/tangem/commands/personalization/entities/Manufacturer.kt +++ b/tangem-core/src/main/java/com/tangem/commands/personalization/entities/Manufacturer.kt @@ -1,6 +1,6 @@ package com.tangem.commands.personalization.entities -import com.tangem.common.KeyPair +import com.tangem.KeyPair data class Manufacturer( val keyPair: KeyPair, diff --git a/tangem-core/src/main/java/com/tangem/common/CompletionResult.kt b/tangem-core/src/main/java/com/tangem/common/CompletionResult.kt index afd76a39e8..71f460c7d7 100644 --- a/tangem-core/src/main/java/com/tangem/common/CompletionResult.kt +++ b/tangem-core/src/main/java/com/tangem/common/CompletionResult.kt @@ -1,7 +1,7 @@ package com.tangem.common +import com.tangem.SessionError import com.tangem.common.CompletionResult.Success -import com.tangem.tasks.TaskError /** * Response class encapsulating successful and failed results. @@ -9,5 +9,5 @@ import com.tangem.tasks.TaskError */ sealed class CompletionResult { class Success(val data: T) : CompletionResult() - class Failure(val error: TaskError) : CompletionResult() + class Failure(val error: SessionError) : CompletionResult() } \ No newline at end of file diff --git a/tangem-core/src/main/java/com/tangem/common/TerminalKeysService.kt b/tangem-core/src/main/java/com/tangem/common/TerminalKeysService.kt index d389c172b3..3ba365247d 100644 --- a/tangem-core/src/main/java/com/tangem/common/TerminalKeysService.kt +++ b/tangem-core/src/main/java/com/tangem/common/TerminalKeysService.kt @@ -1,9 +1,11 @@ package com.tangem.common +import com.tangem.KeyPair + /** * Interface for a service for managing Terminal keypair, used for Linked Terminal feature. - * Its implementation Needs to be provided to [com.tangem.CardManager] - * by calling [com.tangem.CardManager.setTerminalKeysService]. + * Its implementation Needs to be provided to [com.tangem.TangemSdk] + * by calling [com.tangem.TangemSdk.setTerminalKeysService]. * Default implementation is provided in tangem-sdk module: [TerminalKeysStorage]. * Linked Terminal feature can be disabled manually by editing [com.tangem.Config]. */ diff --git a/tangem-core/src/main/java/com/tangem/common/apdu/CommandApdu.kt b/tangem-core/src/main/java/com/tangem/common/apdu/CommandApdu.kt index 1a2c2be076..e2d37df4bb 100644 --- a/tangem-core/src/main/java/com/tangem/common/apdu/CommandApdu.kt +++ b/tangem-core/src/main/java/com/tangem/common/apdu/CommandApdu.kt @@ -1,6 +1,6 @@ package com.tangem.common.apdu -import com.tangem.common.EncryptionMode +import com.tangem.EncryptionMode import com.tangem.common.extensions.calculateCrc16 import com.tangem.common.extensions.toByteArray import com.tangem.crypto.encrypt diff --git a/tangem-core/src/main/java/com/tangem/common/apdu/StatusWord.kt b/tangem-core/src/main/java/com/tangem/common/apdu/StatusWord.kt index a43cfbe969..3938cee65f 100644 --- a/tangem-core/src/main/java/com/tangem/common/apdu/StatusWord.kt +++ b/tangem-core/src/main/java/com/tangem/common/apdu/StatusWord.kt @@ -1,5 +1,7 @@ package com.tangem.common.apdu +import com.tangem.SessionError + /** * Part of a response from the card, shows the status of the operation */ @@ -21,5 +23,18 @@ enum class StatusWord(val code: Int, val description: String) { private val values = values() fun byCode(code: Int): StatusWord = values.find { it.code == code } ?: Unknown } - +} + +fun StatusWord.toSessionError(): SessionError? { + return when (this) { + StatusWord.ProcessCompleted, StatusWord.Pin1Changed, + StatusWord.Pin2Changed, StatusWord.PinsChanged -> null + StatusWord.NeedPause -> null + StatusWord.InvalidParams -> SessionError.InvalidParams() + StatusWord.ErrorProcessingCommand -> SessionError.ErrorProcessingCommand() + StatusWord.InvalidState -> SessionError.InvalidState() + StatusWord.InsNotSupported -> SessionError.InsNotSupported() + StatusWord.NeedEncryption -> SessionError.NeedEncryption() + StatusWord.Unknown -> SessionError.UnknownStatus() + } } diff --git a/tangem-core/src/main/java/com/tangem/common/tlv/TlvMapper.kt b/tangem-core/src/main/java/com/tangem/common/tlv/TlvDecoder.kt similarity index 86% rename from tangem-core/src/main/java/com/tangem/common/tlv/TlvMapper.kt rename to tangem-core/src/main/java/com/tangem/common/tlv/TlvDecoder.kt index 1713fd2e2d..3d63f0e27e 100644 --- a/tangem-core/src/main/java/com/tangem/common/tlv/TlvMapper.kt +++ b/tangem-core/src/main/java/com/tangem/common/tlv/TlvDecoder.kt @@ -1,13 +1,13 @@ package com.tangem.common.tlv import com.tangem.Log +import com.tangem.SessionError import com.tangem.commands.* import com.tangem.commands.common.IssuerDataMode import com.tangem.common.extensions.toDate import com.tangem.common.extensions.toHexString import com.tangem.common.extensions.toInt import com.tangem.common.extensions.toUtf8 -import com.tangem.tasks.TaskError import java.util.* /** @@ -16,7 +16,7 @@ import java.util.* * * @property tlvList List of TLVs, which values are to be converted to particular classes. */ -class TlvMapper(val tlvList: List) { +class TlvDecoder(val tlvList: List) { /** * Finds [Tlv] by its [TlvTag]. @@ -26,10 +26,10 @@ class TlvMapper(val tlvList: List) { * * @return Value converted to a nullable type [T]. */ - inline fun mapOptional(tag: TlvTag): T? = + inline fun decodeOptional(tag: TlvTag): T? = try { - map(tag) - } catch (exception: TaskError.MissingTag) { + decode(tag) + } catch (exception: SessionError.DecodingFailedMissingTag) { null } @@ -44,13 +44,13 @@ class TlvMapper(val tlvList: List) { * * @throws [TaskError.MissingTag] exception if no [Tlv] is found by the Tag. */ - inline fun map(tag: TlvTag): T { + inline fun decode(tag: TlvTag): T { val tlvValue: ByteArray = tlvList.find { it.tag == tag }?.value ?: if (tag.valueType() == TlvValueType.BoolValue && T::class == Boolean::class) { return false as T } else { Log.e(this::class.simpleName!!, "Tag $tag not found") - throw TaskError.MissingTag() + throw SessionError.DecodingFailedMissingTag() } return when (tag.valueType()) { @@ -68,7 +68,7 @@ class TlvMapper(val tlvList: List) { tlvValue.toInt() as T } catch (exception: IllegalArgumentException) { Log.e(this::class.simpleName!!, exception.message ?: "") - throw TaskError.ConvertError() + throw SessionError.DecodingFailed() } } TlvValueType.BoolValue -> { @@ -85,7 +85,7 @@ class TlvMapper(val tlvList: List) { EllipticCurve.byName(tlvValue.toUtf8()) as T } catch (exception: Exception) { logException(tag, tlvValue.toUtf8(), exception) - throw TaskError.ConvertError() + throw SessionError.DecodingFailed() } @@ -96,7 +96,7 @@ class TlvMapper(val tlvList: List) { tlvValue.toDate() as T } catch (exception: Exception) { logException(tag, tlvValue.toHexString(), exception) - throw TaskError.ConvertError() + throw SessionError.DecodingFailed() } } TlvValueType.ProductMask -> { @@ -113,7 +113,7 @@ class TlvMapper(val tlvList: List) { CardStatus.byCode(tlvValue.toInt()) as T } catch (exception: Exception) { logException(tag, tlvValue.toInt().toString(), exception) - throw TaskError.ConvertError() + throw SessionError.DecodingFailed() } } TlvValueType.SigningMethod -> { @@ -122,7 +122,7 @@ class TlvMapper(val tlvList: List) { SigningMethod(tlvValue.toInt()) as T } catch (exception: Exception) { logException(tag, tlvValue.toInt().toString(), exception) - throw TaskError.ConvertError() + throw SessionError.DecodingFailed() } } TlvValueType.IssuerDataMode -> { @@ -131,7 +131,7 @@ class TlvMapper(val tlvList: List) { IssuerDataMode.byCode(tlvValue.toInt().toByte()) as T } catch (exception: Exception) { logException(tag, tlvValue.toInt().toString(), exception) - throw TaskError.ConvertError() + throw SessionError.DecodingFailed() } } } @@ -146,7 +146,7 @@ class TlvMapper(val tlvList: List) { if (T::class != ExpectedT::class) { Log.e(this::class.simpleName!!, "Mapping error. Type for tag: $tag must be ${tag.valueType()}. It is ${T::class}") - throw TaskError.WrongType() + throw SessionError.DecodingFailedTypeMismatch() } } diff --git a/tangem-core/src/main/java/com/tangem/common/tlv/TlvEncoder.kt b/tangem-core/src/main/java/com/tangem/common/tlv/TlvEncoder.kt index 58e4043450..1855badcab 100644 --- a/tangem-core/src/main/java/com/tangem/common/tlv/TlvEncoder.kt +++ b/tangem-core/src/main/java/com/tangem/common/tlv/TlvEncoder.kt @@ -1,13 +1,12 @@ package com.tangem.common.tlv import com.tangem.Log +import com.tangem.SessionError import com.tangem.commands.* import com.tangem.commands.common.IssuerDataMode import com.tangem.common.extensions.calculateSha256 import com.tangem.common.extensions.hexToBytes import com.tangem.common.extensions.toByteArray -import com.tangem.common.extensions.toHexString -import com.tangem.tasks.TaskError import java.util.* /** @@ -25,7 +24,7 @@ class TlvEncoder { return Tlv(tag, encodeValue(tag, value)) } else { Log.e(this::class.simpleName!!, "Encoding error. Value for tag $tag is null") - throw TaskError.SerializeCommandError() + throw SessionError.EncodingFailed() } } @@ -107,7 +106,7 @@ class TlvEncoder { if (T::class != ExpectedT::class) { Log.e(this::class.simpleName!!, "Mapping error. Type for tag: $tag must be ${tag.valueType()}. It is ${T::class}") - throw TaskError.WrongType() + throw SessionError.EncodingFailedTypeMismatch() } } } \ No newline at end of file diff --git a/tangem-core/src/main/java/com/tangem/tasks/CreateWalletTask.kt b/tangem-core/src/main/java/com/tangem/tasks/CreateWalletTask.kt new file mode 100644 index 0000000000..6b21be102c --- /dev/null +++ b/tangem-core/src/main/java/com/tangem/tasks/CreateWalletTask.kt @@ -0,0 +1,43 @@ +package com.tangem.tasks + +import com.tangem.CardSession +import com.tangem.CardSessionRunnable +import com.tangem.SessionError +import com.tangem.commands.CardStatus +import com.tangem.commands.CheckWalletCommand +import com.tangem.commands.CreateWalletCommand +import com.tangem.commands.CreateWalletResponse +import com.tangem.common.CompletionResult + +class CreateWalletTask : CardSessionRunnable { + + override fun run(session: CardSession, callback: (result: CompletionResult) -> Unit) { + val curve = session.environment.card?.curve + if (curve == null) { + callback(CompletionResult.Failure(SessionError.CardError())) + return + } + + val command = CreateWalletCommand() + command.run(session) { createWalletResult -> + when (createWalletResult) { + is CompletionResult.Failure -> callback(createWalletResult) + is CompletionResult.Success -> { + if (createWalletResult.data.status != CardStatus.Loaded) { + callback(CompletionResult.Failure(SessionError.UnknownError())) + } else { + val checkWalletCommand = CheckWalletCommand( + curve, createWalletResult.data.walletPublicKey + ) + checkWalletCommand.run(session) { result -> + when (result) { + is CompletionResult.Failure -> callback(CompletionResult.Failure(result.error)) + is CompletionResult.Success -> callback(createWalletResult) + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/tangem-core/src/main/java/com/tangem/tasks/ReadIssuerDataTask.kt b/tangem-core/src/main/java/com/tangem/tasks/ReadIssuerDataTask.kt deleted file mode 100644 index d7c50dde31..0000000000 --- a/tangem-core/src/main/java/com/tangem/tasks/ReadIssuerDataTask.kt +++ /dev/null @@ -1,53 +0,0 @@ -package com.tangem.tasks - -import com.tangem.commands.Card -import com.tangem.commands.ReadIssuerDataCommand -import com.tangem.commands.ReadIssuerDataResponse -import com.tangem.commands.common.IssuerDataToVerify -import com.tangem.common.CardEnvironment -import com.tangem.common.CompletionResult - -class ReadIssuerDataTask(private val issuerPublicKey: ByteArray? = null) : Task() { - - override fun onRun( - cardEnvironment: CardEnvironment, - currentCard: Card?, - callback: (result: TaskEvent) -> Unit) { - - val command = ReadIssuerDataCommand() - - sendCommand(command, cardEnvironment) { result -> - when (result) { - is CompletionResult.Success -> { - val data = result.data - if (data.issuerData.isEmpty()) { - completeNfcSession() - callback(TaskEvent.Event(result.data)) - callback(TaskEvent.Completion()) - return@sendCommand - } - val publicKey = issuerPublicKey ?: currentCard!!.issuerPublicKey!! - val issuerDataToVerify = IssuerDataToVerify( - cardEnvironment.cardId!!, - data.issuerData, - data.issuerDataCounter - ) - if (command.verify(publicKey, data.issuerDataSignature, issuerDataToVerify)) { - completeNfcSession() - callback(TaskEvent.Event(result.data)) - callback(TaskEvent.Completion()) - } else { - completeNfcSession(TaskError.VerificationFailed()) - callback(TaskEvent.Completion(TaskError.VerificationFailed())) - } - } - is CompletionResult.Failure -> { - if (result.error !is TaskError.UserCancelled) { - completeNfcSession(result.error) - } - callback(TaskEvent.Completion(result.error)) - } - } - } - } -} \ No newline at end of file diff --git a/tangem-core/src/main/java/com/tangem/tasks/ReadIssuerExtraDataTask.kt b/tangem-core/src/main/java/com/tangem/tasks/ReadIssuerExtraDataTask.kt deleted file mode 100644 index 4f1397b8db..0000000000 --- a/tangem-core/src/main/java/com/tangem/tasks/ReadIssuerExtraDataTask.kt +++ /dev/null @@ -1,100 +0,0 @@ -package com.tangem.tasks - -import com.tangem.commands.Card -import com.tangem.commands.ReadIssuerExtraDataCommand -import com.tangem.commands.ReadIssuerExtraDataResponse -import com.tangem.commands.WriteIssuerExtraDataCommand -import com.tangem.commands.common.IssuerDataToVerify -import com.tangem.common.CardEnvironment -import com.tangem.common.CompletionResult -import java.io.ByteArrayOutputStream - -/** - * This task performs [ReadIssuerExtraDataCommand] repeatedly until - * the issuer extra data is fully retrieved. - */ -internal class ReadIssuerExtraDataTask( - private val issuerPublicKey: ByteArray?) : Task() { - - private val issuerData = ByteArrayOutputStream() - private var issuerDataSize = 0 - private lateinit var card: Card - private lateinit var cardEnvironment: CardEnvironment - - override fun onRun( - cardEnvironment: CardEnvironment, - currentCard: Card?, - callback: (result: TaskEvent) -> Unit - ) { - card = currentCard!! - this.cardEnvironment = cardEnvironment - val command = ReadIssuerExtraDataCommand() - readIssuerData(command, callback) - } - - private fun readIssuerData( - command: ReadIssuerExtraDataCommand, - callback: (result: TaskEvent) -> Unit) { - - if (issuerDataSize != 0) { - delegate?.onDelay( - issuerDataSize, command.offset, WriteIssuerExtraDataCommand.SINGLE_WRITE_SIZE - ) - } - - sendCommand(command, cardEnvironment) { result -> - when (result) { - - is CompletionResult.Success -> { - if (result.data.size != null) { - if (result.data.size == 0) { - completeNfcSession() - callback(TaskEvent.Event(result.data)) - callback(TaskEvent.Completion()) - return@sendCommand - } - issuerDataSize = result.data.size - } - issuerData.write(result.data.issuerData) - if (result.data.issuerDataSignature == null) { - command.offset = issuerData.size() - readIssuerData(command, callback) - } else { - completeTask(result.data, command, callback) - } - } - is CompletionResult.Failure -> { - if (result.error !is TaskError.UserCancelled) { - completeNfcSession(result.error) - } - callback(TaskEvent.Completion(result.error)) - } - } - } - } - - private fun completeTask(data: ReadIssuerExtraDataResponse, command: ReadIssuerExtraDataCommand, - callback: (result: TaskEvent) -> Unit) { - val publicKey = issuerPublicKey ?: card.issuerPublicKey!! - val dataToVerify = IssuerDataToVerify( - cardEnvironment.cardId!!, - issuerData.toByteArray(), - data.issuerDataCounter - ) - if (command.verify(publicKey, data.issuerDataSignature!!, dataToVerify)) { - completeNfcSession() - val finalResult = ReadIssuerExtraDataResponse( - data.cardId, - issuerDataSize, - issuerData.toByteArray(), - data.issuerDataSignature, - data.issuerDataCounter - ) - callback(TaskEvent.Event(finalResult)) - callback(TaskEvent.Completion()) - } else { - completeNfcSession(TaskError.VerificationFailed()) - callback(TaskEvent.Completion(TaskError.VerificationFailed())) - } - } -} \ No newline at end of file diff --git a/tangem-core/src/main/java/com/tangem/tasks/ScanTask.kt b/tangem-core/src/main/java/com/tangem/tasks/ScanTask.kt index f0160c48b3..207cb6f705 100644 --- a/tangem-core/src/main/java/com/tangem/tasks/ScanTask.kt +++ b/tangem-core/src/main/java/com/tangem/tasks/ScanTask.kt @@ -1,78 +1,42 @@ package com.tangem.tasks +import com.tangem.CardSession +import com.tangem.CardSessionRunnable +import com.tangem.SessionError import com.tangem.commands.* -import com.tangem.common.CardEnvironment import com.tangem.common.CompletionResult -/** - * Events that [ScanTask] returns on completion of its commands. - */ -sealed class ScanEvent { - - /** - * Contains data from a Tangem card after successful completion of [ReadCommand]. - */ - data class OnReadEvent(val card: Card) : ScanEvent() - - /** - * Shows whether the Tangem card was verified on completion of [CheckWalletCommand]. - */ - data class OnVerifyEvent(val isGenuine: Boolean) : ScanEvent() -} - /** * Task that allows to read Tangem card and verify its private key. * * It performs two commands, [ReadCommand] and [CheckWalletCommand], subsequently. */ -internal class ScanTask : Task() { +internal class ScanTask : CardSessionRunnable { - override fun onRun(cardEnvironment: CardEnvironment, - currentCard: Card?, - callback: (result: TaskEvent) -> Unit) { + override fun run(session: CardSession, callback: (result: CompletionResult) -> Unit) { - if (currentCard != null) callback(TaskEvent.Event(ScanEvent.OnReadEvent(currentCard))) + val card = session.environment.card - if (currentCard == null) { - completeNfcSession(TaskError.MissingPreflightRead()) - callback(TaskEvent.Completion(TaskError.MissingPreflightRead())) + if (card == null) { + callback(CompletionResult.Failure(SessionError.MissingPreflightRead())) - } else if (currentCard.cardData?.productMask?.contains(ProductMask.tag) != false) { - completeNfcSession() - callback(TaskEvent.Completion()) + } else if (card.cardData?.productMask?.contains(ProductMask.tag) != false) { + callback(CompletionResult.Success(card)) - } else if (currentCard.status != CardStatus.Loaded) { - completeNfcSession() - callback(TaskEvent.Completion()) + } else if (card.status != CardStatus.Loaded) { + callback(CompletionResult.Success(card)) - } else if (currentCard.curve == null || currentCard.walletPublicKey == null) { - completeNfcSession(TaskError.CardError()) - callback(TaskEvent.Completion(TaskError.CardError())) + } else if (card.curve == null || card.walletPublicKey == null) { + callback(CompletionResult.Failure(SessionError.CardError())) } else { + val checkWalletCommand = CheckWalletCommand(card.curve, card.walletPublicKey) - val checkWalletCommand = CheckWalletCommand() - - sendCommand(checkWalletCommand, cardEnvironment) { result -> + checkWalletCommand.run(session) { result -> when (result) { - is CompletionResult.Failure -> { - if (result.error !is TaskError.UserCancelled) { - completeNfcSession(result.error) - } - callback(TaskEvent.Completion(result.error)) - } - is CompletionResult.Success -> { - completeNfcSession() - val verified = result.data.verify( - currentCard.curve, - currentCard.walletPublicKey, - checkWalletCommand.challenge - ) - callback(TaskEvent.Event(ScanEvent.OnVerifyEvent(verified))) - callback(TaskEvent.Completion()) - } + is CompletionResult.Success -> callback(CompletionResult.Success(card)) + is CompletionResult.Failure -> callback(CompletionResult.Failure(result.error)) } - } } } diff --git a/tangem-core/src/main/java/com/tangem/tasks/SingleCommandTask.kt b/tangem-core/src/main/java/com/tangem/tasks/SingleCommandTask.kt deleted file mode 100644 index 03efa615f8..0000000000 --- a/tangem-core/src/main/java/com/tangem/tasks/SingleCommandTask.kt +++ /dev/null @@ -1,39 +0,0 @@ -package com.tangem.tasks - -import com.tangem.commands.Card -import com.tangem.common.CardEnvironment -import com.tangem.commands.CommandResponse -import com.tangem.commands.CommandSerializer -import com.tangem.common.CompletionResult - -/** - * Allows to perform a single command. - * - * @property command A command that will be performed. - */ -class SingleCommandTask( - private val command: CommandSerializer -) : Task() { - - override fun onRun( - cardEnvironment: CardEnvironment, - currentCard: Card?, - callback: (result: TaskEvent) -> Unit - ) { - sendCommand(command, cardEnvironment) { result -> - when (result) { - is CompletionResult.Success -> { - completeNfcSession() - callback(TaskEvent.Event(result.data)) - callback(TaskEvent.Completion()) - } - is CompletionResult.Failure -> { - if (result.error !is TaskError.UserCancelled) { - completeNfcSession(result.error) - } - callback(TaskEvent.Completion(result.error)) - } - } - } - } -} diff --git a/tangem-core/src/main/java/com/tangem/tasks/Task.kt b/tangem-core/src/main/java/com/tangem/tasks/Task.kt deleted file mode 100644 index 224b163a9a..0000000000 --- a/tangem-core/src/main/java/com/tangem/tasks/Task.kt +++ /dev/null @@ -1,358 +0,0 @@ -package com.tangem.tasks - -import com.tangem.CardManagerDelegate -import com.tangem.CardReader -import com.tangem.Log -import com.tangem.commands.* -import com.tangem.common.CardEnvironment -import com.tangem.common.CompletionResult -import com.tangem.common.EncryptionMode -import com.tangem.common.apdu.CommandApdu -import com.tangem.common.apdu.StatusWord -import com.tangem.common.extensions.calculateSha256 -import com.tangem.crypto.EncryptionHelper -import com.tangem.crypto.FastEncryptionHelper -import com.tangem.crypto.StrongEncryptionHelper -import com.tangem.crypto.pbkdf2Hash - -/** - * An error class that represent typical errors that may occur when performing Tangem SDK tasks. - * Errors are propagated back to the caller in callbacks. - */ -sealed class TaskError(val code: Int) : Exception() { - - //Errors in serializing APDU - /** - * This error is returned when there [CommandSerializer] cannot deserialize [com.tangem.common.tlv.Tlv] - * (this error is a wrapper around internal [com.tangem.common.tlv.TlvMapper] errors). - */ - class SerializeCommandError : TaskError(1001) - - class EncodingError : TaskError(1002) - class MissingTag : TaskError(1003) - class WrongType : TaskError(1004) - class ConvertError : TaskError(1005) - - /** - * This error is returned when unknown [StatusWord] is received from a card. - */ - class UnknownStatus : TaskError(2001) - - /** - * This error is returned when a card's reply is [StatusWord.ErrorProcessingCommand]. - * The card sends this status in case of internal card error. - */ - class ErrorProcessingCommand : TaskError(2002) - - /** - * This error is returned when a task (such as [ScanTask]) requires that [ReadCommand] - * is executed before performing other commands. - */ - class MissingPreflightRead : TaskError(2003) - - /** - * This error is returned when a card's reply is [StatusWord.InvalidState]. - * The card sends this status when command can not be executed in the current state of a card. - */ - class InvalidState : TaskError(2004) - - /** - * This error is returned when a card's reply is [StatusWord.InsNotSupported]. - * The card sends this status when the card cannot process the [com.tangem.common.apdu.Instruction]. - */ - class InsNotSupported : TaskError(2005) - - /** - * This error is returned when a card's reply is [StatusWord.InvalidParams]. - * The card sends this status when there are wrong or not sufficient parameters in TLV request, - * or wrong PIN1/PIN2. - * The error may be caused, for example, by wrong parameters of the [Task], [CommandSerializer], - * mapping or serialization errors. - */ - class InvalidParams : TaskError(2006) - - /** - * This error is returned when a card's reply is [StatusWord.NeedEncryption] - * and the encryption was not established by TangemSdk. - */ - class NeedEncryption : TaskError(2007) - - //Scan errors - /** - * This error is returned when a [Task] checks unsuccessfully either - * a card's ability to sign with its private key, or the validity of issuer data. - */ - class VerificationFailed : TaskError(3000) - - /** - * This error is returned when a [ScanTask] returns a [Card] without some of the essential fields. - */ - class CardError : TaskError(3001) - - /** - * This error is returned when a [Task] expects a user to use a particular card, - * and a user tries to use a different card. - */ - class WrongCard : TaskError(3002) - - /** - * Tangem cards can sign currently up to 10 hashes during one [com.tangem.commands.SignCommand]. - * This error is returned when a [com.tangem.commands.SignCommand] receives more than 10 hashes to sign. - */ - class TooMuchHashesInOneTransaction : TaskError(3003) - - /** - * This error is returned when a [com.tangem.commands.SignCommand] - * receives only empty hashes for signature. - */ - class EmptyHashes : TaskError(3004) - - /** - * This error is returned when a [com.tangem.commands.SignCommand] - * receives hashes of different lengths for signature. - */ - class HashSizeMustBeEqual : TaskError(3005) - - /** - * This error is returned when [com.tangem.CardManager] was called with a new [Task], - * while a previous [Task] is still in progress. - */ - class Busy : TaskError(4000) - - /** - * This error is returned when a user manually closes NFC Reading Bottom Sheet Dialog. - */ - class UserCancelled : TaskError(4001) - - //NFC errors - class NfcReaderError : TaskError(5002) - - /** - * This error is returned when Android NFC reader loses a tag - * (e.g. a user detaches card from the phone's NFC module) while the NFC session is in progress. - */ - class TagLost : TaskError(5003) - - class UnknownError : TaskError(6000) - - //Specific Command Errors - /** - * This error is returned when [ReadIssuerDataTask] or [ReadIssuerExtraDataTask] expects a counter - * (when the card's requires it), but the counter is missing. - */ - class MissingCounter : TaskError(7001) -} - -/** - * Events that are are sent in callbacks from [Task]. - */ -sealed class TaskEvent { - - /** - * A callback that is triggered by a Task. - */ - class Event(val data: T) : TaskEvent() - - /** - * A callback that is triggered when a [Task] is completed. - * - * @param error is null if it's a successful completion of a [Task] - */ - class Completion(val error: TaskError? = null) : TaskEvent() -} - -/** - * Allows to perform a group of commands interacting between the card and the application. - * A task opens an NFC session, sends commands to the card and receives its responses, - * repeats the commands if needed, and closes session after receiving the last answer. - */ -abstract class Task { - - var delegate: CardManagerDelegate? = null - var reader: CardReader? = null - var performPreflightRead: Boolean = true - var securityDelayDuration: Int = 0 - - /** - * This method should be called to run the [Task] and perform all its operations. - * - * @param cardEnvironment Relevant current version of a card environment - * @param callback It will be triggered during the performance of the [Task] - */ - fun run(cardEnvironment: CardEnvironment, - callback: (result: TaskEvent) -> Unit) { - delegate?.onNfcSessionStarted(cardEnvironment.cardId) - reader?.openSession() - Log.i(this::class.simpleName!!, "Nfc task is started") - - if (performPreflightRead) { - runWithPreflightRead(cardEnvironment, callback) - } else { - onRun(cardEnvironment, null, callback) - } - } - - /** - * Should be called on [Task] completion, whether it was successful or with failure. - * - * @param taskError The error to be shown by [CardManagerDelegate] - */ - protected fun completeNfcSession(taskError: TaskError? = null) { - reader?.closeSession() - if (taskError != null) { - delegate?.onError(taskError) - } else { - delegate?.onNfcSessionCompleted() - } - } - - /** - * In this method the individual Tasks' logic should be implemented. - */ - protected abstract fun onRun(cardEnvironment: CardEnvironment, - currentCard: Card?, - callback: (result: TaskEvent) -> Unit) - - /** - * This method should be called by Tasks in their [onRun] method wherever - * they need to communicate with the Tangem Card by launching commands. - */ - protected fun sendCommand( - command: CommandSerializer, - cardEnvironment: CardEnvironment, - callback: (result: CompletionResult) -> Unit) { - - Log.i(this::class.simpleName!!, "Nfc command ${command::class.simpleName!!} is initiated") - - when (cardEnvironment.encryptionMode) { - EncryptionMode.NONE -> { - val commandApdu = command.serialize(cardEnvironment) - sendRequest(command, commandApdu, cardEnvironment, callback) - } - EncryptionMode.FAST, EncryptionMode.STRONG -> { - if (cardEnvironment.encryptionKey != null ) { - val commandApdu = command.serialize(cardEnvironment) - sendRequest(command, commandApdu, cardEnvironment, callback) - return - } - val encryptionHelper: EncryptionHelper = - if (cardEnvironment.encryptionMode == EncryptionMode.STRONG) { - StrongEncryptionHelper() - } else { - FastEncryptionHelper() - } - val openSessionCommand = OpenSessionCommand(encryptionHelper.keyA) - val openSessionApdu = openSessionCommand.serialize(cardEnvironment) - sendRequest(openSessionCommand, openSessionApdu, cardEnvironment) { result -> - when (result) { - is CompletionResult.Success -> { - val uid = result.data.uid - val protocolKey = cardEnvironment.pin1.calculateSha256().pbkdf2Hash(uid, 50) - val secret = encryptionHelper.generateSecret(result.data.sessionKeyB) - val sessionKey = (secret + protocolKey).calculateSha256() - cardEnvironment.encryptionKey = sessionKey - - sendCommand(command, cardEnvironment, callback) - } - is CompletionResult.Failure -> callback(CompletionResult.Failure(result.error)) - } - } - } - } - } - - private fun sendRequest(command: CommandSerializer, - commandApdu: CommandApdu, - cardEnvironment: CardEnvironment, - callback: (result: CompletionResult) -> Unit) { - - reader?.transceiveApdu(commandApdu) { result -> - - when (result) { - is CompletionResult.Success -> { - val responseApdu = result.data - when (responseApdu.statusWord) { - StatusWord.ProcessCompleted, StatusWord.Pin1Changed, StatusWord.Pin2Changed, StatusWord.PinsChanged - -> { - try { - val responseData = command.deserialize(cardEnvironment, responseApdu) - Log.i(this::class.simpleName!!, "Nfc command ${command::class.simpleName!!} is completed") - callback(CompletionResult.Success(responseData as T)) - } catch (error: TaskError) { - callback(CompletionResult.Failure(error)) - } - } - StatusWord.InvalidParams -> callback(CompletionResult.Failure(TaskError.InvalidParams())) - StatusWord.Unknown -> { - Log.e(this::class.simpleName!!, "Unknown status error: ${result.data.sw}") - callback(CompletionResult.Failure(TaskError.UnknownStatus())) - } - StatusWord.ErrorProcessingCommand -> callback(CompletionResult.Failure(TaskError.ErrorProcessingCommand())) - StatusWord.InvalidState -> callback(CompletionResult.Failure(TaskError.InvalidState())) - - StatusWord.InsNotSupported -> callback(CompletionResult.Failure(TaskError.InsNotSupported())) - StatusWord.NeedEncryption -> { - when (cardEnvironment.encryptionMode) { - EncryptionMode.NONE -> { - cardEnvironment.encryptionKey = null - cardEnvironment.encryptionMode = EncryptionMode.FAST - } - EncryptionMode.FAST -> { - cardEnvironment.encryptionKey = null - cardEnvironment.encryptionMode = EncryptionMode.STRONG - } - EncryptionMode.STRONG -> { - Log.e(this::class.simpleName!!, "Encryption doesn't work") - callback(CompletionResult.Failure(TaskError.NeedEncryption())) - return@transceiveApdu - } - } - sendCommand(command, cardEnvironment, callback) - } - StatusWord.NeedPause -> { - // NeedPause is returned from the card whenever security delay is triggered. - val remainingTime = command.deserializeSecurityDelay(responseApdu, cardEnvironment) - if (remainingTime != null) delegate?.onSecurityDelay(remainingTime, securityDelayDuration) - Log.i(this::class.simpleName!!, "Nfc command ${command::class.simpleName!!} triggered security delay of $remainingTime milliseconds") - sendRequest(command, commandApdu, cardEnvironment, callback) - } - } - } - is CompletionResult.Failure -> - if (result.error == TaskError.TagLost()) { - delegate?.onTagLost() - } else if (result.error is TaskError.UserCancelled) { - callback(CompletionResult.Failure(TaskError.UserCancelled())) - reader?.closeSession() - } - } - } - } - - private fun runWithPreflightRead( - environment: CardEnvironment, callback: (result: TaskEvent) -> Unit) { - sendCommand(ReadCommand(), environment) { readResult -> - when (readResult) { - is CompletionResult.Failure -> { - completeNfcSession(readResult.error) - callback(TaskEvent.Completion(readResult.error)) - } - is CompletionResult.Success -> { - val receivedCardId = readResult.data.cardId - securityDelayDuration = readResult.data.pauseBeforePin2 ?: 0 - - if (environment.cardId != null && environment.cardId != receivedCardId) { - completeNfcSession(TaskError.WrongCard()) - callback(TaskEvent.Completion(TaskError.WrongCard())) - return@sendCommand - } - - val newEnvironment = environment.copy(cardId = receivedCardId) - onRun(newEnvironment, readResult.data, callback) - } - } - } - } -} - - diff --git a/tangem-core/src/main/java/com/tangem/tasks/WriteIssuerDataTask.kt b/tangem-core/src/main/java/com/tangem/tasks/WriteIssuerDataTask.kt deleted file mode 100644 index b22ed95049..0000000000 --- a/tangem-core/src/main/java/com/tangem/tasks/WriteIssuerDataTask.kt +++ /dev/null @@ -1,67 +0,0 @@ -package com.tangem.tasks - -import com.tangem.commands.Card -import com.tangem.commands.Settings -import com.tangem.commands.WriteIssuerDataCommand -import com.tangem.commands.WriteIssuerDataResponse -import com.tangem.commands.common.IssuerDataToVerify -import com.tangem.common.CardEnvironment -import com.tangem.common.CompletionResult - -class WriteIssuerDataTask( - private val issuerData: ByteArray, - private val issuerDataSignature: ByteArray, - private val issuerDataCounter: Int? = null, - private val issuerPublicKey: ByteArray? = null -) : Task() { - - private lateinit var card: Card - - override fun onRun( - cardEnvironment: CardEnvironment, - currentCard: Card?, - callback: (result: TaskEvent) -> Unit) { - - card = currentCard!! - val command = WriteIssuerDataCommand( - issuerData, issuerDataSignature, issuerDataCounter - ) - if (!isCounterValid(issuerDataCounter)) { - completeNfcSession(TaskError.MissingCounter()) - callback(TaskEvent.Completion(TaskError.MissingCounter())) - } else if (!verifySignature(command, cardEnvironment.cardId!!)) { - completeNfcSession(TaskError.VerificationFailed()) - callback(TaskEvent.Completion(TaskError.VerificationFailed())) - } - - sendCommand(command, cardEnvironment) { result -> - when (result) { - is CompletionResult.Success -> { - completeNfcSession() - callback(TaskEvent.Event(result.data)) - callback(TaskEvent.Completion()) - } - is CompletionResult.Failure -> { - if (result.error !is TaskError.UserCancelled) { - completeNfcSession(result.error) - } - callback(TaskEvent.Completion(result.error)) - } - } - } - } - - private fun isCounterValid(issuerDataCounter: Int?): Boolean = - if (isCounterRequired()) issuerDataCounter != null else true - - private fun isCounterRequired(): Boolean = - card.settingsMask?.contains(Settings.ProtectIssuerDataAgainstReplay) != false - - private fun verifySignature(command: WriteIssuerDataCommand, cardId: String): Boolean { - return command.verify( - issuerPublicKey ?: card.issuerPublicKey!!, - issuerDataSignature, - IssuerDataToVerify(cardId, issuerData, issuerDataCounter) - ) - } -} \ No newline at end of file diff --git a/tangem-core/src/main/java/com/tangem/tasks/WriteIssuerExtraDataTask.kt b/tangem-core/src/main/java/com/tangem/tasks/WriteIssuerExtraDataTask.kt deleted file mode 100644 index 887e832548..0000000000 --- a/tangem-core/src/main/java/com/tangem/tasks/WriteIssuerExtraDataTask.kt +++ /dev/null @@ -1,110 +0,0 @@ -package com.tangem.tasks - -import com.tangem.commands.* -import com.tangem.commands.common.IssuerDataMode -import com.tangem.commands.common.IssuerDataToVerify -import com.tangem.common.CardEnvironment -import com.tangem.common.CompletionResult - -/** - * This task performs [WriteIssuerExtraDataCommand] repeatedly until the issuer extra data is fully - * written on the card. - * @param issuerData Data provided by issuer. - * @param startingSignature Issuer’s signature with Issuer Data Private Key of [cardId], - * [issuerDataCounter] (if flags Protect_Issuer_Data_Against_Replay and - * Restrict_Overwrite_Issuer_Extra_Data are set in [SettingsMask]) and size of [issuerData]. - * @param finalizingSignature Issuer’s signature with Issuer Data Private Key of [cardId], - * [issuerData] and [issuerDataCounter] (the latter one only if flags Protect_Issuer_Data_Against_Replay - * andRestrict_Overwrite_Issuer_Extra_Data are set in [SettingsMask]). - * @param issuerDataCounter An optional counter that protect issuer data against replay attack. - */ -internal class WriteIssuerExtraDataTask( - private val issuerData: ByteArray, - private val startingSignature: ByteArray, - private val finalizingSignature: ByteArray, - private val issuerPublicKey: ByteArray? = null, - private val issuerDataCounter: Int? = null -) : Task() { - - private lateinit var card: Card - private lateinit var cardEnvironment: CardEnvironment - - override fun onRun(cardEnvironment: CardEnvironment, - currentCard: Card?, - callback: (result: TaskEvent) -> Unit) { - - card = currentCard!! - this.cardEnvironment = cardEnvironment - val command = WriteIssuerExtraDataCommand( - issuerData, startingSignature, finalizingSignature, issuerDataCounter - ) - if (!isCounterValid(issuerDataCounter)) { - completeNfcSession(TaskError.MissingCounter()) - callback(TaskEvent.Completion(TaskError.MissingCounter())) - } else if (!verifySignatures(command)) { - completeNfcSession(TaskError.VerificationFailed()) - callback(TaskEvent.Completion(TaskError.VerificationFailed())) - } - - writeIssuerData(command, callback) - } - - private fun writeIssuerData( - command: WriteIssuerExtraDataCommand, - callback: (result: TaskEvent) -> Unit) { - - if (command.mode == IssuerDataMode.WriteExtraData) { - delegate?.onDelay(issuerData.size, command.offset, WriteIssuerExtraDataCommand.SINGLE_WRITE_SIZE) - } - sendCommand(command, cardEnvironment) { result -> - when (result) { - - is CompletionResult.Success -> { - when (command.mode) { - IssuerDataMode.InitializeWritingExtraData -> { - command.mode = IssuerDataMode.WriteExtraData - writeIssuerData(command, callback) - return@sendCommand - } - IssuerDataMode.WriteExtraData -> { - command.offset += WriteIssuerExtraDataCommand.SINGLE_WRITE_SIZE - if (command.offset >= issuerData.size) { - command.mode = IssuerDataMode.FinalizeExtraData - } - writeIssuerData(command, callback) - return@sendCommand - } - IssuerDataMode.FinalizeExtraData -> { - completeNfcSession() - callback(TaskEvent.Event(result.data)) - callback(TaskEvent.Completion()) - } - } - } - is CompletionResult.Failure -> { - if (result.error !is TaskError.UserCancelled) { - completeNfcSession(result.error) - } - callback(TaskEvent.Completion(result.error)) - } - } - } - } - - private fun isCounterValid(issuerDataCounter: Int?): Boolean = - if (isCounterRequired()) issuerDataCounter != null else true - - private fun isCounterRequired(): Boolean = - card.settingsMask?.contains(Settings.ProtectIssuerDataAgainstReplay) != false - - private fun verifySignatures(command: WriteIssuerExtraDataCommand): Boolean { - val publicKey = issuerPublicKey ?: card.issuerPublicKey!! - val cardId = cardEnvironment.cardId!! - - val firstData = IssuerDataToVerify(cardId, null, issuerDataCounter, issuerData.size) - val secondData = IssuerDataToVerify(cardId, issuerData, issuerDataCounter) - - return command.verify(publicKey, startingSignature, firstData) && - command.verify(publicKey, finalizingSignature, secondData) - } -} \ No newline at end of file diff --git a/tangem-core/src/test/java/com/tangem/common/tlv/TlvMapperTest.kt b/tangem-core/src/test/java/com/tangem/common/tlv/TlvDecoderTest.kt similarity index 71% rename from tangem-core/src/test/java/com/tangem/common/tlv/TlvMapperTest.kt rename to tangem-core/src/test/java/com/tangem/common/tlv/TlvDecoderTest.kt index 64cb5ce15d..f9915fc7b8 100644 --- a/tangem-core/src/test/java/com/tangem/common/tlv/TlvMapperTest.kt +++ b/tangem-core/src/test/java/com/tangem/common/tlv/TlvDecoderTest.kt @@ -1,69 +1,69 @@ package com.tangem.common.tlv import com.google.common.truth.Truth.assertThat +import com.tangem.SessionError import com.tangem.commands.* import com.tangem.common.extensions.hexToBytes -import com.tangem.tasks.TaskError import org.junit.Test import org.junit.jupiter.api.assertThrows import java.util.* -class TlvMapperTest { +class TlvDecoderTest { private val rawData = byteArrayOf(1, 8, -53, 34, 0, 0, 0, 2, 115, 116, 32, 11, 83, 77, 65, 82, 84, 32, 67, 65, 83, 72, 0, 2, 1, 2, -128, 6, 50, 46, 49, 49, 114, 0, 3, 65, 4, -49, 11, -50, -66, -121, -25, -2, 65, 65, -13, 14, 49, 27, -82, -33, -85, -113, 65, 20, 8, -39, -75, 57, 45, 65, -31, 35, 44, 38, 40, 63, -44, 113, -45, -75, -95, -118, 118, 29, 65, 117, -24, -53, 82, -72, 91, -20, -96, -77, -103, -14, -63, 52, -127, -123, -27, -16, -128, -67, -3, -104, -26, -22, 65, 10, 4, 0, 0, 126, 33, 12, 90, -127, 2, 0, 41, -126, 4, 7, -29, 5, 2, -125, 7, 84, 65, 78, 71, 69, 77, 0, -124, 3, 69, 84, 72, -122, 64, 111, -103, 48, -114, -40, 18, -103, 26, -102, -12, -38, -78, -90, -9, -98, 88, -47, -100, -24, 24, -105, -70, -72, 6, 94, -96, -77, 11, -123, -28, -118, 37, 63, 107, -55, -11, 23, -12, 13, -23, -121, -63, 36, -59, 70, 116, 91, -125, -34, -69, 23, -112, 6, 17, 4, -49, 68, -56, 29, -45, 81, 10, 97, 83, 48, 65, 4, -127, -106, -86, 75, 65, 10, -60, 74, 59, -100, -50, 24, -25, -66, 34, 106, -22, 7, 10, -52, -125, -87, -49, 103, 84, 15, -84, 73, -81, 37, 18, -97, 106, 83, -118, 40, -83, 99, 65, 53, -114, 60, 79, -103, 99, 6, 79, 126, 54, 83, 114, -90, 81, -45, 116, -27, -62, 60, -35, 55, -3, 9, -101, -14, 5, 10, 115, 101, 99, 112, 50, 53, 54, 107, 49, 0, 8, 4, 0, 15, 66, 64, 7, 1, 0, 9, 2, 11, -72, 96, 65, 4, -42, -5, -41, -84, -23, 88, 2, 86, -63, -118, -123, -10, -66, -82, -107, -68, -93, 111, 47, 93, -20, -86, 74, 28, 21, 81, 93, -21, -124, -57, -102, 55, 17, 84, -66, -68, -22, -128, 126, -99, -65, -54, -42, 59, -25, -21, -124, 5, 59, -16, -72, 73, 48, 16, -27, 103, -112, -73, 2, 96, -51, 41, -42, 116, 98, 4, 0, 15, 66, 52, 99, 4, 0, 0, 0, 13, 15, 1, 0) private val tlvData = Tlv.deserialize(rawData) - private val tlvMapper = TlvMapper(tlvData!!) + private val tlvMapper = TlvDecoder(tlvData!!) - private val cardDataRaw: ByteArray = tlvMapper.map(TlvTag.CardData) - private val cardDataMapper = TlvMapper(Tlv.deserialize(cardDataRaw)!!) + private val cardDataRaw: ByteArray = tlvMapper.decode(TlvTag.CardData) + private val cardDataMapper = TlvDecoder(Tlv.deserialize(cardDataRaw)!!) @Test fun `map optional when value is present`() { - val settingsMask: SettingsMask? = tlvMapper.mapOptional(TlvTag.SettingsMask) + val settingsMask: SettingsMask? = tlvMapper.decodeOptional(TlvTag.SettingsMask) assertThat(settingsMask) .isNotNull() } @Test fun `map optional when no tag returns null`() { - val tokenSymbol: String? = tlvMapper.mapOptional(TlvTag.TokenSymbol) + val tokenSymbol: String? = tlvMapper.decodeOptional(TlvTag.TokenSymbol) assertThat(tokenSymbol) .isNull() } @Test fun `map when value is null throws MissingTagException`() { - assertThrows { - tlvMapper.map(TlvTag.TokenSymbol) + assertThrows { + tlvMapper.decode(TlvTag.TokenSymbol) } } @Test fun `map optional to wrong type throws WrongTypeException`() { - assertThrows { - tlvMapper.mapOptional(TlvTag.CardData) + assertThrows { + tlvMapper.decodeOptional(TlvTag.CardData) } } @Test fun `map to wrong type throws WrongTypeException`() { - assertThrows { - tlvMapper.map(TlvTag.CardData) + assertThrows { + tlvMapper.decode(TlvTag.CardData) } } @Test fun `map boolean missing flag returns false`() { - val terminalIsLinked: Boolean = tlvMapper.map(TlvTag.TerminalIsLinked) + val terminalIsLinked: Boolean = tlvMapper.decode(TlvTag.TerminalIsLinked) assertThat(terminalIsLinked) .isFalse() } @Test fun `map SettingsMask returns correct value`() { - val settingsMask: SettingsMask = tlvMapper.map(TlvTag.SettingsMask) + val settingsMask: SettingsMask = tlvMapper.decode(TlvTag.SettingsMask) assertThat(settingsMask) .isNotNull() assertThat(settingsMask.rawValue) @@ -82,16 +82,16 @@ class TlvMapperTest { @Test fun `map SigningMethods single value returns correct value`() { - val signingMethods: SigningMethod = tlvMapper.map(TlvTag.SigningMethod) + val signingMethods: SigningMethod = tlvMapper.decode(TlvTag.SigningMethod) assertThat(signingMethods.contains(SigningMethod.signHash)) .isTrue() } @Test fun `map SigningMethods set of methods returns correct value`() { - val localMapper = TlvMapper(Tlv.deserialize("070195".hexToBytes())!!) + val localMapper = TlvDecoder(Tlv.deserialize("070195".hexToBytes())!!) - val signingMethod: SigningMethod = localMapper.map(TlvTag.SigningMethod) + val signingMethod: SigningMethod = localMapper.decode(TlvTag.SigningMethod) assertThat(signingMethod.contains(SigningMethod.signHash)) .isTrue() assertThat(signingMethod.contains(SigningMethod.signHashValidatedByIssuer)) @@ -110,38 +110,38 @@ class TlvMapperTest { @Test fun `map CardStatus returns correct value`() { - val cardStatus: CardStatus = tlvMapper.map(TlvTag.Status) + val cardStatus: CardStatus = tlvMapper.decode(TlvTag.Status) assertThat(cardStatus) .isEqualTo(CardStatus.Loaded) } @Test fun `map ProductMask with raw value 5 returns correct value`() { - val localMapper = TlvMapper(listOf(Tlv(TlvTag.ProductMask, byteArrayOf(5)))) - val productMask: ProductMask = localMapper.map(TlvTag.ProductMask) + val localMapper = TlvDecoder(listOf(Tlv(TlvTag.ProductMask, byteArrayOf(5)))) + val productMask: ProductMask = localMapper.decode(TlvTag.ProductMask) assertThat(productMask.contains(ProductMask.note) && productMask.contains(ProductMask.idCard)) .isTrue() } @Test fun `map ProductMask with raw value 1 returns correct value`() { - val localMapper = TlvMapper(listOf(Tlv(TlvTag.ProductMask, byteArrayOf(1)))) - val productMask: ProductMask = localMapper.map(TlvTag.ProductMask) + val localMapper = TlvDecoder(listOf(Tlv(TlvTag.ProductMask, byteArrayOf(1)))) + val productMask: ProductMask = localMapper.decode(TlvTag.ProductMask) assertThat(productMask.contains(ProductMask.note)) .isTrue() } @Test fun `map Enum with unknown code throws ConversionException error`() { - val localMapper = TlvMapper(listOf(Tlv(TlvTag.CurveId, "test".toByteArray()))) - assertThrows { - localMapper.map(TlvTag.CurveId) + val localMapper = TlvDecoder(listOf(Tlv(TlvTag.CurveId, "test".toByteArray()))) + assertThrows { + localMapper.decode(TlvTag.CurveId) } } @Test fun `map DateTime returns correct value`() { - val date: Date = cardDataMapper.map(TlvTag.ManufactureDateTime) + val date: Date = cardDataMapper.decode(TlvTag.ManufactureDateTime) val expected = Calendar.getInstance().apply { this.set(2019, 4, 2, 0, 0, 0) }.time assertThat(date.toString()) .isEqualTo(expected.toString()) @@ -149,43 +149,43 @@ class TlvMapperTest { @Test fun `map EllipticCurve returns correct value`() { - val ellipticCurve: EllipticCurve = tlvMapper.map(TlvTag.CurveId) + val ellipticCurve: EllipticCurve = tlvMapper.decode(TlvTag.CurveId) assertThat(ellipticCurve) .isEqualTo(EllipticCurve.Secp256k1) } @Test fun `map ByteArray returns correctly`() { - val cardPublicKey: ByteArray = tlvMapper.map(TlvTag.CardPublicKey) + val cardPublicKey: ByteArray = tlvMapper.decode(TlvTag.CardPublicKey) assertThat(cardPublicKey) .isInstanceOf(ByteArray::class.java) } @Test fun `map Int returns correct value`() { - val signedHashes: Int = tlvMapper.map(TlvTag.SignedHashes) + val signedHashes: Int = tlvMapper.decode(TlvTag.SignedHashes) assertThat(signedHashes) .isEqualTo(13) } @Test fun `map Int with wrong value throws ConversionException`() { - val localMapper = TlvMapper(listOf(Tlv(TlvTag.SignedHashes, byteArrayOf(1, 2, 3, 4, 5)))) - assertThrows { - localMapper.map(TlvTag.SignedHashes) + val localMapper = TlvDecoder(listOf(Tlv(TlvTag.SignedHashes, byteArrayOf(1, 2, 3, 4, 5)))) + assertThrows { + localMapper.decode(TlvTag.SignedHashes) } } @Test fun `map UTF8 returns correct value`() { - val blockchainId: String = cardDataMapper.map(TlvTag.BlockchainId) + val blockchainId: String = cardDataMapper.decode(TlvTag.BlockchainId) assertThat(blockchainId) .isEqualTo("ETH") } @Test fun `map Hex returns correct value`() { - val cardId: String = tlvMapper.map(TlvTag.CardId) + val cardId: String = tlvMapper.decode(TlvTag.CardId) assertThat(cardId) .isEqualTo("cb22000000027374") } diff --git a/tangem-sdk/src/main/java/com/tangem/tangem_sdk_new/DefaultCardManagerDelegate.kt b/tangem-sdk/src/main/java/com/tangem/tangem_sdk_new/DefaultCardSessionViewDelegate.kt similarity index 87% rename from tangem-sdk/src/main/java/com/tangem/tangem_sdk_new/DefaultCardManagerDelegate.kt rename to tangem-sdk/src/main/java/com/tangem/tangem_sdk_new/DefaultCardSessionViewDelegate.kt index e1dd5bb69b..56cce78f88 100644 --- a/tangem-sdk/src/main/java/com/tangem/tangem_sdk_new/DefaultCardManagerDelegate.kt +++ b/tangem-sdk/src/main/java/com/tangem/tangem_sdk_new/DefaultCardSessionViewDelegate.kt @@ -6,24 +6,24 @@ import android.view.View import android.view.animation.DecelerateInterpolator import androidx.fragment.app.FragmentActivity import com.google.android.material.bottomsheet.BottomSheetDialog -import com.tangem.CardManagerDelegate +import com.tangem.CardSessionViewDelegate import com.tangem.Log import com.tangem.LoggerInterface +import com.tangem.Message import com.tangem.common.CompletionResult import com.tangem.tangem_sdk_new.extensions.hide import com.tangem.tangem_sdk_new.extensions.show import com.tangem.tangem_sdk_new.nfc.NfcReader import com.tangem.tangem_sdk_new.ui.NfcEnableDialog import com.tangem.tangem_sdk_new.ui.TouchCardAnimation -import com.tangem.tasks.TaskError import kotlinx.android.synthetic.main.layout_touch_card.* import kotlinx.android.synthetic.main.nfc_bottom_sheet.* /** - * Default implementation of [CardManagerDelegate]. + * Default implementation of [CardSessionViewDelegate]. * If no customisation is required, this is the preferred way to use Tangem SDK. */ -class DefaultCardManagerDelegate(private val reader: NfcReader) : CardManagerDelegate { +class DefaultCardSessionViewDelegate(private val reader: NfcReader) : CardSessionViewDelegate { lateinit var activity: FragmentActivity private var readingDialog: BottomSheetDialog? = null @@ -33,13 +33,13 @@ class DefaultCardManagerDelegate(private val reader: NfcReader) : CardManagerDel setLogger() } - override fun onNfcSessionStarted(cardId: String?) { + override fun onNfcSessionStarted(cardId: String?, message: Message?) { reader.readingCancelled = false - postUI { showReadingDialog(activity, cardId) } + postUI { showReadingDialog(activity, cardId, message) } if (!reader.nfcEnabled) showNFCEnableDialog() } - private fun showReadingDialog(activity: FragmentActivity, cardId: String?) { + private fun showReadingDialog(activity: FragmentActivity, cardId: String?, message: Message?) { val dialogView = activity.layoutInflater.inflate(R.layout.nfc_bottom_sheet, null) readingDialog = BottomSheetDialog(activity) readingDialog?.setContentView(dialogView) @@ -56,6 +56,10 @@ class DefaultCardManagerDelegate(private val reader: NfcReader) : CardManagerDel readingDialog?.tvCardId?.visibility = View.VISIBLE readingDialog?.tvCardId?.text = cardId } + if (message != null) { + if (message.body != null) readingDialog?.tvTaskText?.text = message.body + if (message.header != null) readingDialog?.tvTaskTitle?.text = message.header + } } readingDialog?.setOnCancelListener { reader.readingCancelled = true @@ -132,25 +136,29 @@ class DefaultCardManagerDelegate(private val reader: NfcReader) : CardManagerDel } } - override fun onNfcSessionCompleted() { + override fun onNfcSessionCompleted(message: Message?) { postUI { readingDialog?.lTouchCard?.hide() readingDialog?.flSecurityDelay?.hide() readingDialog?.flCompletion?.show() readingDialog?.ivCompletion?.setImageDrawable(activity.getDrawable(R.drawable.ic_done_135dp)) + if (message != null) { + if (message.body != null) readingDialog?.tvTaskText?.text = message.body + if (message.header != null) readingDialog?.tvTaskTitle?.text = message.header + } performHapticFeedback() } postUI(300) { readingDialog?.dismiss() } } - override fun onError(error: TaskError) { + override fun onError(errorMessage: String) { postUI { readingDialog?.lTouchCard?.hide() readingDialog?.flSecurityDelay?.hide() readingDialog?.flCompletion?.hide() readingDialog?.flError?.show() readingDialog?.tvTaskTitle?.text = activity.getText(R.string.dialog_error) - readingDialog?.tvTaskText?.text = "${error::class.simpleName}: ${error.code}" + readingDialog?.tvTaskText?.text = errorMessage performHapticFeedback() } } diff --git a/tangem-sdk/src/main/java/com/tangem/tangem_sdk_new/TerminalKeysStorage.kt b/tangem-sdk/src/main/java/com/tangem/tangem_sdk_new/TerminalKeysStorage.kt index 70b2cfa431..0e4eabae9c 100644 --- a/tangem-sdk/src/main/java/com/tangem/tangem_sdk_new/TerminalKeysStorage.kt +++ b/tangem-sdk/src/main/java/com/tangem/tangem_sdk_new/TerminalKeysStorage.kt @@ -3,7 +3,7 @@ package com.tangem.tangem_sdk_new import android.app.Application import android.content.SharedPreferences import at.favre.lib.armadillo.Armadillo -import com.tangem.common.KeyPair +import com.tangem.KeyPair import com.tangem.common.TerminalKeysService import com.tangem.common.extensions.hexToBytes import com.tangem.common.extensions.toHexString @@ -12,7 +12,7 @@ import com.tangem.crypto.CryptoUtils /** * Service for managing Terminal keypair, used for Linked Terminal feature. - * Needs to be provided to [com.tangem.CardManager] by calling [com.tangem.CardManager.setTerminalKeysService] + * Needs to be provided to [com.tangem.TangemSdk] by calling [com.tangem.TangemSdk.setTerminalKeysService] * Linked Terminal feature can be disabled manually by editing [com.tangem.Config]. * @param applicationContext is required to retrieve an instance of [SharedPreferences] */ diff --git a/tangem-sdk/src/main/java/com/tangem/tangem_sdk_new/extensions/CardManager.kt b/tangem-sdk/src/main/java/com/tangem/tangem_sdk_new/extensions/TangemSdk.kt similarity index 62% rename from tangem-sdk/src/main/java/com/tangem/tangem_sdk_new/extensions/CardManager.kt rename to tangem-sdk/src/main/java/com/tangem/tangem_sdk_new/extensions/TangemSdk.kt index b2364c13cf..90a47a9d29 100644 --- a/tangem-sdk/src/main/java/com/tangem/tangem_sdk_new/extensions/CardManager.kt +++ b/tangem-sdk/src/main/java/com/tangem/tangem_sdk_new/extensions/TangemSdk.kt @@ -1,21 +1,21 @@ package com.tangem.tangem_sdk_new.extensions import androidx.fragment.app.FragmentActivity -import com.tangem.CardManager -import com.tangem.tangem_sdk_new.DefaultCardManagerDelegate +import com.tangem.TangemSdk +import com.tangem.tangem_sdk_new.DefaultCardSessionViewDelegate import com.tangem.tangem_sdk_new.NfcLifecycleObserver import com.tangem.tangem_sdk_new.TerminalKeysStorage import com.tangem.tangem_sdk_new.nfc.NfcManager -fun CardManager.Companion.init(activity: FragmentActivity): CardManager { +fun TangemSdk.Companion.init(activity: FragmentActivity): TangemSdk { val nfcManager = NfcManager().apply { this.setCurrentActivity(activity) activity.lifecycle.addObserver(NfcLifecycleObserver(this)) } - val cardManagerDelegate = DefaultCardManagerDelegate(nfcManager.reader).apply { + val viewDelegate = DefaultCardSessionViewDelegate(nfcManager.reader).apply { this.activity = activity } - return CardManager(nfcManager.reader, cardManagerDelegate).apply { + return TangemSdk(nfcManager.reader, viewDelegate).apply { this.setTerminalKeysService(TerminalKeysStorage(activity.application)) } } \ No newline at end of file diff --git a/tangem-sdk/src/main/java/com/tangem/tangem_sdk_new/nfc/NfcReader.kt b/tangem-sdk/src/main/java/com/tangem/tangem_sdk_new/nfc/NfcReader.kt index 5ba6b11d4b..845dbb2abd 100644 --- a/tangem-sdk/src/main/java/com/tangem/tangem_sdk_new/nfc/NfcReader.kt +++ b/tangem-sdk/src/main/java/com/tangem/tangem_sdk_new/nfc/NfcReader.kt @@ -6,10 +6,10 @@ import android.nfc.tech.IsoDep import android.nfc.tech.NfcV import com.tangem.CardReader import com.tangem.Log +import com.tangem.SessionError import com.tangem.common.CompletionResult import com.tangem.common.apdu.CommandApdu import com.tangem.common.apdu.ResponseApdu -import com.tangem.tasks.TaskError /** * Provides NFC communication between an Android application and Tangem card. @@ -38,7 +38,7 @@ class NfcReader : CardReader { // Stops reading and sends failure callback to a task // if reading is cancelled (when user closes nfc bottom sheet dialog). closeSession() - callback?.invoke(CompletionResult.Failure(TaskError.UserCancelled())) + callback?.invoke(CompletionResult.Failure(SessionError.UserCancelled())) } } @@ -67,7 +67,7 @@ class NfcReader : CardReader { private fun transceiveData() { if (readingCancelled) { - callback?.invoke(CompletionResult.Failure(TaskError.UserCancelled())) + callback?.invoke(CompletionResult.Failure(SessionError.UserCancelled())) return } if (data == null) return @@ -76,7 +76,7 @@ class NfcReader : CardReader { try { rawResponse = isoDep?.transceive(data) } catch (exception: TagLostException) { - callback?.invoke(CompletionResult.Failure(TaskError.TagLost())) + callback?.invoke(CompletionResult.Failure(SessionError.TagLost())) isoDep = null return } catch (exception: Exception) { @@ -110,7 +110,7 @@ class NfcReader : CardReader { when (response) { is SlixReadResult.Failure -> { Log.e(this::class.simpleName!!, "${response.exception.message}") - callback?.invoke(CompletionResult.Failure(TaskError.ErrorProcessingCommand())) + callback?.invoke(CompletionResult.Failure(SessionError.ErrorProcessingCommand())) } is SlixReadResult.Success -> { callback?.invoke(CompletionResult.Success(ResponseApdu(response.data))) diff --git a/tangem-sdk/src/main/java/com/tangem/tangem_sdk_new/nfc/SlixTagReader.kt b/tangem-sdk/src/main/java/com/tangem/tangem_sdk_new/nfc/SlixTagReader.kt index 587ded11a3..79f99b4eba 100644 --- a/tangem-sdk/src/main/java/com/tangem/tangem_sdk_new/nfc/SlixTagReader.kt +++ b/tangem-sdk/src/main/java/com/tangem/tangem_sdk_new/nfc/SlixTagReader.kt @@ -7,7 +7,7 @@ import com.tangem.common.apdu.StatusWord import com.tangem.common.extensions.toByteArray import com.tangem.common.extensions.toHexString import com.tangem.common.tlv.Tlv -import com.tangem.common.tlv.TlvMapper +import com.tangem.common.tlv.TlvDecoder import com.tangem.common.tlv.TlvTag import java.io.ByteArrayOutputStream import java.io.IOException @@ -64,9 +64,9 @@ class SlixTagReader() { val areaBuf = readMultipleBlocks(1, blocksCount) - val tlvNdef = TlvMapper(Tlv.deserialize(areaBuf, true) ?: listOf()) + val tlvNdef = TlvDecoder(Tlv.deserialize(areaBuf, true) ?: listOf()) - return NdefMessage(tlvNdef.map(TlvTag.CardPublicKey)) + return NdefMessage(tlvNdef.decode(TlvTag.CardPublicKey)) } private fun readSingleBlock(blockNo: Int): ByteArray { From 31a5de3eedea7f3ec104dca690818cb5f58f596e Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 10 Apr 2020 12:10:53 +0300 Subject: [PATCH 23/39] Updated on 2026-08-14 --- .../blockchain_demo/BlockchainDemoActivity.kt | 34 +++++++------------ .../bitcoin/BitcoinWalletManager.kt | 6 ++-- .../cardano/CardanoWalletManager.kt | 6 ++-- .../tangem/blockchain/common/WalletManager.kt | 4 +-- .../common/extensions/Coroutines.kt | 10 +++--- .../ethereum/EthereumWalletManager.kt | 6 ++-- .../stellar/StellarWalletManager.kt | 6 ++-- .../tangem/blockchain/xrp/XrpWalletManager.kt | 6 ++-- .../common/WalletManagerFactoryTest.kt | 2 +- 9 files changed, 36 insertions(+), 44 deletions(-) diff --git a/blockchain-demo/src/main/java/com/tangem/blockchain_demo/BlockchainDemoActivity.kt b/blockchain-demo/src/main/java/com/tangem/blockchain_demo/BlockchainDemoActivity.kt index 0028ee31c5..76d9ce0ffa 100644 --- a/blockchain-demo/src/main/java/com/tangem/blockchain_demo/BlockchainDemoActivity.kt +++ b/blockchain-demo/src/main/java/com/tangem/blockchain_demo/BlockchainDemoActivity.kt @@ -4,24 +4,23 @@ import android.os.Bundle import android.text.Editable import android.widget.Toast import androidx.appcompat.app.AppCompatActivity -import com.tangem.CardManager +import com.tangem.SessionError +import com.tangem.TangemSdk import com.tangem.blockchain.common.* import com.tangem.blockchain.common.extensions.Result import com.tangem.blockchain.common.extensions.Signer import com.tangem.blockchain.common.extensions.SimpleResult import com.tangem.blockchain_demo.databinding.ActivityBlockchainDemoBinding import com.tangem.commands.Card +import com.tangem.common.CompletionResult import com.tangem.tangem_sdk_new.extensions.init -import com.tangem.tasks.ScanEvent -import com.tangem.tasks.TaskError -import com.tangem.tasks.TaskEvent import kotlinx.coroutines.* import java.math.BigDecimal import kotlin.coroutines.CoroutineContext class BlockchainDemoActivity : AppCompatActivity() { - private lateinit var cardManager: CardManager + private lateinit var tangemSdk: TangemSdk private lateinit var signer: TransactionSigner private lateinit var card: Card private lateinit var walletManager: WalletManager @@ -47,8 +46,8 @@ class BlockchainDemoActivity : AppCompatActivity() { val view = binding.root setContentView(view) - cardManager = CardManager.init(this) - signer = Signer(cardManager) + tangemSdk = TangemSdk.init(this) + signer = Signer(tangemSdk) binding.btnScan.setOnClickListener { scan() } @@ -59,27 +58,20 @@ class BlockchainDemoActivity : AppCompatActivity() { private fun scan() { - cardManager.scanCard { taskEvent -> - when (taskEvent) { - is TaskEvent.Event -> { - when (taskEvent.data) { - is ScanEvent.OnReadEvent -> { - card = (taskEvent.data as ScanEvent.OnReadEvent).card - walletManager = WalletManagerFactory.makeWalletManager(card)!! + tangemSdk.scanCard { result -> + when (result) { + is CompletionResult.Success -> { + walletManager = WalletManagerFactory.makeWalletManager(result.data)!! getInfo() } - } - } - is TaskEvent.Completion -> { - if (taskEvent.error != null) { - if (taskEvent.error !is TaskError.UserCancelled) { - handleError(taskEvent.error.toString()) + is CompletionResult.Failure -> { + if (result.error !is SessionError.UserCancelled) { + handleError(result.error.toString()) } } } } } - } private fun getInfo() { scope.launch { diff --git a/blockchain/src/main/java/com/tangem/blockchain/bitcoin/BitcoinWalletManager.kt b/blockchain/src/main/java/com/tangem/blockchain/bitcoin/BitcoinWalletManager.kt index 58ae155e27..72a18f08fc 100644 --- a/blockchain/src/main/java/com/tangem/blockchain/bitcoin/BitcoinWalletManager.kt +++ b/blockchain/src/main/java/com/tangem/blockchain/bitcoin/BitcoinWalletManager.kt @@ -8,8 +8,8 @@ import com.tangem.blockchain.common.* import com.tangem.blockchain.common.extensions.Result import com.tangem.blockchain.common.extensions.SimpleResult import com.tangem.blockchain.wallets.CurrencyWallet +import com.tangem.common.CompletionResult import com.tangem.common.extensions.toHexString -import com.tangem.tasks.TaskEvent import java.math.BigDecimal class BitcoinWalletManager( @@ -66,11 +66,11 @@ class BitcoinWalletManager( is Result.Failure -> return SimpleResult.Failure(buildTransactionResult.error) is Result.Success -> { when (val signerResponse = signer.sign(buildTransactionResult.data.toTypedArray(), cardId)) { - is TaskEvent.Event -> { + is CompletionResult.Success -> { val transactionToSend = transactionBuilder.buildToSend(signerResponse.data.signature, walletPublicKey) return networkManager.sendTransaction(transactionToSend.toHexString()) } - is TaskEvent.Completion -> return SimpleResult.Failure(signerResponse.error) + is CompletionResult.Failure -> return SimpleResult.Failure(signerResponse.error) } } } diff --git a/blockchain/src/main/java/com/tangem/blockchain/cardano/CardanoWalletManager.kt b/blockchain/src/main/java/com/tangem/blockchain/cardano/CardanoWalletManager.kt index edf2acefbb..86b5f25a51 100644 --- a/blockchain/src/main/java/com/tangem/blockchain/cardano/CardanoWalletManager.kt +++ b/blockchain/src/main/java/com/tangem/blockchain/cardano/CardanoWalletManager.kt @@ -8,7 +8,7 @@ import com.tangem.blockchain.common.extensions.Result import com.tangem.blockchain.common.extensions.SimpleResult import com.tangem.blockchain.common.extensions.encodeBase64NoWrap import com.tangem.blockchain.wallets.CurrencyWallet -import com.tangem.tasks.TaskEvent +import com.tangem.common.CompletionResult class CardanoWalletManager( private val cardId: String, @@ -47,11 +47,11 @@ class CardanoWalletManager( val transactionHash = transactionBuilder.buildToSign(transactionData) when (val signerResponse = signer.sign(arrayOf(transactionHash), cardId)) { - is TaskEvent.Event -> { + is CompletionResult.Success -> { val transactionToSend = transactionBuilder.buildToSend(signerResponse.data.signature, walletPublicKey) return networkManager.sendTransaction(transactionToSend.encodeBase64NoWrap()) } - is TaskEvent.Completion -> return SimpleResult.Failure(signerResponse.error) + is CompletionResult.Failure -> return SimpleResult.Failure(signerResponse.error) } } diff --git a/blockchain/src/main/java/com/tangem/blockchain/common/WalletManager.kt b/blockchain/src/main/java/com/tangem/blockchain/common/WalletManager.kt index c39d9b6c0e..6fe5a862ea 100644 --- a/blockchain/src/main/java/com/tangem/blockchain/common/WalletManager.kt +++ b/blockchain/src/main/java/com/tangem/blockchain/common/WalletManager.kt @@ -4,7 +4,7 @@ import com.tangem.blockchain.common.extensions.Result import com.tangem.blockchain.common.extensions.SimpleResult import com.tangem.blockchain.wallets.CurrencyWallet import com.tangem.commands.SignResponse -import com.tangem.tasks.TaskEvent +import com.tangem.common.CompletionResult interface WalletManager { var wallet: CurrencyWallet @@ -18,7 +18,7 @@ interface TransactionSender { } interface TransactionSigner { - suspend fun sign(hashes: Array, cardId: String): TaskEvent + suspend fun sign(hashes: Array, cardId: String): CompletionResult } interface FeeProvider { diff --git a/blockchain/src/main/java/com/tangem/blockchain/common/extensions/Coroutines.kt b/blockchain/src/main/java/com/tangem/blockchain/common/extensions/Coroutines.kt index cdf9edd92e..8451a23b67 100644 --- a/blockchain/src/main/java/com/tangem/blockchain/common/extensions/Coroutines.kt +++ b/blockchain/src/main/java/com/tangem/blockchain/common/extensions/Coroutines.kt @@ -1,9 +1,9 @@ package com.tangem.blockchain.common.extensions -import com.tangem.CardManager +import com.tangem.TangemSdk import com.tangem.blockchain.common.TransactionSigner import com.tangem.commands.SignResponse -import com.tangem.tasks.TaskEvent +import com.tangem.common.CompletionResult import kotlinx.coroutines.delay import kotlinx.coroutines.suspendCancellableCoroutine import java.io.IOException @@ -54,10 +54,10 @@ sealed class SimpleResult { data class Failure(val error: Throwable?) : SimpleResult() } -class Signer(private val cardManager: CardManager) : TransactionSigner { - override suspend fun sign(hashes: Array, cardId: String): TaskEvent = +class Signer(private val tangemSdk: TangemSdk) : TransactionSigner { + override suspend fun sign(hashes: Array, cardId: String): CompletionResult = suspendCancellableCoroutine { continuation -> - cardManager.sign(hashes, cardId) { result -> + tangemSdk.sign(hashes, cardId) { result -> if (continuation.isActive) continuation.resume(result) } } diff --git a/blockchain/src/main/java/com/tangem/blockchain/ethereum/EthereumWalletManager.kt b/blockchain/src/main/java/com/tangem/blockchain/ethereum/EthereumWalletManager.kt index d7c26e526e..3999bdf7a3 100644 --- a/blockchain/src/main/java/com/tangem/blockchain/ethereum/EthereumWalletManager.kt +++ b/blockchain/src/main/java/com/tangem/blockchain/ethereum/EthereumWalletManager.kt @@ -8,8 +8,8 @@ import com.tangem.blockchain.common.extensions.SimpleResult import com.tangem.blockchain.ethereum.network.EthereumNetworkManager import com.tangem.blockchain.ethereum.network.EthereumResponse import com.tangem.blockchain.wallets.CurrencyWallet +import com.tangem.common.CompletionResult import com.tangem.common.extensions.toHexString -import com.tangem.tasks.TaskEvent import org.kethereum.DEFAULT_GAS_LIMIT import org.kethereum.crypto.api.ec.ECDSASignature import org.kethereum.crypto.determineRecId @@ -81,11 +81,11 @@ class EthereumWalletManager( val transactionToSign = builder.buildToSign(transactionData, txCount.toBigInteger()) ?: return SimpleResult.Failure(Exception("Not enough data")) when (val signerResponse = signer.sign(transactionToSign.hashes.toTypedArray(), cardId)) { - is TaskEvent.Event -> { + is CompletionResult.Success -> { val transactionToSend = builder.buildToSend(signerResponse.data.signature, transactionToSign, walletPublicKey) return networkManager.sendTransaction(String.format("0x%s", transactionToSend.toHexString())) } - is TaskEvent.Completion -> return SimpleResult.Failure(signerResponse.error) + is CompletionResult.Failure -> return SimpleResult.Failure(signerResponse.error) } } diff --git a/blockchain/src/main/java/com/tangem/blockchain/stellar/StellarWalletManager.kt b/blockchain/src/main/java/com/tangem/blockchain/stellar/StellarWalletManager.kt index e65278fa35..754cf7fa60 100644 --- a/blockchain/src/main/java/com/tangem/blockchain/stellar/StellarWalletManager.kt +++ b/blockchain/src/main/java/com/tangem/blockchain/stellar/StellarWalletManager.kt @@ -5,7 +5,7 @@ import com.tangem.blockchain.common.* import com.tangem.blockchain.common.extensions.Result import com.tangem.blockchain.common.extensions.SimpleResult import com.tangem.blockchain.wallets.CurrencyWallet -import com.tangem.tasks.TaskEvent +import com.tangem.common.CompletionResult import java.math.BigDecimal import java.util.* @@ -58,11 +58,11 @@ class StellarWalletManager( override suspend fun send(transactionData: TransactionData, signer: TransactionSigner): SimpleResult { val hashes = builder.buildToSign(transactionData, sequence, baseFee.toStroops()) when (val signerResponse = signer.sign(hashes.toTypedArray(), cardId)) { - is TaskEvent.Event -> { + is CompletionResult.Success -> { val transactionToSend = builder.buildToSend(signerResponse.data.signature) return networkManager.sendTransaction(transactionToSend) } - is TaskEvent.Completion -> return SimpleResult.Failure(signerResponse.error) + is CompletionResult.Failure -> return SimpleResult.Failure(signerResponse.error) } } diff --git a/blockchain/src/main/java/com/tangem/blockchain/xrp/XrpWalletManager.kt b/blockchain/src/main/java/com/tangem/blockchain/xrp/XrpWalletManager.kt index bba2e26427..cf6c8206a8 100644 --- a/blockchain/src/main/java/com/tangem/blockchain/xrp/XrpWalletManager.kt +++ b/blockchain/src/main/java/com/tangem/blockchain/xrp/XrpWalletManager.kt @@ -7,7 +7,7 @@ import com.tangem.blockchain.common.extensions.SimpleResult import com.tangem.blockchain.wallets.CurrencyWallet import com.tangem.blockchain.xrp.network.XrpInfoResponse import com.tangem.blockchain.xrp.network.XrpNetworkManager -import com.tangem.tasks.TaskEvent +import com.tangem.common.CompletionResult class XrpWalletManager( private val cardId: String, @@ -68,11 +68,11 @@ class XrpWalletManager( val transactionHash = transactionBuilder.buildToSign(transactionData) when (val signerResponse = signer.sign(arrayOf(transactionHash), cardId)) { - is TaskEvent.Event -> { + is CompletionResult.Success -> { val transactionToSend = transactionBuilder.buildToSend(signerResponse.data.signature) return networkManager.sendTransaction(transactionToSend) } - is TaskEvent.Completion -> return SimpleResult.Failure(signerResponse.error) + is CompletionResult.Failure -> return SimpleResult.Failure(signerResponse.error) } } diff --git a/blockchain/src/test/java/com/tangem/blockchain/common/WalletManagerFactoryTest.kt b/blockchain/src/test/java/com/tangem/blockchain/common/WalletManagerFactoryTest.kt index a476130557..58034631e7 100644 --- a/blockchain/src/test/java/com/tangem/blockchain/common/WalletManagerFactoryTest.kt +++ b/blockchain/src/test/java/com/tangem/blockchain/common/WalletManagerFactoryTest.kt @@ -1,7 +1,7 @@ package com.tangem.blockchain.common import com.google.common.truth.Truth -import com.tangem.common.CardEnvironment +import com.tangem.CardEnvironment import com.tangem.blockchain.bitcoin.BitcoinWalletManager import com.tangem.blockchain.cardano.CardanoWalletManager import com.tangem.blockchain.ethereum.EthereumWalletManager From 08cfad4489be83c37a4ddd8be566afed6b2f791f Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 10 Apr 2020 12:17:56 +0300 Subject: [PATCH 24/39] Updated on 2026-08-14 --- .idea/dictionaries/tangem.xml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 .idea/dictionaries/tangem.xml diff --git a/.idea/dictionaries/tangem.xml b/.idea/dictionaries/tangem.xml new file mode 100644 index 0000000000..e362578668 --- /dev/null +++ b/.idea/dictionaries/tangem.xml @@ -0,0 +1,16 @@ + + + + blockchain + tangem + testnet + ethereum + cardano + binance + tezos + ducatus + litecoin + matic + + + \ No newline at end of file From 7700a1250e1873cb98871d054fb50ab3ae0dc3d6 Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 10 Apr 2020 12:18:50 +0300 Subject: [PATCH 25/39] Updated on 2026-08-14 --- .idea/dictionaries/tangem.xml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.idea/dictionaries/tangem.xml b/.idea/dictionaries/tangem.xml index e362578668..e77bbeb7ed 100644 --- a/.idea/dictionaries/tangem.xml +++ b/.idea/dictionaries/tangem.xml @@ -1,16 +1,16 @@ - blockchain - tangem - testnet - ethereum - cardano binance - tezos + blockchain + cardano ducatus + ethereum litecoin matic + tangem + testnet + tezos \ No newline at end of file From bcf885568f196707ac1e5a639fa839a3bcaee243 Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 10 Apr 2020 12:26:42 +0300 Subject: [PATCH 26/39] Updated on 2026-08-14 --- ...nitializer.kt => ResponseJsonConverter.kt} | 10 +++++++++- .../ucase/resources/MainResourceHolder.kt | 20 +++++++++++++++++++ .../tangemtest/ucase/ui/ActionViewModel.kt | 4 ++-- .../personalize/ui/widgets/BaseAppWidget.kt | 14 ++++++------- .../ui/widgets/DescriptionWidget.kt | 2 +- .../responses/converter/CardConverter.kt | 11 +++++----- .../variants/responses/ui/ResponseFragment.kt | 6 ++++-- .../responses/ui/widget/ResponseWidget.kt | 5 +++-- 8 files changed, 52 insertions(+), 20 deletions(-) rename tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/responses/{GsonInitializer.kt => ResponseJsonConverter.kt} (89%) diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/responses/GsonInitializer.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/responses/ResponseJsonConverter.kt similarity index 89% rename from tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/responses/GsonInitializer.kt rename to tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/responses/ResponseJsonConverter.kt index 4d6e2407d7..84695d90fb 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/responses/GsonInitializer.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/responses/ResponseJsonConverter.kt @@ -6,6 +6,7 @@ import com.tangem.commands.Settings import com.tangem.commands.SettingsMask import com.tangem.commands.SigningMethod import com.tangem.common.extensions.toHexString +import com.tangem.tasks.TaskEvent import java.lang.reflect.Type import java.text.DateFormat import java.util.* @@ -13,7 +14,7 @@ import java.util.* /** [REDACTED_AUTHOR] */ -class GsonInitializer { +class ResponseJsonConverter { val gson: Gson by lazy { init() } private fun init(): Gson { @@ -27,6 +28,13 @@ class GsonInitializer { builder.setPrettyPrinting() return builder.create() } + + fun convertTaskEvent(task: TaskEvent<*>?): String = gson.toJson(task) + + fun convertEvent(task: TaskEvent<*>?): String { + val event = task as? TaskEvent.Event ?: "" + return gson.toJson(event) + } } class ByteTypeAdapter : JsonSerializer { diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/resources/MainResourceHolder.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/resources/MainResourceHolder.kt index cf6a61abbc..86c7454365 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/resources/MainResourceHolder.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/resources/MainResourceHolder.kt @@ -1,7 +1,10 @@ package com.tangem.tangemtest.ucase.resources +import com.tangem.tangemtest.AppTangemDemo 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.StringResId import com.tangem.tangemtest.ucase.resources.initializers.ActionResources import com.tangem.tangemtest.ucase.resources.initializers.PersonalizationResources import com.tangem.tangemtest.ucase.resources.initializers.ResponseResources @@ -21,6 +24,23 @@ object MainResourceHolder : ResourceHolder() { ResponseResources().init(this) } + override fun get(type: Id): Resources? { + return when(type) { + is StringId -> getResourcesFromName(type.value) + is StringResId -> Resources(type.value) + else -> super.get(type) + } + } + + private fun getResourcesFromName(resName: String): Resources? { + val context = AppTangemDemo.appInstance.applicationContext + val resNameId = context.resources.getIdentifier(resName, "string", context.packageName) + val resInfoId = context.resources.getIdentifier("info_$resName", "string", context.packageName) + if (resNameId == 0) return null + + return Resources(resNameId, if (resInfoId == 0) null else resInfoId) + } + inline fun safeGet(id: Id): Res { val result = super.get(id) ?: Resources(R.string.unknown, R.string.unknown) return result as Res diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/ActionViewModel.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/ActionViewModel.kt index e1b63c7df7..a44367f98c 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/ActionViewModel.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/ActionViewModel.kt @@ -12,7 +12,7 @@ import com.tangem.tangemtest._arch.structure.abstraction.Item import com.tangem.tangemtest._arch.structure.abstraction.iterate import com.tangem.tangemtest.commons.performAction import com.tangem.tangemtest.ucase.domain.paramsManager.ItemsManager -import com.tangem.tangemtest.ucase.domain.responses.GsonInitializer +import com.tangem.tangemtest.ucase.domain.responses.ResponseJsonConverter import com.tangem.tangemtest.ucase.tunnel.ViewScreen import com.tangem.tasks.ScanEvent import com.tangem.tasks.TaskError @@ -92,7 +92,7 @@ class ActionViewModel(private val itemsManager: ItemsManager) : ViewModel(), Lif internal class Notifier(private val vm: ActionViewModel) { private var notShowedError: TaskError? = null - private val gson: Gson = GsonInitializer().gson + private val gson: Gson = ResponseJsonConverter().gson fun handleActionResult(response: TaskEvent<*>, list: List) { if (list.isNotEmpty()) notifyItemsChanged(list) diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/BaseAppWidget.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/BaseAppWidget.kt index 65f110c8c2..5900b5da37 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/BaseAppWidget.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/BaseAppWidget.kt @@ -2,7 +2,6 @@ package com.tangem.tangemtest.ucase.variants.personalize.ui.widgets import android.view.ViewGroup import com.tangem.tangemtest._arch.structure.StringId -import com.tangem.tangemtest._arch.structure.StringResId import com.tangem.tangemtest._arch.structure.abstraction.Item import com.tangem.tangemtest._arch.widget.abstraction.BaseViewWidget import com.tangem.tangemtest._arch.widget.abstraction.ViewWidget @@ -15,14 +14,15 @@ import ru.dev.gbixahue.eu4d.lib.android._android.views.stringFrom */ abstract class BaseAppWidget(parent: ViewGroup, item: Item) : BaseViewWidget(parent, item) { override fun getName(): String { - return when (val id = item.id) { - is StringId -> id.value - is StringResId -> view.stringFrom(id.value) - else -> view.stringFrom(getResNameId()) - } + val idName = if (item.id is StringId) (item.id as StringId).value else "" + + val resources = getResources() ?: return idName + return view.stringFrom(resources.resName) } } +fun ViewWidget.getResources(): Resources? = MainResourceHolder.get(item.id) + fun ViewWidget.getResNameId(): Int = MainResourceHolder.safeGet(item.id).resName -fun ViewWidget.getResDescription(): Int? = MainResourceHolder.safeGet(item.id).resDescription \ No newline at end of file +fun ViewWidget.getResDescriptionId(): Int? = MainResourceHolder.get(item.id)?.resDescription \ No newline at end of file diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/DescriptionWidget.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/DescriptionWidget.kt index 0e49f54ec5..b1d699f972 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/DescriptionWidget.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/DescriptionWidget.kt @@ -30,7 +30,7 @@ abstract class DescriptionWidget( protected open fun changeDescriptionVisibility(state: Int) { val tv = tvDescription ?: return - val descriptionId = getResDescription() ?: return + val descriptionId = getResDescriptionId() ?: return val description = tv.context.getString(descriptionId) if (description.isEmpty()) return 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 40db39e261..374cdaaf20 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 @@ -4,6 +4,7 @@ import com.tangem.commands.Card import com.tangem.commands.CardData import com.tangem.commands.Settings import com.tangem.commands.SettingsMask +import com.tangem.common.extensions.toHexString import com.tangem.tangemtest.R import com.tangem.tangemtest._arch.structure.Id import com.tangem.tangemtest._arch.structure.StringId @@ -51,11 +52,11 @@ class CardConverter : ModelToItems { group.addItem(TextItem(CardId.manufacturerName, from.manufacturerName)) group.addItem(TextItem(CardId.status, stringOf(from.status))) group.addItem(TextItem(CardId.firmwareVersion, from.firmwareVersion)) - group.addItem(TextItem(CardId.cardPublicKey, stringOf(from.cardPublicKey))) - group.addItem(TextItem(CardId.issuerPublicKey, stringOf(from.issuerPublicKey))) + group.addItem(TextItem(CardId.cardPublicKey, from.cardPublicKey?.toHexString())) + group.addItem(TextItem(CardId.issuerPublicKey, from.issuerPublicKey?.toHexString())) group.addItem(TextItem(CardId.curve, stringOf(from.curve))) group.addItem(TextItem(CardId.maxSignatures, stringOf(from.maxSignatures))) - group.addItem(TextItem(CardId.signingMethod, stringOf(from.signingMethod))) + group.addItem(TextItem(CardId.signingMethod, stringOf(from.signingMethod?.rawValue))) group.addItem(TextItem(CardId.pauseBeforePin2, stringOf(from.pauseBeforePin2))) group.addItem(TextItem(CardId.walletPublicKey, stringOf(from.walletPublicKey))) group.addItem(TextItem(CardId.walletRemainingSignatures, stringOf(from.walletRemainingSignatures))) @@ -79,8 +80,8 @@ class CardConverter : ModelToItems { group.addItem(TextItem(CardDataId.manufactureDateTime, stringOf(data.manufactureDateTime))) group.addItem(TextItem(CardDataId.issuerName, data.issuerName)) group.addItem(TextItem(CardDataId.blockchainName, data.blockchainName)) - group.addItem(TextItem(CardDataId.manufacturerSignature, stringOf(data.manufacturerSignature))) - group.addItem(TextItem(CardDataId.productMask, stringOf(data.productMask))) + group.addItem(TextItem(CardDataId.manufacturerSignature, data.manufacturerSignature?.toHexString())) + group.addItem(TextItem(CardDataId.productMask, stringOf(data.productMask?.rawValue))) group.addItem(TextItem(CardDataId.tokenSymbol, data.tokenSymbol)) group.addItem(TextItem(CardDataId.tokenContractAddress, data.tokenContractAddress)) group.addItem(TextItem(CardDataId.tokenDecimal, data.tokenSymbol)) diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/responses/ui/ResponseFragment.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/responses/ui/ResponseFragment.kt index e5cc169f68..4477f12894 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/responses/ui/ResponseFragment.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/responses/ui/ResponseFragment.kt @@ -6,11 +6,11 @@ import android.widget.LinearLayout import androidx.fragment.app.activityViewModels import androidx.fragment.app.viewModels import androidx.lifecycle.Observer -import com.google.gson.Gson import com.tangem.tangemtest.R import com.tangem.tangemtest._arch.widget.WidgetBuilder import com.tangem.tangemtest._main.MainViewModel import com.tangem.tangemtest.extensions.shareText +import com.tangem.tangemtest.ucase.domain.responses.ResponseJsonConverter import com.tangem.tangemtest.ucase.ui.BaseFragment import com.tangem.tangemtest.ucase.variants.responses.ResponseViewModel import com.tangem.tangemtest.ucase.variants.responses.ui.widget.ResponseItemBuilder @@ -66,7 +66,9 @@ open class ResponseFragment : BaseFragment() { override fun onOptionsItemSelected(item: MenuItem): Boolean { when (item.itemId) { - R.id.action_share -> shareText(Gson().toJson(mainActivityVM.responseEvent)) + R.id.action_share -> { + shareText(ResponseJsonConverter().convertEvent(mainActivityVM.responseEvent)) + } } return super.onOptionsItemSelected(item) } diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/responses/ui/widget/ResponseWidget.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/responses/ui/widget/ResponseWidget.kt index d08de11386..054ab81dbc 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/responses/ui/widget/ResponseWidget.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/responses/ui/widget/ResponseWidget.kt @@ -22,11 +22,12 @@ abstract class ResponseWidget(parent: ViewGroup, item: Item): DescriptionWidget( val clipboard = view.context.getSystemService(Context.CLIPBOARD_SERVICE) as? ClipboardManager ?: return@setOnClickListener - val clip: ClipData = ClipData.newPlainText("FieldValue", "$data") + val clipboardData = "${getName()} - $data" + val clip: ClipData = ClipData.newPlainText("FieldValue", clipboardData) clipboard.setPrimaryClip(clip) val copyMessage = view.stringFrom(R.string.copy_to_clipboard) - view.toast("$copyMessage: ${getName()} - $data") + view.toast("$copyMessage\n$clipboardData") } } } \ No newline at end of file From 64a7baa2c0190a61ed20d44c3a3394a8d044ea66 Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 10 Apr 2020 12:36:15 +0300 Subject: [PATCH 27/39] Updated on 2026-08-14 --- .../com/tangem/tangemtest/Old_MainActivity.kt | 107 +++++++----------- .../tangem/tangemtest/TestUserDataActivity.kt | 93 +++++---------- .../tangemtest/ucase/domain/actions/Action.kt | 12 +- .../domain/actions/DepersonalizeAction.kt | 2 +- .../ucase/domain/actions/PersonalizeAction.kt | 2 +- .../ucase/domain/actions/ScanAction.kt | 2 +- .../ucase/domain/actions/SignAction.kt | 2 +- .../domain/paramsManager/ItemsManager.kt | 10 +- .../managers/BaseItemsManager.kt | 8 +- .../managers/DepersonalizeItemsManager.kt | 10 +- .../managers/PersonalizeItemsManager.kt | 6 +- .../managers/ScanItemsManager.kt | 6 +- .../managers/SignItemsManager.kt | 10 +- .../afterAction/AfterActionModifiers.kt | 4 +- .../triggers/afterAction/AfterScanModifier.kt | 12 +- .../ucase/ui/BaseCardActionFragment.kt | 4 +- .../tangemtest/ucase/ui/ParamsViewModel.kt | 75 ++++++------ .../dto/DefaultPersonalizeParams.kt | 2 +- .../personalize/ui/PersonalizeFragment.kt | 4 +- 19 files changed, 155 insertions(+), 216 deletions(-) diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/Old_MainActivity.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/Old_MainActivity.kt index b39081b6b0..40998ca19b 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/Old_MainActivity.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/Old_MainActivity.kt @@ -3,17 +3,14 @@ package com.tangem.tangemtest import android.content.Intent import android.os.Bundle import androidx.appcompat.app.AppCompatActivity -import com.tangem.CardManager +import com.tangem.TangemSdk +import com.tangem.common.CompletionResult import com.tangem.tangem_sdk_new.extensions.init -import com.tangem.tangemtest.extensions.create -import com.tangem.tasks.ScanEvent -import com.tangem.tasks.TaskError -import com.tangem.tasks.TaskEvent import kotlinx.android.synthetic.main.old_activity_main.* class Old_MainActivity : AppCompatActivity() { - private lateinit var cardManager: CardManager + private lateinit var tangemSdk: TangemSdk private lateinit var cardId: String private lateinit var issuerData: ByteArray private lateinit var issuerDataSignature: ByteArray @@ -23,66 +20,50 @@ class Old_MainActivity : AppCompatActivity() { super.onCreate(savedInstanceState) setContentView(R.layout.old_activity_main) - cardManager = CardManager.init(this) + tangemSdk = TangemSdk.init(this) btn_scan?.setOnClickListener { _ -> - cardManager.scanCard { taskEvent -> + tangemSdk.scanCard { taskEvent -> when (taskEvent) { - is TaskEvent.Event -> { - when (taskEvent.data) { - is ScanEvent.OnReadEvent -> { - // Handle returned card data - cardId = (taskEvent.data as ScanEvent.OnReadEvent).card.cardId - runOnUiThread { - tv_card_cid?.text = cardId - btn_create_wallet.isEnabled = true - } - } - is ScanEvent.OnVerifyEvent -> { - //Handle card verification - runOnUiThread { - tv_card_cid?.text = cardId - btn_sign.isEnabled = true - btn_read_issuer_data.isEnabled = true - btn_read_issuer_extra_data.isEnabled = true - btn_write_issuer_data.isEnabled = true - btn_purge_wallet.isEnabled = true - btn_create_wallet.isEnabled = true - } - } + is CompletionResult.Success -> { + // Handle returned card data + val card = taskEvent.data + cardId = card.cardId + runOnUiThread { + tv_card_cid?.text = cardId + btn_create_wallet.isEnabled = true + tv_card_cid?.text = cardId + btn_sign.isEnabled = true + btn_read_issuer_data.isEnabled = true + btn_read_issuer_extra_data.isEnabled = true + btn_write_issuer_data.isEnabled = true + btn_purge_wallet.isEnabled = true + btn_create_wallet.isEnabled = true + } } - is TaskEvent.Completion -> { - if (taskEvent.error != null) { - if (taskEvent.error is TaskError.UserCancelled) { - // Handle case when user cancelled manually - } - // Handle other errors - } - // Handle completion - } } } } btn_sign?.setOnClickListener { _ -> - cardManager.sign( + tangemSdk.sign( createSampleHashes(), cardId) { when (it) { - is TaskEvent.Completion -> { - if (it.error != null) runOnUiThread { tv_card_cid?.text = it.error!!::class.simpleName } + is CompletionResult.Failure -> { + runOnUiThread { tv_card_cid?.text = it.error::class.simpleName } } - is TaskEvent.Event -> runOnUiThread { tv_card_cid?.text = cardId + "was used to sign sample hashes." } + is CompletionResult.Success -> runOnUiThread { tv_card_cid?.text = cardId + "was used to sign sample hashes." } } } } btn_read_issuer_data?.setOnClickListener { _ -> - cardManager.readIssuerData(cardId) { + tangemSdk.readIssuerData(cardId) { when (it) { - is TaskEvent.Completion -> { - if (it.error != null) runOnUiThread { tv_card_cid?.text = it.error!!::class.simpleName } + is CompletionResult.Failure -> { + runOnUiThread { tv_card_cid?.text = it.error::class.simpleName } } - is TaskEvent.Event -> runOnUiThread { + is CompletionResult.Success -> runOnUiThread { btn_write_issuer_data.isEnabled = true tv_card_cid?.text = it.data.issuerData.contentToString() issuerData = it.data.issuerData @@ -92,27 +73,27 @@ class Old_MainActivity : AppCompatActivity() { } } btn_write_issuer_data?.setOnClickListener { _ -> - cardManager.writeIssuerData( + tangemSdk.writeIssuerData( cardId, issuerData, issuerDataSignature) { when (it) { - is TaskEvent.Completion -> { - if (it.error != null) runOnUiThread { tv_card_cid?.text = it.error!!::class.simpleName } + is CompletionResult.Failure -> { + runOnUiThread { tv_card_cid?.text = it.error::class.simpleName } } - is TaskEvent.Event -> runOnUiThread { + is CompletionResult.Success -> runOnUiThread { tv_card_cid?.text = it.data.cardId } } } } btn_read_issuer_extra_data?.setOnClickListener { _ -> - cardManager.readIssuerExtraData(cardId) { + tangemSdk.readIssuerExtraData(cardId) { when (it) { - is TaskEvent.Completion -> { - if (it.error != null) runOnUiThread { tv_card_cid?.text = it.error!!::class.simpleName } + is CompletionResult.Failure -> { + runOnUiThread { tv_card_cid?.text = it.error::class.simpleName } } - is TaskEvent.Event -> runOnUiThread { + is CompletionResult.Success -> runOnUiThread { issuerDataCounter = (it.data.issuerDataCounter ?: 0) + 1 btn_write_issuer_data.isEnabled = true tv_card_cid?.text = "Read ${it.data.issuerData.size} bytes of data." @@ -121,26 +102,26 @@ class Old_MainActivity : AppCompatActivity() { } } btn_purge_wallet?.setOnClickListener { _ -> - cardManager.purgeWallet( + tangemSdk.purgeWallet( cardId) { when (it) { - is TaskEvent.Completion -> { - if (it.error != null) runOnUiThread { tv_card_cid?.text = it.error!!::class.simpleName } + is CompletionResult.Failure -> { + runOnUiThread { tv_card_cid?.text = it.error::class.simpleName } } - is TaskEvent.Event -> runOnUiThread { + is CompletionResult.Success -> runOnUiThread { tv_card_cid?.text = it.data.status.name } } } } btn_create_wallet?.setOnClickListener { _ -> - cardManager.createWallet( + tangemSdk.createWallet( cardId) { when (it) { - is TaskEvent.Completion -> { - if (it.error != null) runOnUiThread { tv_card_cid?.text = it.error!!::class.simpleName } + is CompletionResult.Failure -> { + runOnUiThread { tv_card_cid?.text = it.error::class.simpleName } } - is TaskEvent.Event -> runOnUiThread { + is CompletionResult.Success -> runOnUiThread { tv_card_cid?.text = it.data.status.name btn_sign.isEnabled = true btn_read_issuer_data.isEnabled = true diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/TestUserDataActivity.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/TestUserDataActivity.kt index 9ffcba3103..0df7bd9aa6 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/TestUserDataActivity.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/TestUserDataActivity.kt @@ -5,14 +5,11 @@ import android.view.View import android.widget.CompoundButton import android.widget.TextView import androidx.appcompat.app.AppCompatActivity -import com.tangem.CardManager -import com.tangem.commands.ReadUserDataResponse -import com.tangem.commands.WriteUserDataResponse -import com.tangem.common.CardEnvironment +import com.tangem.CardEnvironment +import com.tangem.SessionError +import com.tangem.TangemSdk +import com.tangem.common.CompletionResult import com.tangem.tangem_sdk_new.extensions.init -import com.tangem.tasks.ScanEvent -import com.tangem.tasks.TaskError -import com.tangem.tasks.TaskEvent import kotlinx.android.synthetic.main.activity_test_user_data.* import java.nio.charset.StandardCharsets @@ -21,7 +18,7 @@ import java.nio.charset.StandardCharsets */ class TestUserDataActivity : AppCompatActivity() { - private lateinit var cardManager: CardManager + private lateinit var tangemSdk: TangemSdk private lateinit var writeOptions: WriteOptions override fun onCreate(savedInstanceState: Bundle?) { @@ -33,31 +30,15 @@ class TestUserDataActivity : AppCompatActivity() { } private fun init() { - cardManager = CardManager.init(this) + tangemSdk = TangemSdk.init(this) btn_scan?.setOnClickListener { _ -> - cardManager.scanCard { taskEvent -> + tangemSdk.scanCard { taskEvent -> when (taskEvent) { - is TaskEvent.Event -> { - when (taskEvent.data) { - is ScanEvent.OnReadEvent -> { - // Handle returned card data - writeOptions.cardId = (taskEvent.data as ScanEvent.OnReadEvent).card.cardId - runOnUiThread { showReadWriteSection(true) } - } - is ScanEvent.OnVerifyEvent -> { - //Handle card verification - } - } - } - is TaskEvent.Completion -> { - if (taskEvent.error != null) { - if (taskEvent.error is TaskError.UserCancelled) { - // Handle case when user cancelled manually - } - // Handle other errors - } - // Handle completion + is CompletionResult.Success -> { + // Handle returned card data + writeOptions.cardId = taskEvent.data.cardId + runOnUiThread { showReadWriteSection(true) } } } } @@ -66,7 +47,7 @@ class TestUserDataActivity : AppCompatActivity() { btn_write.setOnClickListener { if (writeOptions.cardId == null) return@setOnClickListener - cardManager.writeUserData( + tangemSdk.writeUserData( writeOptions.cardId!!, writeOptions.userData, writeOptions.userProtectedData, @@ -74,16 +55,9 @@ class TestUserDataActivity : AppCompatActivity() { writeOptions.userProtectedCounter ) { when (it) { - is TaskEvent.Completion -> handleError(tv_write_result, it.error) - is TaskEvent.Event -> { - runOnUiThread { - val data = it.data as? WriteUserDataResponse - if (data == null) { - tv_write_result.text = "Response doesn't match" - return@runOnUiThread - } - tv_write_result?.text = "Success" - } + is CompletionResult.Failure -> handleError(tv_write_result, it.error) + is CompletionResult.Success -> { + runOnUiThread { tv_write_result?.text = "Success" } } } } @@ -92,28 +66,24 @@ class TestUserDataActivity : AppCompatActivity() { btn_read.setOnClickListener { if (writeOptions.cardId == null) return@setOnClickListener - cardManager.readUserData(writeOptions.cardId!!) { + tangemSdk.readUserData(writeOptions.cardId!!) { when (it) { - is TaskEvent.Completion -> handleError(tv_read_result, it.error) - is TaskEvent.Event -> { + is CompletionResult.Failure -> handleError(tv_write_result, it.error) + is CompletionResult.Success -> { runOnUiThread { - val data = it.data as? ReadUserDataResponse - if (data == null) { - tv_read_result.text = "Response doesn't match" - return@runOnUiThread - } + tv_read_result?.text = "Success" - writeOptions.userData = data.userData - writeOptions.userProtectedData = data.userProtectedData - writeOptions.userCounter = data.userCounter - writeOptions.userProtectedCounter = data.userProtectedCounter + writeOptions.userData = it.data.userData + writeOptions.userProtectedData = it.data.userProtectedData + writeOptions.userCounter = it.data.userCounter + writeOptions.userProtectedCounter = it.data.userProtectedCounter - tv_card_cid.text = data.cardId - tv_data.text = String(data.userData, StandardCharsets.US_ASCII) - tv_protected_data.text = String(data.userProtectedData, StandardCharsets.US_ASCII) - tv_counter.text = data.userCounter.toString() - tv_protected_counter.text = data.userProtectedCounter.toString() + tv_card_cid.text = it.data.cardId + tv_data.text = String(it.data.userData, StandardCharsets.US_ASCII) + tv_protected_data.text = String(it.data.userProtectedData, StandardCharsets.US_ASCII) + tv_counter.text = it.data.userCounter.toString() + tv_protected_counter.text = it.data.userProtectedCounter.toString() } } @@ -122,11 +92,10 @@ class TestUserDataActivity : AppCompatActivity() { } } - private fun handleError(tv: TextView, error: TaskError?) { - val er = error ?: return - if (er is TaskError.UserCancelled) return + private fun handleError(tv: TextView, error: SessionError) { + if (error is SessionError.UserCancelled) return - runOnUiThread { tv.text = er::class.simpleName } + runOnUiThread { tv.text = error::class.simpleName } } private fun initWriteOptions() { diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/actions/Action.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/actions/Action.kt index 285c8de4b6..6e05a899a0 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/actions/Action.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/actions/Action.kt @@ -1,6 +1,7 @@ package com.tangem.tangemtest.ucase.domain.actions -import com.tangem.CardManager +import com.tangem.TangemSdk +import com.tangem.common.CompletionResult import com.tangem.tangemtest._arch.structure.Id import com.tangem.tangemtest._arch.structure.Payload import com.tangem.tangemtest._arch.structure.PayloadHolder @@ -8,7 +9,6 @@ import com.tangem.tangemtest._arch.structure.abstraction.Item import com.tangem.tangemtest.ucase.domain.paramsManager.ActionCallback import com.tangem.tangemtest.ucase.domain.paramsManager.triggers.afterAction.AfterActionModification import com.tangem.tangemtest.ucase.domain.paramsManager.triggers.changeConsequence.ItemsChangeConsequence -import com.tangem.tasks.TaskEvent /** [REDACTED_AUTHOR] @@ -17,7 +17,7 @@ import com.tangem.tasks.TaskEvent * and then processing the response. It also allows you to extract the main action as a lambda expression */ data class AttrForAction( - val cardManager: CardManager, + val tangemSdk: TangemSdk, val itemList: List, val payload: Payload, val consequence: ItemsChangeConsequence? @@ -31,16 +31,16 @@ interface Action { abstract class BaseAction : Action { protected open fun handleResult( payload: PayloadHolder, - taskEvent: TaskEvent<*>, + commandResult: CompletionResult<*>, modifier: AfterActionModification?, attrs: AttrForAction, callback: ActionCallback ) { val modifiedItems = mutableListOf() - modifier?.modify(payload, taskEvent, attrs.itemList)?.forEach { item -> + modifier?.modify(payload, commandResult, attrs.itemList)?.forEach { item -> modifiedItems.add(item) attrs.consequence?.affectChanges(payload, item, attrs.itemList)?.let { modifiedItems.addAll(it) } } - callback(taskEvent, modifiedItems) + callback(commandResult, modifiedItems) } } \ No newline at end of file diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/actions/DepersonalizeAction.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/actions/DepersonalizeAction.kt index 2433302a4b..2b5d0a79c1 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/actions/DepersonalizeAction.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/actions/DepersonalizeAction.kt @@ -14,7 +14,7 @@ class DepersonalizeAction : BaseAction() { val item = attrs.itemList.findDataItem(TlvId.CardId) ?: return val cardId = item.viewModel.data as? String ?: return - attrs.cardManager.depersonalize(cardId) { handleResult(payload, it, null, attrs, callback) } + attrs.tangemSdk.depersonalize(cardId) { handleResult(payload, it, null, attrs, callback) } } override fun getActionByTag(payload: PayloadHolder, id: Id, attrs: AttrForAction): ((ActionCallback) -> Unit)? { diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/actions/PersonalizeAction.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/actions/PersonalizeAction.kt index 8ef5e4c0cc..ab3330ca54 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/actions/PersonalizeAction.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/actions/PersonalizeAction.kt @@ -40,7 +40,7 @@ class PersonalizeAction : BaseAction() { val personalizeConfig = PersonalizeConfigConverter().convert(itemList, PersonalizeConfig()) val cardConfig = PersonalizeConfigToCardConfig().convert(personalizeConfig) - attrs.cardManager.personalize(cardConfig, issuer, manufacturer, acquirer) { + attrs.tangemSdk.personalize(cardConfig, issuer, manufacturer, acquirer) { handleResult(payload, it, null, attrs, callback) } } diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/actions/ScanAction.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/actions/ScanAction.kt index 5264be9aed..213fea8b63 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/actions/ScanAction.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/actions/ScanAction.kt @@ -9,6 +9,6 @@ import com.tangem.tangemtest.ucase.domain.paramsManager.triggers.afterAction.Aft */ class ScanAction : BaseAction() { override fun executeMainAction(payload: PayloadHolder, attrs: AttrForAction, callback: ActionCallback) { - attrs.cardManager.scanCard { handleResult(payload, it, AfterScanModifier(), attrs, callback) } + attrs.tangemSdk.scanCard { handleResult(payload, it, AfterScanModifier(), attrs, callback) } } } \ No newline at end of file diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/actions/SignAction.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/actions/SignAction.kt index 18fef1d8c5..c74209099b 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/actions/SignAction.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/actions/SignAction.kt @@ -16,7 +16,7 @@ class SignAction : BaseAction() { val hash = dataForHashing.getData() as? ByteArray ?: return val cardId = attrs.itemList.findDataItem(TlvId.CardId)?.viewModel?.data ?: return - attrs.cardManager.sign(arrayOf(hash), stringOf(cardId)) { handleResult(payload, it, null, attrs, callback) } + attrs.tangemSdk.sign(arrayOf(hash), stringOf(cardId)) { handleResult(payload, it, null, attrs, callback) } } override fun getActionByTag(payload: PayloadHolder, id: Id, attrs: AttrForAction): ((ActionCallback) -> Unit)? { diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/ItemsManager.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/ItemsManager.kt index f97013d6f8..89c8011add 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/ItemsManager.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/ItemsManager.kt @@ -1,14 +1,14 @@ package com.tangem.tangemtest.ucase.domain.paramsManager -import com.tangem.CardManager +import com.tangem.TangemSdk +import com.tangem.common.CompletionResult 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.Item import com.tangem.tangemtest.ucase.domain.paramsManager.triggers.changeConsequence.ItemsChangeConsequence -import com.tangem.tasks.TaskEvent -typealias ActionResponse = TaskEvent<*> +typealias ActionResponse = CompletionResult<*> typealias AffectedList = List typealias AffectedItemsCallback = (AffectedList) -> Unit typealias ActionCallback = (ActionResponse, AffectedList) -> Unit @@ -20,8 +20,8 @@ interface ItemsManager : PayloadHolder { fun itemChanged(id: Id, value: Any?, callback: AffectedItemsCallback? = null) fun getItems(): List - fun invokeMainAction(cardManager: CardManager, callback: ActionCallback) - fun getActionByTag(id: Id, cardManager: CardManager): ((ActionCallback) -> Unit)? + fun invokeMainAction(tangemSdk: TangemSdk, callback: ActionCallback) + fun getActionByTag(id: Id, tangemSdk: TangemSdk): ((ActionCallback) -> Unit)? fun attachPayload(payload: Payload) val consequence: ItemsChangeConsequence? diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/managers/BaseItemsManager.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/managers/BaseItemsManager.kt index c9016165ac..e8461a7e69 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/managers/BaseItemsManager.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/managers/BaseItemsManager.kt @@ -1,6 +1,6 @@ package com.tangem.tangemtest.ucase.domain.paramsManager.managers -import com.tangem.CardManager +import com.tangem.TangemSdk import com.tangem.tangemtest._arch.structure.Id import com.tangem.tangemtest._arch.structure.Payload import com.tangem.tangemtest._arch.structure.abstraction.Item @@ -33,7 +33,7 @@ abstract class BaseItemsManager(protected val action: Action) : ItemsManager { override fun getItems(): MutableList = itemList.toMutableList() - override fun getActionByTag(id: Id, cardManager: CardManager): ((ActionCallback) -> Unit)? = null + override fun getActionByTag(id: Id, tangemSdk: TangemSdk): ((ActionCallback) -> Unit)? = null override fun attachPayload(payload: Payload) { payload.forEach { this.payload[it.key] = it.value } @@ -44,8 +44,8 @@ abstract class BaseItemsManager(protected val action: Action) : ItemsManager { consequence?.affectChanges(this, param, itemList)?.let { callback?.invoke(it) } } - protected fun getAttrsForAction(cardManager: CardManager) - : AttrForAction = AttrForAction(cardManager, itemList, payload, consequence) + protected fun getAttrsForAction(tangemSdk: TangemSdk) + : AttrForAction = AttrForAction(tangemSdk, itemList, payload, consequence) abstract fun createItemsList(): List } \ No newline at end of file diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/managers/DepersonalizeItemsManager.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/managers/DepersonalizeItemsManager.kt index 8267c86bb6..3f94e7ea85 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/managers/DepersonalizeItemsManager.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/managers/DepersonalizeItemsManager.kt @@ -1,6 +1,6 @@ package com.tangem.tangemtest.ucase.domain.paramsManager.managers -import com.tangem.CardManager +import com.tangem.TangemSdk import com.tangem.tangemtest._arch.structure.Id import com.tangem.tangemtest._arch.structure.abstraction.Item import com.tangem.tangemtest._arch.structure.impl.EditTextItem @@ -16,11 +16,11 @@ class DepersonalizeItemsManager : BaseItemsManager(DepersonalizeAction()) { return listOf(EditTextItem(TlvId.CardId, null)) } - override fun invokeMainAction(cardManager: CardManager, callback: ActionCallback) { - action.executeMainAction(this, getAttrsForAction(cardManager), callback) + override fun invokeMainAction(tangemSdk: TangemSdk, callback: ActionCallback) { + action.executeMainAction(this, getAttrsForAction(tangemSdk), callback) } - override fun getActionByTag(id: Id, cardManager: CardManager): ((ActionCallback) -> Unit)? { - return action.getActionByTag(this, id, getAttrsForAction(cardManager)) + override fun getActionByTag(id: Id, tangemSdk: TangemSdk): ((ActionCallback) -> Unit)? { + return action.getActionByTag(this, id, getAttrsForAction(tangemSdk)) } } \ No newline at end of file diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/managers/PersonalizeItemsManager.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/managers/PersonalizeItemsManager.kt index 7f40ac54f5..a23e009c8c 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/managers/PersonalizeItemsManager.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/managers/PersonalizeItemsManager.kt @@ -1,6 +1,6 @@ package com.tangem.tangemtest.ucase.domain.paramsManager.managers -import com.tangem.CardManager +import com.tangem.TangemSdk import com.tangem.tangemtest._arch.structure.abstraction.Item import com.tangem.tangemtest.ucase.domain.actions.PersonalizeAction import com.tangem.tangemtest.ucase.domain.paramsManager.ActionCallback @@ -11,7 +11,7 @@ import com.tangem.tangemtest.ucase.domain.paramsManager.ActionCallback class PersonalizeItemsManager : BaseItemsManager(PersonalizeAction()) { override fun createItemsList(): List = listOf() - override fun invokeMainAction(cardManager: CardManager, callback: ActionCallback) { - action.executeMainAction(this, getAttrsForAction(cardManager), callback) + override fun invokeMainAction(tangemSdk: TangemSdk, callback: ActionCallback) { + action.executeMainAction(this, getAttrsForAction(tangemSdk), callback) } } \ No newline at end of file diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/managers/ScanItemsManager.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/managers/ScanItemsManager.kt index 1d9c973786..c3f8767717 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/managers/ScanItemsManager.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/managers/ScanItemsManager.kt @@ -1,6 +1,6 @@ package com.tangem.tangemtest.ucase.domain.paramsManager.managers -import com.tangem.CardManager +import com.tangem.TangemSdk import com.tangem.tangemtest._arch.structure.abstraction.Item import com.tangem.tangemtest.ucase.domain.actions.ScanAction import com.tangem.tangemtest.ucase.domain.paramsManager.ActionCallback @@ -11,7 +11,7 @@ import com.tangem.tangemtest.ucase.domain.paramsManager.ActionCallback class ScanItemsManager : BaseItemsManager(ScanAction()) { override fun createItemsList(): List = listOf() - override fun invokeMainAction(cardManager: CardManager, callback: ActionCallback) { - action.executeMainAction(this, getAttrsForAction(cardManager), callback) + override fun invokeMainAction(tangemSdk: TangemSdk, callback: ActionCallback) { + action.executeMainAction(this, getAttrsForAction(tangemSdk), callback) } } \ No newline at end of file diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/managers/SignItemsManager.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/managers/SignItemsManager.kt index 7d5d77a912..91b6b5ba01 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/managers/SignItemsManager.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/managers/SignItemsManager.kt @@ -1,6 +1,6 @@ package com.tangem.tangemtest.ucase.domain.paramsManager.managers -import com.tangem.CardManager +import com.tangem.TangemSdk import com.tangem.tangemtest._arch.structure.Id import com.tangem.tangemtest._arch.structure.abstraction.Item import com.tangem.tangemtest._arch.structure.impl.EditTextItem @@ -24,11 +24,11 @@ class SignItemsManager : BaseItemsManager(SignAction()) { ) } - override fun invokeMainAction(cardManager: CardManager, callback: ActionCallback) { - action.executeMainAction(this, getAttrsForAction(cardManager), callback) + override fun invokeMainAction(tangemSdk: TangemSdk, callback: ActionCallback) { + action.executeMainAction(this, getAttrsForAction(tangemSdk), callback) } - override fun getActionByTag(id: Id, cardManager: CardManager): ((ActionCallback) -> Unit)? { - return action.getActionByTag(this, id, getAttrsForAction(cardManager)) + override fun getActionByTag(id: Id, tangemSdk: TangemSdk): ((ActionCallback) -> Unit)? { + return action.getActionByTag(this, id, getAttrsForAction(tangemSdk)) } } \ No newline at end of file diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/triggers/afterAction/AfterActionModifiers.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/triggers/afterAction/AfterActionModifiers.kt index 0b6cecbf64..48ab757ff0 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/triggers/afterAction/AfterActionModifiers.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/triggers/afterAction/AfterActionModifiers.kt @@ -1,8 +1,8 @@ package com.tangem.tangemtest.ucase.domain.paramsManager.triggers.afterAction +import com.tangem.common.CompletionResult import com.tangem.tangemtest._arch.structure.PayloadHolder import com.tangem.tangemtest._arch.structure.abstraction.Item -import com.tangem.tasks.TaskEvent /** [REDACTED_AUTHOR] @@ -12,5 +12,5 @@ import com.tangem.tasks.TaskEvent * Returns a list of items that have been modified */ interface AfterActionModification { - fun modify(payload: PayloadHolder, taskEvent: TaskEvent<*>, itemList: List): List + fun modify(payload: PayloadHolder, commandResult: CompletionResult<*>, itemList: List): List } \ No newline at end of file diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/triggers/afterAction/AfterScanModifier.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/triggers/afterAction/AfterScanModifier.kt index b75c721424..60464e4bb0 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/triggers/afterAction/AfterScanModifier.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/triggers/afterAction/AfterScanModifier.kt @@ -2,6 +2,7 @@ package com.tangem.tangemtest.ucase.domain.paramsManager.triggers.afterAction import com.tangem.commands.Card import com.tangem.commands.CardStatus +import com.tangem.common.CompletionResult import com.tangem.tangemtest._arch.structure.PayloadHolder import com.tangem.tangemtest._arch.structure.abstraction.Item import com.tangem.tangemtest._arch.structure.abstraction.findDataItem @@ -9,17 +10,16 @@ import com.tangem.tangemtest.ucase.domain.paramsManager.PayloadKey import com.tangem.tangemtest.ucase.tunnel.ActionView import com.tangem.tangemtest.ucase.tunnel.CardError import com.tangem.tangemtest.ucase.variants.TlvId -import com.tangem.tasks.ScanEvent -import com.tangem.tasks.TaskEvent + import ru.dev.gbixahue.eu4d.lib.android.global.threading.postUI /** [REDACTED_AUTHOR] */ class AfterScanModifier : AfterActionModification { - override fun modify(payload: PayloadHolder, taskEvent: TaskEvent<*>, itemList: List): List { + override fun modify(payload: PayloadHolder, commandResult: CompletionResult<*>, itemList: List): List { val foundItem = itemList.findDataItem(TlvId.CardId) ?: return listOf() - val card = smartCast(taskEvent)?.card ?: return listOf() + val card = smartCast(commandResult) ?: return listOf() val actionView = payload.get(PayloadKey.actionView) as? ActionView ?: return listOf() return if (isNotPersonalized(card)) { @@ -34,8 +34,8 @@ class AfterScanModifier : AfterActionModification { } } - private fun smartCast(taskEvent: TaskEvent<*>): ScanEvent.OnReadEvent? { - return (taskEvent as? TaskEvent.Event)?.data as? ScanEvent.OnReadEvent + private fun smartCast(commandResult: CompletionResult<*>): Card? { + return (commandResult as? CompletionResult.Success)?.data } private fun isNotPersonalized(card: Card): Boolean = card.status == CardStatus.NotPersonalized diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/BaseCardActionFragment.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/BaseCardActionFragment.kt index d44d2c998d..b18588989d 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/BaseCardActionFragment.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/BaseCardActionFragment.kt @@ -13,7 +13,7 @@ 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.TangemSdk import com.tangem.tangem_sdk_new.extensions.init import com.tangem.tangemtest.R import com.tangem.tangemtest._arch.structure.Id @@ -56,7 +56,7 @@ abstract class BaseCardActionFragment : Fragment(), LayoutHolder, ActionView { Log.d(this, "onViewCreated") viewLifecycleOwner.lifecycle.addObserver(paramsVM) - paramsVM.setCardManager(CardManager.init(requireActivity())) + paramsVM.setCardManager(TangemSdk.init(requireActivity())) paramsVM.attachToPayload(mutableMapOf(PayloadKey.actionView to this as ActionView)) initFab() showActionFab(false) diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/ParamsViewModel.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/ParamsViewModel.kt index 2df743786e..84c3ed915d 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/ParamsViewModel.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/ParamsViewModel.kt @@ -3,8 +3,11 @@ package com.tangem.tangemtest.ucase.ui import androidx.annotation.UiThread import androidx.lifecycle.* import com.google.gson.Gson -import com.tangem.CardManager +import com.tangem.SessionError +import com.tangem.TangemSdk import com.tangem.commands.Card +import com.tangem.commands.CommandResponse +import com.tangem.common.CompletionResult import com.tangem.tangemtest._arch.SingleLiveEvent import com.tangem.tangemtest._arch.structure.Id import com.tangem.tangemtest._arch.structure.Payload @@ -13,9 +16,6 @@ import com.tangem.tangemtest.commons.performAction import com.tangem.tangemtest.ucase.domain.paramsManager.ItemsManager import com.tangem.tangemtest.ucase.domain.responses.GsonInitializer import com.tangem.tangemtest.ucase.tunnel.ViewScreen -import com.tangem.tasks.ScanEvent -import com.tangem.tasks.TaskError -import com.tangem.tasks.TaskEvent import ru.dev.gbixahue.eu4d.lib.android.global.log.Log /** @@ -37,10 +37,10 @@ class ParamsViewModel(private val itemsManager: ItemsManager) : ViewModel(), Lif val seChangedItems: MutableLiveData> = SingleLiveEvent() private val notifier: Notifier = Notifier(this) - private lateinit var cardManager: CardManager + private lateinit var tangemSdk: TangemSdk - fun setCardManager(cardManager: CardManager) { - this.cardManager = cardManager + fun setCardManager(tangemSdk: TangemSdk) { + this.tangemSdk = tangemSdk } fun userChangedItem(id: Id, value: Any?) { @@ -53,7 +53,7 @@ class ParamsViewModel(private val itemsManager: ItemsManager) : ViewModel(), Lif //invokes Scan, Sign etc... fun invokeMainAction() { - performAction(itemsManager, cardManager) { paramsManager, cardManager -> + performAction(itemsManager, tangemSdk) { paramsManager, cardManager -> paramsManager.invokeMainAction(cardManager) { response, listOfChangedParams -> notifier.handleActionResult(response, listOfChangedParams) } @@ -61,7 +61,7 @@ class ParamsViewModel(private val itemsManager: ItemsManager) : ViewModel(), Lif } fun getItemAction(id: Id): (() -> Unit)? { - val itemFunction = itemsManager.getActionByTag(id, cardManager) ?: return null + val itemFunction = itemsManager.getActionByTag(id, tangemSdk) ?: return null return { itemFunction { response, listOfChangedParams -> @@ -84,10 +84,10 @@ class ParamsViewModel(private val itemsManager: ItemsManager) : ViewModel(), Lif internal class Notifier(private val vm: ParamsViewModel) { - private var notShowedError: TaskError? = null + private var notShowedError: SessionError? = null private val gson: Gson = GsonInitializer().gson - fun handleActionResult(response: TaskEvent<*>, list: List) { + fun handleActionResult(response: CompletionResult<*>, list: List) { if (list.isNotEmpty()) notifyItemsChanged(list) handleResponse(response) } @@ -97,48 +97,37 @@ internal class Notifier(private val vm: ParamsViewModel) { vm.seChangedItems.postValue(list) } - fun handleResponse(response: TaskEvent<*>) { - val taskEvent = response as? TaskEvent<*> ?: return + fun handleResponse(response: CompletionResult<*>) { + val commandResponse = response as? CompletionResult ?: return - when (taskEvent) { - is TaskEvent.Completion -> handleCompletionEvent(taskEvent) - is TaskEvent.Event -> handleDataEvent(taskEvent.data) + when (commandResponse) { + is CompletionResult.Failure -> handleError(commandResponse.error) + is CompletionResult.Success -> handleDataEvent(commandResponse.data) } } - private fun handleDataEvent(event: Any?) { - when (event) { - is ScanEvent.OnReadEvent -> { - vm.ldCard.postValue(event.card) - vm.ldReadResponse.postValue(gson.toJson(event)) + private fun handleDataEvent(data: CommandResponse?) { + when (data) { + is Card -> { + vm.ldCard.postValue(data) + vm.ldReadResponse.postValue(gson.toJson(data)) } - is ScanEvent.OnVerifyEvent -> { - vm.ldIsVerified.postValue(true) - } - else -> vm.ldResponse.postValue(gson.toJson(event)) + else -> vm.ldResponse.postValue(gson.toJson(data)) } } - private fun handleCompletionEvent(taskEvent: TaskEvent.Completion<*>) { - if (taskEvent.error == null) { - Log.d(this, "error = null") - if (notShowedError != null) { - vm.seError.postValue("${notShowedError!!::class.simpleName}") - notShowedError = null - } - } else { - Log.d(this, "error = ${taskEvent.error}") - when (taskEvent.error) { - is TaskError.UserCancelled -> { - if (notShowedError == null) { - vm.seError.postValue("User canceled the action") - } else { - vm.seError.postValue("${notShowedError!!::class.simpleName}") - notShowedError = null - } + private fun handleError(error: SessionError) { + Log.d(this, "error = ${error}") + when (error) { + is SessionError.UserCancelled -> { + if (notShowedError == null) { + vm.seError.postValue("User canceled the action") + } else { + vm.seError.postValue("${notShowedError!!::class.simpleName}") + notShowedError = null } - else -> notShowedError = taskEvent.error } + else -> notShowedError = error } } } \ No newline at end of file diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/dto/DefaultPersonalizeParams.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/dto/DefaultPersonalizeParams.kt index c8d9afa0d1..f6e5cdb5ce 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/dto/DefaultPersonalizeParams.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/dto/DefaultPersonalizeParams.kt @@ -1,9 +1,9 @@ package com.tangem.tangemtest.ucase.variants.personalize.dto +import com.tangem.KeyPair import com.tangem.commands.personalization.entities.Acquirer import com.tangem.commands.personalization.entities.Issuer import com.tangem.commands.personalization.entities.Manufacturer -import com.tangem.common.KeyPair /** [REDACTED_AUTHOR] diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/PersonalizeFragment.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/PersonalizeFragment.kt index 3456d91ad2..4561e8e59d 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/PersonalizeFragment.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/PersonalizeFragment.kt @@ -12,7 +12,7 @@ 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.TangemSdk import com.tangem.tangem_sdk_new.extensions.init import com.tangem.tangemtest.R import com.tangem.tangemtest._arch.structure.Id @@ -70,7 +70,7 @@ class PersonalizeFragment : Fragment(), ActionView { personalizeVM.toggleDescriptionVisibility(it) }) - val cardManager = CardManager.init(requireActivity()) + val cardManager = TangemSdk.init(requireActivity()) paramsVM.setCardManager(cardManager) paramsVM.ldResponse.observe(viewLifecycleOwner, Observer { Log.d(this, "action response: ${if (it.length > 50) it.substring(0..50) else it}") From 25d005a9b9cfc186f6ab94f40968248ca2d78e45 Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 10 Apr 2020 13:27:47 +0300 Subject: [PATCH 28/39] Updated on 2026-08-14 --- .../converter/PersonalizationConfigConverter.kt | 15 ++++++++++----- .../personalize/dto/PersonalizationConfig.kt | 15 ++++----------- .../personalize/ui/widgets/SpinnerWidget.kt | 7 ++++--- 3 files changed, 18 insertions(+), 19 deletions(-) diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/converter/PersonalizationConfigConverter.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/converter/PersonalizationConfigConverter.kt index cd0c912920..b5c7250462 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/converter/PersonalizationConfigConverter.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/converter/PersonalizationConfigConverter.kt @@ -166,12 +166,13 @@ class PersonalizationConfigToItems : ModelToItems { private fun common(): ItemGroup { val block = createGroup(BlockId.Common) mutableListOf( - Common.Curve, Common.Blockchain, Common.BlockchainCustom, + Common.Curve, Common.MaxSignatures, - Common.CreateWallet - ).forEach { createItem(block, it) } + Common.CreateWallet, + Pins.PauseBeforePin2 + ).forEach { createItem(block, it as Id) } return block } @@ -279,8 +280,12 @@ class PersonalizationConfigToItems : ModelToItems { private fun pins(): ItemGroup { val block = createGroup(BlockId.Pins) - mutableListOf(Pins.Pin, Pins.Pin2, Pins.Pin3, Pins.Cvc, Pins.PauseBeforePin2) - .forEach { createItem(block, it) } + mutableListOf( + Pins.Pin, + Pins.Pin2, + Pins.Pin3, + Pins.Cvc + ).forEach { createItem(block, it) } return block } diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/dto/PersonalizationConfig.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/dto/PersonalizationConfig.kt index 1b09726453..2bfd50514c 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/dto/PersonalizationConfig.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/dto/PersonalizationConfig.kt @@ -15,8 +15,8 @@ class PersonalizationConfig { var curveID = "ed25519" var blockchain = "BTC/test" var blockchainCustom = "" - var MaxSignatures: Long = 100 - var createWallet = false + var MaxSignatures: Long = 999999 + var createWallet = true // Signing method var SigningMethod0 = true @@ -62,7 +62,7 @@ class PersonalizationConfig { var allowSwapPIN = true var allowSwapPIN2 = true var forbidDefaultPIN = false - var smartSecurityDelay = false + var smartSecurityDelay = true var protectIssuerDataAgainstReplay = true var skipSecurityDelayIfValidatedByIssuer = true var skipCheckPIN2andCVCIfValidatedByIssuer = true @@ -88,15 +88,8 @@ class PersonalizationConfig { var PIN2 = "000" var PIN3 = "" var CVC = "000" - var pauseBeforePIN2: Long = 15000 + var pauseBeforePIN2: Long = 5000L - - // not used -// var count: Long = 20 -// var numberFormat = "" -// var issuerData = null -// var releaseVersion = false -// var issuerName = "TANGEM SDK" } class CardData { diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/SpinnerWidget.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/SpinnerWidget.kt index d3e06d96cf..7703222579 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/SpinnerWidget.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/widgets/SpinnerWidget.kt @@ -41,7 +41,7 @@ class SpinnerWidget( val name = view.findViewById(R.id.tv_name) name.text = getName() spinner.adapter = spAdapter - spAdapter.getItemPosition(stringOf(viewModel.selectedItem))?.let { + spAdapter.getItemPosition(viewModel.selectedItem)?.let { spinner.setSelection(it) } spinner.onItemSelectedListener = onItemSelectedListener @@ -87,8 +87,9 @@ class SpItemAdapter(list: List?) : BaseAdapter() { override fun getCount(): Int = itemList.size - fun getItemPosition(item: String): Int? { - val kv = itemList.firstOrNull { it.value == item } ?: return null + fun getItemPosition(item: Any?): Int? { + val selectedValue = item ?: return null + val kv = itemList.firstOrNull { it.value == selectedValue } ?: return null return itemList.indexOf(kv) } } \ No newline at end of file From 82c48330e20773a0061dce648918a1fd9397e511 Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 10 Apr 2020 15:16:11 +0300 Subject: [PATCH 29/39] Updated on 2026-08-14 --- .../tangem/tangemtest/_main/MainViewModel.kt | 10 +- .../managers/PersonalizationItemsManager.kt | 6 +- .../domain/responses/ResponseJsonConverter.kt | 13 +- .../tangemtest/ucase/ui/ActionViewModel.kt | 153 +++++++++--------- .../ucase/ui/BaseCardActionFragment.kt | 28 ++-- .../personalize/ui/PersonalizationFragment.kt | 20 +-- .../variants/responses/ResponseViewModel.kt | 20 ++- .../responses/converter/ConvertersStore.kt | 3 +- .../responses/converter/SimpleConverters.kt | 7 +- .../variants/responses/ui/ResponseFragment.kt | 6 +- .../variants/scan/ui/ScanActionFragment.kt | 16 +- 11 files changed, 131 insertions(+), 151 deletions(-) diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/_main/MainViewModel.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/_main/MainViewModel.kt index a599bcb479..a50da93a68 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/_main/MainViewModel.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/_main/MainViewModel.kt @@ -2,7 +2,7 @@ package com.tangem.tangemtest._main import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel -import com.tangem.tasks.TaskEvent +import com.tangem.commands.CommandResponse import ru.dev.gbixahue.eu4d.lib.android.global.log.Log /** @@ -11,16 +11,16 @@ import ru.dev.gbixahue.eu4d.lib.android.global.log.Log class MainViewModel : ViewModel() { val ldDescriptionSwitch = MutableLiveData(false) - var responseEvent: TaskEvent<*>? = null + var commandResponse: CommandResponse? = null fun switchToggled(state: Boolean) { ldDescriptionSwitch.postValue(state) } - fun changeResponseEvent(event: TaskEvent<*>?) { + fun changeResponseEvent(commandResponse: CommandResponse?) { Log.d(this, "changeResponseEvent") - val reqEvent = event ?: return + val response = commandResponse ?: return - responseEvent = reqEvent + this.commandResponse = response } } \ No newline at end of file diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/managers/PersonalizationItemsManager.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/managers/PersonalizationItemsManager.kt index e67e278aca..1652cd2833 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/managers/PersonalizationItemsManager.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/managers/PersonalizationItemsManager.kt @@ -2,7 +2,7 @@ package com.tangem.tangemtest.ucase.domain.paramsManager.managers import androidx.lifecycle.Lifecycle import androidx.lifecycle.OnLifecycleEvent -import com.tangem.CardManager +import com.tangem.TangemSdk import com.tangem.tangemtest.commons.Store import com.tangem.tangemtest.ucase.domain.actions.PersonalizeAction import com.tangem.tangemtest.ucase.domain.paramsManager.ActionCallback @@ -23,8 +23,8 @@ class PersonalizationItemsManager( setItems(converter.convert(config)) } - override fun invokeMainAction(cardManager: CardManager, callback: ActionCallback) { - action.executeMainAction(this, getAttrsForAction(cardManager), callback) + override fun invokeMainAction(tangemSdk: TangemSdk, callback: ActionCallback) { + action.executeMainAction(this, getAttrsForAction(tangemSdk), callback) } @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY) 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 84695d90fb..d54bc08a5d 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 @@ -1,12 +1,8 @@ package com.tangem.tangemtest.ucase.domain.responses import com.google.gson.* -import com.tangem.commands.ProductMask -import com.tangem.commands.Settings -import com.tangem.commands.SettingsMask -import com.tangem.commands.SigningMethod +import com.tangem.commands.* import com.tangem.common.extensions.toHexString -import com.tangem.tasks.TaskEvent import java.lang.reflect.Type import java.text.DateFormat import java.util.* @@ -29,12 +25,7 @@ class ResponseJsonConverter { return builder.create() } - fun convertTaskEvent(task: TaskEvent<*>?): String = gson.toJson(task) - - fun convertEvent(task: TaskEvent<*>?): String { - val event = task as? TaskEvent.Event ?: "" - return gson.toJson(event) - } + fun convertResponse(response: CommandResponse?): String = gson.toJson(response) } class ByteTypeAdapter : JsonSerializer { diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/ActionViewModel.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/ActionViewModel.kt index f0fc4deec9..ea37c478fd 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/ActionViewModel.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/ActionViewModel.kt @@ -14,13 +14,9 @@ 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.iterate -import com.tangem.tangemtest.commons.performAction import com.tangem.tangemtest.ucase.domain.paramsManager.ItemsManager import com.tangem.tangemtest.ucase.domain.responses.ResponseJsonConverter import com.tangem.tangemtest.ucase.tunnel.ViewScreen -import com.tangem.tasks.ScanEvent -import com.tangem.tasks.TaskError -import com.tangem.tasks.TaskEvent import ru.dev.gbixahue.eu4d.lib.android.global.log.Log /** @@ -32,9 +28,9 @@ class ActionViewModelFactory(private val manager: ItemsManager) : ViewModelProvi class ActionViewModel(private val itemsManager: ItemsManager) : ViewModel(), LifecycleObserver { - val seResponseEvent = SingleLiveEvent>() - val seReadResponse = SingleLiveEvent() - val seResponse = SingleLiveEvent() + val seResponse = SingleLiveEvent() + val seResponseData = SingleLiveEvent() + val seResponseCardData = SingleLiveEvent() val ldItemList = MutableLiveData(itemsManager.getItems()) val seError: MutableLiveData = SingleLiveEvent() @@ -58,88 +54,87 @@ class ActionViewModel(private val itemsManager: ItemsManager) : ViewModel(), Lif //invokes Scan, Sign etc... fun invokeMainAction() { - performAction(itemsManager, tangemSdk) { paramsManager, cardManager -> - performAction(itemsManager, tangemSdk, { paramsManager, cardManager -> - paramsManager.invokeMainAction(tangemSdk) { response, listOfChangedParams -> - notifier.handleActionResult(response, listOfChangedParams) - } - }) + if (!::tangemSdk.isInitialized) { + Log.e(this, "TangemSdk isn't initialized") + return } - - fun getItemAction(id: Id): (() -> Unit)? { - val itemFunction = itemsManager.getActionByTag(id, tangemSdk) ?: return null - - return { - itemFunction { response, listOfChangedParams -> - notifier.handleActionResult(response, listOfChangedParams) - } - } - } - - fun toggleDescriptionVisibility(state: Boolean) { - ldItemList.value?.iterate { - it.viewModel.viewState.descriptionVisibility = if (state) View.VISIBLE else View.GONE - } - } - - fun attachToPayload(payload: Payload) { - itemsManager.attachPayload(payload) - } - - @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY) - fun viewOnDestroy() { - val keyList = mutableListOf() - itemsManager.payload.filterValues { it is ViewScreen }.forEach { keyList.add(it.key) } - keyList.forEach { itemsManager.payload.remove(it) } + itemsManager.invokeMainAction(tangemSdk) { response, listOfChangedParams -> + notifier.handleActionResult(response, listOfChangedParams) } } - internal class Notifier(private val vm: ActionViewModel) { + fun getItemAction(id: Id): (() -> Unit)? { + val itemFunction = itemsManager.getActionByTag(id, tangemSdk) ?: return null - private var notShowedError: SessionError? = null - private val gson: Gson = ResponseJsonConverter().gson - - fun handleActionResult(response: CompletionResult<*>, list: List) { - if (list.isNotEmpty()) notifyItemsChanged(list) - handleResponse(response) - } - - @UiThread - fun notifyItemsChanged(list: List) { - vm.seChangedItems.postValue(list) - } - - fun handleResponse(response: CompletionResult<*>) { - val taskEvent = response as? CompletionResult ?: return - - when (commandResponse) { - is CompletionResult.Success -> handleDataEvent(commandResponse.data) - is CompletionResult.Failure -> handleError(commandResponse.error) + return { + itemFunction { response, listOfChangedParams -> + notifier.handleActionResult(response, listOfChangedParams) } } + } - private fun handleDataEvent(event: CommandResponse?) { - when (event) { - is ScanEvent.OnReadEvent -> vm.seReadResponse.postValue(gson.toJson(event)) - is ScanEvent.OnVerifyEvent -> { + fun toggleDescriptionVisibility(state: Boolean) { + ldItemList.value?.iterate { + it.viewModel.viewState.descriptionVisibility = if (state) View.VISIBLE else View.GONE + } + } + + fun attachToPayload(payload: Payload) { + itemsManager.attachPayload(payload) + } + + @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY) + fun viewOnDestroy() { + val keyList = mutableListOf() + itemsManager.payload.filterValues { it is ViewScreen }.forEach { keyList.add(it.key) } + keyList.forEach { itemsManager.payload.remove(it) } + } +} + +internal class Notifier(private val vm: ActionViewModel) { + + private var notShowedError: SessionError? = null + private val gson: Gson = ResponseJsonConverter().gson + + fun handleActionResult(result: CompletionResult<*>, list: List) { + if (list.isNotEmpty()) notifyItemsChanged(list) + handleCompletionResult(result) + } + + @UiThread + fun notifyItemsChanged(list: List) { + vm.seChangedItems.postValue(list) + } + + fun handleCompletionResult(result: CompletionResult<*>) { + val commandResponse = result as? CompletionResult ?: return + + when (commandResponse) { + is CompletionResult.Success -> handleData(commandResponse.data) + is CompletionResult.Failure -> handleError(commandResponse.error) + } + } + + private fun handleData(event: CommandResponse?) { + vm.seResponse.postValue(event) + when (event) { + is Card -> vm.seResponseCardData.postValue(event) + else -> vm.seResponseData.postValue(event) + } + } + + private fun handleError(error: SessionError) { + Log.d(this, "error = $error") + when (error) { + is SessionError.UserCancelled -> { + if (notShowedError == null) { + vm.seError.postValue("User canceled the action") + } else { + vm.seError.postValue("${notShowedError!!::class.simpleName}") + notShowedError = null } - else -> vm.seResponse.postValue(gson.toJson(event)) - } - } - - private fun handleCompletionEvent(error: SessionError) { - Log.d(this, "error = ${taskEvent.error}") - when (taskEvent.error) { - is TaskError.UserCancelled -> { - if (notShowedError == null) { - vm.seError.postValue("User canceled the action") - } else { - vm.seError.postValue("${notShowedError!!::class.simpleName}") - notShowedError = null - } - } - else -> notShowedError = taskEvent.error } + else -> notShowedError = error } } } \ No newline at end of file diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/BaseCardActionFragment.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/BaseCardActionFragment.kt index 36acc2c199..7e50f755ef 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/BaseCardActionFragment.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/BaseCardActionFragment.kt @@ -9,9 +9,8 @@ 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.TangemSdk +import com.tangem.commands.Card import com.tangem.tangem_sdk_new.extensions.init import com.tangem.tangemtest.R import com.tangem.tangemtest._arch.structure.Id @@ -77,28 +76,37 @@ abstract class BaseCardActionFragment : BaseFragment(), ActionView { protected open fun subscribeToViewModelChanges() { Log.d(this, "subscribeToViewModelChanges") - listenEvent() - listenReadResponse() listenResponse() + listenResponseData() + listenResponseCardData() listenError() listenChangedItems() listenDescriptionSwitchChanges() } - protected open fun listenEvent() { - actionVM.seResponseEvent.observe(viewLifecycleOwner, Observer { + private fun listenResponse() { + actionVM.seResponse.observe(viewLifecycleOwner, Observer { + Log.d(this, "listen response: $it") mainActivityVM.changeResponseEvent(it) }) } - protected open fun listenReadResponse() {} - - protected open fun listenResponse() { - actionVM.seResponse.observe(viewLifecycleOwner, Observer { + protected open fun listenResponseData() { + actionVM.seResponseData.observe(viewLifecycleOwner, Observer { + Log.d(this, "listen responseData: $it") navigateTo(R.id.action_nav_card_action_to_response_screen) }) } + protected open fun listenResponseCardData() { + actionVM.seResponseCardData.observe(viewLifecycleOwner, Observer { + Log.d(this, "listen responseCardData: $it") + responseCardDataHandled(it) + }) + } + + protected open fun responseCardDataHandled(card: Card?) {} + protected open fun listenError() { actionVM.seError.observe(viewLifecycleOwner, Observer { showSnackbar(it) }) } diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/PersonalizationFragment.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/PersonalizationFragment.kt index d0216c725b..4b950d8b57 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/PersonalizationFragment.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/PersonalizationFragment.kt @@ -3,6 +3,7 @@ package com.tangem.tangemtest.ucase.variants.personalize.ui import android.os.Bundle import android.view.View import androidx.lifecycle.Observer +import com.tangem.commands.Card import com.tangem.tangemtest.R import com.tangem.tangemtest._arch.structure.Id import com.tangem.tangemtest._arch.structure.abstraction.Item @@ -52,20 +53,6 @@ class PersonalizationFragment : BaseCardActionFragment() { }) } - override fun subscribeToViewModelChanges() { - Log.d(this, "subscribeToViewModelChanges") - listenEvent() - listenError() - listenDescriptionSwitchChanges() - } - - override fun listenEvent() { - actionVM.seResponseEvent.observe(viewLifecycleOwner, Observer { - mainActivityVM.changeResponseEvent(it) - navigateTo(R.id.action_nav_card_action_to_response_screen) - }) - } - override fun showSnackbar(id: Id, additionalHandler: ((Id) -> Int)?) { super.showSnackbar(id) { when (id) { @@ -75,4 +62,9 @@ class PersonalizationFragment : BaseCardActionFragment() { } } } + + override fun responseCardDataHandled(card: Card?) { + super.responseCardDataHandled(card) + navigateTo(R.id.action_nav_card_action_to_response_screen) + } } \ No newline at end of file diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/responses/ResponseViewModel.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/responses/ResponseViewModel.kt index 6216ffbddc..dae8eef20d 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/responses/ResponseViewModel.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/responses/ResponseViewModel.kt @@ -3,6 +3,7 @@ package com.tangem.tangemtest.ucase.variants.responses import android.view.View import androidx.lifecycle.ViewModel import com.tangem.commands.Card +import com.tangem.commands.CommandResponse import com.tangem.commands.SignResponse import com.tangem.commands.personalization.DepersonalizeResponse import com.tangem.tangemtest.R @@ -10,8 +11,6 @@ import com.tangem.tangemtest._arch.structure.abstraction.Item import com.tangem.tangemtest._arch.structure.abstraction.ModelToItems import com.tangem.tangemtest._arch.structure.abstraction.iterate import com.tangem.tangemtest.ucase.variants.responses.converter.ConvertersStore -import com.tangem.tasks.ScanEvent -import com.tangem.tasks.TaskEvent import ru.dev.gbixahue.eu4d.lib.android.global.log.Log /** @@ -22,13 +21,13 @@ class ResponseViewModel : ViewModel() { private val convertersHolder = ConvertersStore() private var itemList: List? = null - fun createItemList(taskEvent: TaskEvent<*>?): List { + fun createItemList(response: CommandResponse?): List { Log.d(this, "createItemList: itemList size: ${itemList?.size ?: 0}") - val event = taskEvent as? TaskEvent.Event ?: return emptyList() - val type = event.data::class.java + val responseEvent = response ?: return emptyList() + val type = responseEvent::class.java val converter = convertersHolder.get(type) as? ModelToItems ?: return emptyList() - itemList = converter.convert(event.data) + itemList = converter.convert(responseEvent) return itemList!! } @@ -38,13 +37,12 @@ class ResponseViewModel : ViewModel() { } } - fun determineTitleId(taskEvent: TaskEvent<*>?): Int { - val event = taskEvent as? TaskEvent.Event ?: return R.string.unknown + fun determineTitleId(response: CommandResponse?): Int { + val responseEvent = response ?: return R.string.unknown - return when (event.data) { - is ScanEvent.OnReadEvent -> R.string.fg_name_response_scan - is SignResponse -> R.string.fg_name_response_sign + return when (responseEvent) { is Card -> R.string.fg_name_response_personalization + is SignResponse -> R.string.fg_name_response_sign is DepersonalizeResponse -> R.string.fg_name_response_depersonalization else -> R.string.unknown } diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/responses/converter/ConvertersStore.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/responses/converter/ConvertersStore.kt index 9b1dc76a49..067d47027c 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/responses/converter/ConvertersStore.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/responses/converter/ConvertersStore.kt @@ -3,13 +3,12 @@ package com.tangem.tangemtest.ucase.variants.responses.converter import com.tangem.commands.Card import com.tangem.commands.SignResponse import com.tangem.commands.personalization.DepersonalizeResponse -import com.tangem.tasks.ScanEvent import ru.dev.gbixahue.eu4d.lib.kotlin.common.BaseTypedHolder import java.lang.reflect.Type class ConvertersStore : BaseTypedHolder() { init { - register(ScanEvent.OnReadEvent::class.java, ReadEventConverter()) +// register(CompletionResult.Success::class.java, ReadEventConverter()) register(SignResponse::class.java, SignResponseConverter()) register(Card::class.java, CardConverter()) register(DepersonalizeResponse::class.java, DepersonalizeResponseConverter()) diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/responses/converter/SimpleConverters.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/responses/converter/SimpleConverters.kt index a1c997465b..b0c43734b1 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/responses/converter/SimpleConverters.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/responses/converter/SimpleConverters.kt @@ -1,19 +1,20 @@ package com.tangem.tangemtest.ucase.variants.responses.converter +import com.tangem.commands.Card import com.tangem.commands.SignResponse import com.tangem.commands.personalization.DepersonalizeResponse +import com.tangem.common.CompletionResult import com.tangem.tangemtest._arch.structure.StringId import com.tangem.tangemtest._arch.structure.abstraction.Item import com.tangem.tangemtest._arch.structure.abstraction.ModelToItems import com.tangem.tangemtest._arch.structure.impl.TextItem -import com.tangem.tasks.ScanEvent import ru.dev.gbixahue.eu4d.lib.kotlin.stringOf /** [REDACTED_AUTHOR] */ -class ReadEventConverter : ModelToItems { - override fun convert(from: ScanEvent.OnReadEvent): List = CardConverter().convert(from.card) +class ReadEventConverter : ModelToItems> { + override fun convert(from: CompletionResult.Success): List = CardConverter().convert(from.data) } class SignResponseConverter : ModelToItems { diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/responses/ui/ResponseFragment.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/responses/ui/ResponseFragment.kt index 4477f12894..a42d51e237 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/responses/ui/ResponseFragment.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/responses/ui/ResponseFragment.kt @@ -33,7 +33,7 @@ open class ResponseFragment : BaseFragment() { } private fun setTittle() { - val titleId = selfVM.determineTitleId(mainActivityVM.responseEvent) + val titleId = selfVM.determineTitleId(mainActivityVM.commandResponse) activity?.setTitle(titleId) } @@ -47,7 +47,7 @@ open class ResponseFragment : BaseFragment() { private fun buildWidgets() { val builder = WidgetBuilder(ResponseItemBuilder()) - val itemList = selfVM.createItemList(mainActivityVM.responseEvent) + val itemList = selfVM.createItemList(mainActivityVM.commandResponse) itemList.forEach { builder.build(it, itemContainer) } } @@ -67,7 +67,7 @@ open class ResponseFragment : BaseFragment() { override fun onOptionsItemSelected(item: MenuItem): Boolean { when (item.itemId) { R.id.action_share -> { - shareText(ResponseJsonConverter().convertEvent(mainActivityVM.responseEvent)) + shareText(ResponseJsonConverter().convertResponse(mainActivityVM.commandResponse)) } } return super.onOptionsItemSelected(item) diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/scan/ui/ScanActionFragment.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/scan/ui/ScanActionFragment.kt index 1e5ed5eb9b..90e3931375 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/scan/ui/ScanActionFragment.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/scan/ui/ScanActionFragment.kt @@ -1,8 +1,6 @@ package com.tangem.tangemtest.ucase.variants.scan.ui -import android.os.Bundle -import android.view.View -import androidx.lifecycle.Observer +import com.tangem.commands.Card import com.tangem.tangemtest.R import com.tangem.tangemtest.ucase.domain.paramsManager.ItemsManager import com.tangem.tangemtest.ucase.domain.paramsManager.managers.ScanItemsManager @@ -17,14 +15,12 @@ class ScanActionFragment : BaseCardActionFragment() { override fun getLayoutId(): Int = R.layout.fg_action_card_scan - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - enableActionFab(true) + override fun initFab() { + actionFab.setOnClickListener { actionVM.invokeMainAction() } } - override fun listenReadResponse() { - actionVM.seReadResponse.observe(viewLifecycleOwner, Observer { - navigateTo(R.id.action_nav_card_action_to_response_screen) - }) + override fun responseCardDataHandled(card: Card?) { + super.responseCardDataHandled(card) + navigateTo(R.id.action_nav_card_action_to_response_screen) } } \ No newline at end of file From 2b2413cb14803cfdb06315e37df6aa6864f8d3b5 Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 10 Apr 2020 18:22:35 +0300 Subject: [PATCH 30/39] Updated on 2026-08-14 --- .../common/WalletManagerFactoryTest.kt | 12 ++-- .../src/main/java/com/tangem/CardSession.kt | 9 ++- .../src/main/java/com/tangem/Config.kt | 7 +- ...rdEnvironment.kt => SessionEnvironment.kt} | 5 +- ...ViewDelegate.kt => SessionViewDelegate.kt} | 2 +- .../src/main/java/com/tangem/TangemSdk.kt | 72 +++++++++---------- .../com/tangem/commands/CheckWalletCommand.kt | 6 +- .../main/java/com/tangem/commands/Command.kt | 17 +++-- .../tangem/commands/CreateWalletCommand.kt | 6 +- .../com/tangem/commands/OpenSessionCommand.kt | 6 +- .../com/tangem/commands/PurgeWalletCommand.kt | 6 +- .../java/com/tangem/commands/ReadCommand.kt | 8 +-- .../tangem/commands/ReadIssuerDataCommand.kt | 6 +- .../commands/ReadIssuerExtraDataCommand.kt | 6 +- .../tangem/commands/ReadUserDataCommand.kt | 6 +- .../java/com/tangem/commands/SignCommand.kt | 8 +-- .../tangem/commands/WriteIssuerDataCommand.kt | 6 +- .../commands/WriteIssuerExtraDataCommand.kt | 6 +- .../tangem/commands/WriteUserDataCommand.kt | 6 +- .../personalization/DepersonalizeCommand.kt | 6 +- .../personalization/PersonalizeCommand.kt | 6 +- .../tangem/tangemtest/TestUserDataActivity.kt | 4 +- ...egate.kt => DefaultSessionViewDelegate.kt} | 6 +- .../tangem_sdk_new/extensions/TangemSdk.kt | 4 +- 24 files changed, 111 insertions(+), 115 deletions(-) rename tangem-core/src/main/java/com/tangem/{CardEnvironment.kt => SessionEnvironment.kt} (89%) rename tangem-core/src/main/java/com/tangem/{CardSessionViewDelegate.kt => SessionViewDelegate.kt} (97%) rename tangem-sdk/src/main/java/com/tangem/tangem_sdk_new/{DefaultCardSessionViewDelegate.kt => DefaultSessionViewDelegate.kt} (97%) diff --git a/blockchain/src/test/java/com/tangem/blockchain/common/WalletManagerFactoryTest.kt b/blockchain/src/test/java/com/tangem/blockchain/common/WalletManagerFactoryTest.kt index 58034631e7..9ce1fd90ac 100644 --- a/blockchain/src/test/java/com/tangem/blockchain/common/WalletManagerFactoryTest.kt +++ b/blockchain/src/test/java/com/tangem/blockchain/common/WalletManagerFactoryTest.kt @@ -1,7 +1,7 @@ package com.tangem.blockchain.common import com.google.common.truth.Truth -import com.tangem.CardEnvironment +import com.tangem.SessionEnvironment import com.tangem.blockchain.bitcoin.BitcoinWalletManager import com.tangem.blockchain.cardano.CardanoWalletManager import com.tangem.blockchain.ethereum.EthereumWalletManager @@ -18,7 +18,7 @@ internal class WalletManagerFactoryTest { fun createBitcoinWalletManager() { val data = "0108bb00000000000304200754414e47454d00020102800a322e3432642053444b000341040876bdec26b89bd2159a668b9af3d9fe86370f318717c92b8d6c1186fb3648c32a5f9321998cc2d042901c91d40601e79a641e1cbcebe7a2358be6054e1b6e5d0a04041e76310c618102ffff8a0101820407e30b0d830b54414e47454d2053444b0084034254438640e17ceec48c5be36240c98019f95ad8b6e56acfebe60d11979c6279f715d607d76a860a137da8d109e805753f3f56b0130709f4bbf4cb9974b4c57b8469bf4b873041045f16bd1d2eafe463e62a335a09e6b2bbcbd04452526885cb679fc4d27af1bd22f553c7deefb54fd3d4f361d14e6dc3f11b7d4ea183250a60720ebdf9e110cd26050a736563703235366b310008040000006407010009020bb8604104752a727e14bba5bd73b6714d72500f61ffd11026ad1196d2e1c54577cbeeac3d11fc68a64700f8d533f4e311964ea8fb3aa26c588295f2133868d69c3e62869362040000005c6304000000090f01009000" val responseApdu = ResponseApdu(data.hexToBytes()) - val card = ReadCommand().deserialize(CardEnvironment(), responseApdu) + val card = ReadCommand().deserialize(SessionEnvironment(), responseApdu) val walletManager = WalletManagerFactory.makeWalletManager(card!!) Truth.assertThat(walletManager) @@ -29,7 +29,7 @@ internal class WalletManagerFactoryTest { fun createEthereumWalletManager() { val data = "0108bb00000000000536200754414e47454d00020102800a322e3432642053444b000341046c8aea0d5a850b0a608acf9a0c453c39ea86131e88bfa78800de3cfb5bf1007aeaa7b9ffc184212255758605c2461be343c0a661d73cabafa4c9c175b3f0e59a0a04041e76310c618102ffff8a0101820407e30b0d830b54414e47454d2053444b0084034554488640431b6244acfeac479becdff201a7f720a7d70a97edc4e019fb678596baf52dfe9d0e8faf08ceb4443b82d4e66815541f2dc8ec6dd3ff83eb42f06e5eab07f25f3041045f16bd1d2eafe463e62a335a09e6b2bbcbd04452526885cb679fc4d27af1bd22f553c7deefb54fd3d4f361d14e6dc3f11b7d4ea183250a60720ebdf9e110cd26050a736563703235366b3100080400000064070100090205dc60410464dddc3f356744aaecfa07427f9eb996ff537d65f20fb5be3abccf0354352a6b5f8a1942e0f8ddeea3a170eda78d060be8162ad60e94e4e91fbbdf0a7054785562040000005b6304000000090f01009000" val responseApdu = ResponseApdu(data.hexToBytes()) - val card = ReadCommand().deserialize(CardEnvironment(), responseApdu) + val card = ReadCommand().deserialize(SessionEnvironment(), responseApdu) val walletManager = WalletManagerFactory.makeWalletManager(card!!) Truth.assertThat(walletManager) @@ -40,7 +40,7 @@ internal class WalletManagerFactoryTest { fun createStellarWalletManager() { val data = "0108bb00000000000379200754414e47454d00020102800a322e3432642053444b0003410487d7bb51b189213e3cedc3fcfa3fc047b3b71b7805b5b215e14639b3a8ebb1952c9dd5ea4354441b6ada4e8b8327674bb102ddae69df55be69643a2c916edf650a04041e76310c618102ffff8a0101820407e30b0d830b54414e47454d2053444b008403584c4d86409a4bc2baf0e5836887da21167cf33458d5249d1a610bced0e31dc053f23729ed24d715912bf89e6804669430dfe396ed83274e0031f6803e2bdb8c041fa993413041045f16bd1d2eafe463e62a335a09e6b2bbcbd04452526885cb679fc4d27af1bd22f553c7deefb54fd3d4f361d14e6dc3f11b7d4ea183250a60720ebdf9e110cd2605086564323535313900080400000064070100090205dc6020e078212d58b2b9d0edc9c936830d10081cd38b90c31778c56dfb1171027e294e62040000003863040000002c0f01009000" val responseApdu = ResponseApdu(data.hexToBytes()) - val card = ReadCommand().deserialize(CardEnvironment(), responseApdu) + val card = ReadCommand().deserialize(SessionEnvironment(), responseApdu) val walletManager = WalletManagerFactory.makeWalletManager(card!!) Truth.assertThat(walletManager) @@ -51,7 +51,7 @@ internal class WalletManagerFactoryTest { fun createCardanoWalletManager() { val data = "0108bb00000000000502200754414e47454d00020102800a322e3432642053444b0003410402c1e39257d60583489da2d67d35d1cc2a1c005cc05c1021f44838edcaf25d5615cad7c9d11c2e23f5efa93e50904d33c88808d0e169060508df840992e31f4d0a04041e76310c658102ffff8a0101820407e30b0d830b54414e47454d2053444b00840743415244414e4f8640f24ef5c8c6eba0ff97560d5b013edb4a452594270db9647bd0a3543df8104dec75731d4db3ebe0fc493f2afee00195e560b51e3c41189b7c61ba7895d6434b9d3041045f16bd1d2eafe463e62a335a09e6b2bbcbd04452526885cb679fc4d27af1bd22f553c7deefb54fd3d4f361d14e6dc3f11b7d4ea183250a60720ebdf9e110cd2605086564323535313900080400000064070100090205dc60208a71161cfdf1e0a85d8e7ff372aa4a01136046292aceb5f9ad7ebdb98d3f60a86204000000646304000000000f01009000" val responseApdu = ResponseApdu(data.hexToBytes()) - val card = ReadCommand().deserialize(CardEnvironment(), responseApdu) + val card = ReadCommand().deserialize(SessionEnvironment(), responseApdu) val walletManager = WalletManagerFactory.makeWalletManager(card!!) Truth.assertThat(walletManager) @@ -62,7 +62,7 @@ internal class WalletManagerFactoryTest { fun createXrpWalletManager() { val data = "0108cb21000000002154200b534d4152542043415348000201028006322e31317200034104bdad63848f97c535da53cf8fd300d24fa33f0516d194aa78ec164a06994d00204bae243a424e316c6ec845e02d9b15eafae8c19018a926b0b7435e6e941cdadb0a0400007e210c5a81020028820407e30502830754414e47454d00840358525086400ed8734b877869722c7d0b37ffb154b9fef21c54bf2c6496feb1fb5c1fc28a2ac28e201dde84f27495fa7f08b3ca2be2fb4954bf0fe78af027d6cdc16c3eee923041048196aa4b410ac44a3b9cce18e7be226aea070acc83a9cf67540fac49af25129f6a538a28ad6341358e3c4f9963064f7e365372a651d374e5c23cdd37fd099bf2050a736563703235366b31000804000f4240070100090205dc604104d2b9fb288540d54e5b32ecaf0381cd571f97f6f1ecd036b66bb11aa52ffe9981110d883080e2e255c6b1640586f7765e6faa325d1340f49b56b83d9de56bc7ed6204000f42406304000000000f01009000" val responseApdu = ResponseApdu(data.hexToBytes()) - val card = ReadCommand().deserialize(CardEnvironment(), responseApdu) + val card = ReadCommand().deserialize(SessionEnvironment(), responseApdu) val walletManager = WalletManagerFactory.makeWalletManager(card!!) Truth.assertThat(walletManager) diff --git a/tangem-core/src/main/java/com/tangem/CardSession.kt b/tangem-core/src/main/java/com/tangem/CardSession.kt index f8455ace3e..1852ae25d9 100644 --- a/tangem-core/src/main/java/com/tangem/CardSession.kt +++ b/tangem-core/src/main/java/com/tangem/CardSession.kt @@ -7,7 +7,6 @@ import com.tangem.commands.ReadCommand import com.tangem.common.CompletionResult import com.tangem.common.apdu.CommandApdu import com.tangem.common.apdu.ResponseApdu -import com.tangem.common.extensions.calculateSha256 import com.tangem.crypto.EncryptionHelper import com.tangem.crypto.FastEncryptionHelper import com.tangem.crypto.StrongEncryptionHelper @@ -41,9 +40,9 @@ interface CardSessionRunnable { * If null, a default header and text body will be used. */ class CardSession( - val environment: CardEnvironment, - val reader: CardReader, - val viewDelegate: CardSessionViewDelegate, + val environment: SessionEnvironment, + private val reader: CardReader, + val viewDelegate: SessionViewDelegate, private var cardId: String? = null, private val initialMessage: Message? = null ) { @@ -208,7 +207,7 @@ class CardSession( when (result) { is CompletionResult.Success -> { val uid = result.data.uid - val protocolKey = environment.pin1.calculateSha256().pbkdf2Hash(uid, 50) + val protocolKey = environment.pin1.pbkdf2Hash(uid, 50) val secret = encryptionHelper.generateSecret(result.data.sessionKeyB) val sessionKey = (secret + protocolKey).calculateSha256() environment.encryptionKey = sessionKey diff --git a/tangem-core/src/main/java/com/tangem/Config.kt b/tangem-core/src/main/java/com/tangem/Config.kt index 7403a4c771..d99c57fcf9 100644 --- a/tangem-core/src/main/java/com/tangem/Config.kt +++ b/tangem-core/src/main/java/com/tangem/Config.kt @@ -24,5 +24,10 @@ class Config( * If not null, it will be used to validate Issuer data and issuer extra data. * If null, issuerPublicKey from current card will be used. */ - val issuerPublicKey: ByteArray? = null + val issuerPublicKey: ByteArray? = null, + + /** + * Level of encryption used in communication with a Tangem Card. + */ + val encryptionMode: EncryptionMode = EncryptionMode.NONE ) \ No newline at end of file diff --git a/tangem-core/src/main/java/com/tangem/CardEnvironment.kt b/tangem-core/src/main/java/com/tangem/SessionEnvironment.kt similarity index 89% rename from tangem-core/src/main/java/com/tangem/CardEnvironment.kt rename to tangem-core/src/main/java/com/tangem/SessionEnvironment.kt index e4b2c36c55..81a541184c 100644 --- a/tangem-core/src/main/java/com/tangem/CardEnvironment.kt +++ b/tangem-core/src/main/java/com/tangem/SessionEnvironment.kt @@ -2,17 +2,18 @@ package com.tangem import com.tangem.commands.Card import com.tangem.commands.EllipticCurve +import com.tangem.common.extensions.calculateSha256 import com.tangem.crypto.CryptoUtils.generatePublicKey /** * Contains data relating to a Tangem card. It is used in constructing all the commands, - * and commands can return modified [CardEnvironment]. + * and commands can return modified [SessionEnvironment]. * * @property card Current card, read by preflight [com.tangem.commands.ReadCommand]. * @property terminalKeys generated terminal keys used in Linked Terminal feature. */ -data class CardEnvironment( +data class SessionEnvironment( val pin1: String = DEFAULT_PIN, val pin2: String = DEFAULT_PIN2, var card: Card? = null, diff --git a/tangem-core/src/main/java/com/tangem/CardSessionViewDelegate.kt b/tangem-core/src/main/java/com/tangem/SessionViewDelegate.kt similarity index 97% rename from tangem-core/src/main/java/com/tangem/CardSessionViewDelegate.kt rename to tangem-core/src/main/java/com/tangem/SessionViewDelegate.kt index da5f26e020..c372b1730c 100644 --- a/tangem-core/src/main/java/com/tangem/CardSessionViewDelegate.kt +++ b/tangem-core/src/main/java/com/tangem/SessionViewDelegate.kt @@ -7,7 +7,7 @@ import com.tangem.common.CompletionResult * * Its default implementation, DefaultCardManagerDelegate, is in our tangem-sdk module. */ -interface CardSessionViewDelegate { +interface SessionViewDelegate { /** * It is called when user is expected to scan a Tangem Card with an Android device. diff --git a/tangem-core/src/main/java/com/tangem/TangemSdk.kt b/tangem-core/src/main/java/com/tangem/TangemSdk.kt index 786249d22b..145efc088e 100644 --- a/tangem-core/src/main/java/com/tangem/TangemSdk.kt +++ b/tangem-core/src/main/java/com/tangem/TangemSdk.kt @@ -27,12 +27,11 @@ import com.tangem.tasks.ScanTask */ class TangemSdk( private val reader: CardReader, - private val viewDelegate: CardSessionViewDelegate, + private val viewDelegate: SessionViewDelegate, var config: Config = Config() ) { private var terminalKeysService: TerminalKeysService? = null - private var cardSession: CardSession? = null init { CryptoUtils.initCrypto() @@ -49,9 +48,8 @@ class TangemSdk( * @param callback is triggered on the completion of the [ScanTask] and provides card response * in the form of [Card] if the task was performed successfully or [SessionError] in case of an error. */ - fun scanCard(callback: (result: CompletionResult) -> Unit) { - cardSession = CardSession(buildEnvironment(), reader, viewDelegate) - cardSession!!.startWithRunnable(ScanTask(), callback) + fun scanCard(initialMessage: Message? = null, callback: (result: CompletionResult) -> Unit) { + startSession(ScanTask(), null, initialMessage, callback) } /** @@ -64,7 +62,7 @@ class TangemSdk( * * Please note that Tangem cards usually protect the signing with a security delay * that may last up to 90 seconds, depending on a card. - * It is for [CardSessionViewDelegate] to notify users of security delay. + * It is for [SessionViewDelegate] to notify users of security delay. * * @param hashes Array of transaction hashes. It can be from one or up to ten hashes of the same length. * @param cardId CID, Unique Tangem card ID number @@ -72,10 +70,9 @@ class TangemSdk( * in the form of [SignResponse] if the task was performed successfully * or [SessionError] in case of an error. */ - fun sign(hashes: Array, cardId: String, + fun sign(hashes: Array, cardId: String, initialMessage: Message? = null, callback: (result: CompletionResult) -> Unit) { - cardSession = CardSession(buildEnvironment(), reader, viewDelegate, cardId) - cardSession!!.startWithRunnable(SignCommand(hashes), callback) + startSession(SignCommand(hashes), cardId, initialMessage, callback) } /** @@ -91,10 +88,9 @@ class TangemSdk( * card response in the form of [ReadIssuerDataResponse] if the task was performed successfully * or [SessionError] in case of an error. */ - fun readIssuerData(cardId: String, + fun readIssuerData(cardId: String, initialMessage: Message? = null, callback: (result: CompletionResult) -> Unit) { - cardSession = CardSession(buildEnvironment(), reader, viewDelegate, cardId) - cardSession!!.startWithRunnable(ReadIssuerDataCommand(config.issuerPublicKey), callback) + startSession(ReadIssuerDataCommand(config.issuerPublicKey), cardId, initialMessage, callback) } /** @@ -113,8 +109,7 @@ class TangemSdk( */ fun readIssuerExtraData(cardId: String, callback: (result: CompletionResult) -> Unit) { - cardSession = CardSession(buildEnvironment(), reader, viewDelegate, cardId) - cardSession!!.startWithRunnable(ReadIssuerExtraDataCommand(config.issuerPublicKey), callback) + startSession(ReadIssuerExtraDataCommand(config.issuerPublicKey), cardId, null, callback) } /** @@ -137,6 +132,7 @@ class TangemSdk( issuerData: ByteArray, issuerDataSignature: ByteArray, issuerDataCounter: Int? = null, + initialMessage: Message? = null, callback: (result: CompletionResult) -> Unit) { val command = WriteIssuerDataCommand( issuerData, @@ -144,8 +140,7 @@ class TangemSdk( issuerDataCounter, config.issuerPublicKey ) - cardSession = CardSession(buildEnvironment(), reader, viewDelegate, cardId) - cardSession!!.startWithRunnable(command, callback) + startSession(command, cardId, initialMessage, callback) } /** @@ -176,6 +171,7 @@ class TangemSdk( startingSignature: ByteArray, finalizingSignature: ByteArray, issuerDataCounter: Int? = null, + initialMessage: Message? = null, callback: (result: CompletionResult) -> Unit) { val command = WriteIssuerExtraDataCommand( issuerData, @@ -183,8 +179,7 @@ class TangemSdk( issuerDataCounter, config.issuerPublicKey ) - cardSession = CardSession(buildEnvironment(), reader, viewDelegate, cardId) - cardSession!!.startWithRunnable(command, callback) + startSession(command, cardId, initialMessage, callback) } /** @@ -207,11 +202,11 @@ class TangemSdk( userProtectedData: ByteArray? = null, userCounter: Int? = null, userProtectedCounter: Int? = null, + initialMessage: Message? = null, callback: (result: CompletionResult) -> Unit ) { val command = WriteUserDataCommand(userData, userProtectedData, userCounter, userProtectedCounter) - cardSession = CardSession(buildEnvironment(), reader, viewDelegate, cardId) - cardSession!!.startWithRunnable(command, callback) + startSession(command, cardId, initialMessage, callback) } /** @@ -231,9 +226,9 @@ class TangemSdk( * card response in the form of [ReadUserDataResponse] if the task was performed successfully * or [SessionError] in case of an error. */ - fun readUserData(cardId: String, callback: (result: CompletionResult) -> Unit) { - cardSession = CardSession(buildEnvironment(), reader, viewDelegate, cardId) - cardSession!!.startWithRunnable(ReadUserDataCommand(), callback) + fun readUserData(cardId: String, initialMessage: Message? = null, + callback: (result: CompletionResult) -> Unit) { + startSession(ReadUserDataCommand(), cardId, initialMessage, callback) } /** @@ -253,10 +248,9 @@ class TangemSdk( * card response in the form of [CreateWalletResponse] if the task was performed successfully * or [SessionError] in case of an error. */ - fun createWallet(cardId: String, + fun createWallet(cardId: String, initialMessage: Message? = null, callback: (result: CompletionResult) -> Unit) { - cardSession = CardSession(buildEnvironment(), reader, viewDelegate, cardId) - cardSession!!.startWithRunnable(CreateWalletTask(), callback) + startSession(CreateWalletTask(), cardId, initialMessage, callback) } /** @@ -273,10 +267,9 @@ class TangemSdk( * card response in the form of [PurgeWalletResponse] if the task was performed successfully * or [SessionError] in case of an error. */ - fun purgeWallet(cardId: String, + fun purgeWallet(cardId: String, initialMessage: Message? = null, callback: (result: CompletionResult) -> Unit) { - cardSession = CardSession(buildEnvironment(), reader, viewDelegate, cardId) - cardSession!!.startWithRunnable(PurgeWalletCommand(), callback) + startSession(PurgeWalletCommand(), cardId, initialMessage, callback) } /** @@ -292,10 +285,9 @@ class TangemSdk( * card response in the form of [DepersonalizeResponse] if the task was performed successfully * or [SessionError] in case of an error. * */ - fun depersonalize(cardId: String, + fun depersonalize(cardId: String, initialMessage: Message? = null, callback: (result: CompletionResult) -> Unit) { - cardSession = CardSession(buildEnvironment(), reader, viewDelegate, cardId) - cardSession!!.startWithRunnable(DepersonalizeCommand(), callback) + startSession(DepersonalizeCommand(), cardId, initialMessage, callback) } /** @@ -318,10 +310,10 @@ class TangemSdk( */ fun personalize(config: CardConfig, issuer: Issuer, manufacturer: Manufacturer, acquirer: Acquirer? = null, + initialMessage: Message? = null, callback: (result: CompletionResult) -> Unit) { val command = PersonalizeCommand(config, issuer, manufacturer, acquirer) - cardSession = CardSession(buildEnvironment(), reader, viewDelegate) - cardSession!!.startWithRunnable(command, callback) + startSession(command, null, initialMessage, callback) } /** @@ -341,8 +333,8 @@ class TangemSdk( fun startSession( runnable: CardSessionRunnable, cardId: String? = null, initialMessage: Message? = null, callback: (result: CompletionResult) -> Unit) { - cardSession = CardSession(buildEnvironment(), reader, viewDelegate, cardId, initialMessage) - Thread().run { cardSession!!.startWithRunnable(runnable, callback) } + val cardSession = CardSession(buildEnvironment(), reader, viewDelegate, cardId, initialMessage) + Thread().run { cardSession.startWithRunnable(runnable, callback) } } /** @@ -360,8 +352,8 @@ class TangemSdk( fun startSession( cardId: String? = null, initialMessage: Message? = null, callback: (session: CardSession, error: SessionError?) -> Unit) { - cardSession = CardSession(buildEnvironment(), reader, viewDelegate, cardId, initialMessage) - Thread().run { cardSession!!.start(callback) } + val cardSession = CardSession(buildEnvironment(), reader, viewDelegate, cardId, initialMessage) + Thread().run { cardSession.start(callback) } } /** @@ -372,9 +364,9 @@ class TangemSdk( this.terminalKeysService = terminalKeysService } - private fun buildEnvironment(): CardEnvironment { + private fun buildEnvironment(): SessionEnvironment { val terminalKeys = if (config.linkedTerminal) terminalKeysService?.getKeys() else null - return CardEnvironment( + return SessionEnvironment( terminalKeys = terminalKeys ) } diff --git a/tangem-core/src/main/java/com/tangem/commands/CheckWalletCommand.kt b/tangem-core/src/main/java/com/tangem/commands/CheckWalletCommand.kt index d6df4c4204..4d9c3a53ff 100644 --- a/tangem-core/src/main/java/com/tangem/commands/CheckWalletCommand.kt +++ b/tangem-core/src/main/java/com/tangem/commands/CheckWalletCommand.kt @@ -1,7 +1,7 @@ package com.tangem.commands -import com.tangem.CardEnvironment import com.tangem.CardSession +import com.tangem.SessionEnvironment import com.tangem.SessionError import com.tangem.common.CompletionResult import com.tangem.common.apdu.CommandApdu @@ -70,7 +70,7 @@ class CheckWalletCommand( } } - override fun serialize(environment: CardEnvironment): CommandApdu { + override fun serialize(environment: SessionEnvironment): CommandApdu { val tlvBuilder = TlvBuilder() tlvBuilder.append(TlvTag.Pin, environment.pin1) tlvBuilder.append(TlvTag.CardId, environment.card?.cardId) @@ -81,7 +81,7 @@ class CheckWalletCommand( ) } - override fun deserialize(environment: CardEnvironment, apdu: ResponseApdu): CheckWalletResponse { + override fun deserialize(environment: SessionEnvironment, apdu: ResponseApdu): CheckWalletResponse { val tlvData = apdu.getTlvData(environment.encryptionKey) ?: throw SessionError.DeserializeApduFailed() diff --git a/tangem-core/src/main/java/com/tangem/commands/Command.kt b/tangem-core/src/main/java/com/tangem/commands/Command.kt index 9d06c5fe7f..a44332d04f 100644 --- a/tangem-core/src/main/java/com/tangem/commands/Command.kt +++ b/tangem-core/src/main/java/com/tangem/commands/Command.kt @@ -22,20 +22,20 @@ abstract class Command : CardSessionRunnable { /** * Serializes data into an array of [com.tangem.common.tlv.Tlv], * then creates [CommandApdu] with this data. - * @param environment [CardEnvironment] of the current card + * @param environment [SessionEnvironment] of the current card * @return command data converted to [CommandApdu] that allows to convert it to [ByteArray] * that can be sent to a Tangem card */ - abstract fun serialize(environment: CardEnvironment): CommandApdu + abstract fun serialize(environment: SessionEnvironment): CommandApdu /** * Deserializes data received from a card and stored in [ResponseApdu] * into an array of [com.tangem.common.tlv.Tlv]. Then maps it into a [CommandResponse]. - * @param environment [CardEnvironment] of the current card. + * @param environment [SessionEnvironment] of the current card. * @param apdu received data. * @return Card response converted to a [CommandResponse] of a type [T] */ - abstract fun deserialize(environment: CardEnvironment, apdu: ResponseApdu): T + abstract fun deserialize(environment: SessionEnvironment, apdu: ResponseApdu): T override fun run(session: CardSession, callback: (result: CompletionResult) -> Unit) { transceive(session, callback) @@ -92,9 +92,8 @@ abstract class Command : CardSessionRunnable { is CompletionResult.Failure -> if (result.error == SessionError.TagLost()) { session.viewDelegate.onTagLost() - } else if (result.error is SessionError.UserCancelled) { - callback(CompletionResult.Failure(SessionError.UserCancelled())) - session.reader.closeSession() + } else { + callback(CompletionResult.Failure(result.error)) } } } @@ -105,12 +104,12 @@ abstract class Command : CardSessionRunnable { * * @return Remaining security delay in milliseconds. */ - private fun deserializeSecurityDelay(responseApdu: ResponseApdu, cardEnvironment: CardEnvironment): Int? { + private fun deserializeSecurityDelay(responseApdu: ResponseApdu, environment: SessionEnvironment): Int? { val tlv = responseApdu.getTlvData() return tlv?.find { it.tag == TlvTag.Pause }?.value?.toInt() } - private fun tryHandleError(error: SessionError) : Boolean { + private fun tryHandleError(error: SessionError): Boolean { return false } diff --git a/tangem-core/src/main/java/com/tangem/commands/CreateWalletCommand.kt b/tangem-core/src/main/java/com/tangem/commands/CreateWalletCommand.kt index d93da64242..089a134702 100644 --- a/tangem-core/src/main/java/com/tangem/commands/CreateWalletCommand.kt +++ b/tangem-core/src/main/java/com/tangem/commands/CreateWalletCommand.kt @@ -1,6 +1,6 @@ package com.tangem.commands -import com.tangem.CardEnvironment +import com.tangem.SessionEnvironment import com.tangem.SessionError import com.tangem.common.apdu.CommandApdu import com.tangem.common.apdu.Instruction @@ -37,7 +37,7 @@ class CreateWalletResponse( */ class CreateWalletCommand : Command() { - override fun serialize(environment: CardEnvironment): CommandApdu { + override fun serialize(environment: SessionEnvironment): CommandApdu { val tlvBuilder = TlvBuilder() tlvBuilder.append(TlvTag.Pin, environment.pin1) tlvBuilder.append(TlvTag.CardId, environment.card?.cardId) @@ -49,7 +49,7 @@ class CreateWalletCommand : Command() { ) } - override fun deserialize(environment: CardEnvironment, apdu: ResponseApdu): CreateWalletResponse { + override fun deserialize(environment: SessionEnvironment, apdu: ResponseApdu): CreateWalletResponse { val tlvData = apdu.getTlvData(environment.encryptionKey) ?: throw SessionError.DeserializeApduFailed() diff --git a/tangem-core/src/main/java/com/tangem/commands/OpenSessionCommand.kt b/tangem-core/src/main/java/com/tangem/commands/OpenSessionCommand.kt index 48b3d9a80e..7484f9d48b 100644 --- a/tangem-core/src/main/java/com/tangem/commands/OpenSessionCommand.kt +++ b/tangem-core/src/main/java/com/tangem/commands/OpenSessionCommand.kt @@ -1,6 +1,6 @@ package com.tangem.commands -import com.tangem.CardEnvironment +import com.tangem.SessionEnvironment import com.tangem.SessionError import com.tangem.common.apdu.CommandApdu import com.tangem.common.apdu.Instruction @@ -21,7 +21,7 @@ class OpenSessionResponse( */ class OpenSessionCommand(private val sessionKeyA: ByteArray) : Command() { - override fun serialize(environment: CardEnvironment): CommandApdu { + override fun serialize(environment: SessionEnvironment): CommandApdu { val tlvBuilder = TlvBuilder() tlvBuilder.append(TlvTag.SessionKeyA, sessionKeyA) return CommandApdu( @@ -30,7 +30,7 @@ class OpenSessionCommand(private val sessionKeyA: ByteArray) : Command() { - override fun serialize(environment: CardEnvironment): CommandApdu { + override fun serialize(environment: SessionEnvironment): CommandApdu { val tlvBuilder = TlvBuilder() tlvBuilder.append(TlvTag.Pin, environment.pin1) tlvBuilder.append(TlvTag.CardId, environment.card?.cardId) @@ -40,7 +40,7 @@ class PurgeWalletCommand : Command() { ) } - override fun deserialize(environment: CardEnvironment, apdu: ResponseApdu): PurgeWalletResponse { + override fun deserialize(environment: SessionEnvironment, apdu: ResponseApdu): PurgeWalletResponse { val tlvData = apdu.getTlvData(environment.encryptionKey) ?: throw SessionError.DeserializeApduFailed() diff --git a/tangem-core/src/main/java/com/tangem/commands/ReadCommand.kt b/tangem-core/src/main/java/com/tangem/commands/ReadCommand.kt index e0deea6cb8..09c188072d 100644 --- a/tangem-core/src/main/java/com/tangem/commands/ReadCommand.kt +++ b/tangem-core/src/main/java/com/tangem/commands/ReadCommand.kt @@ -1,6 +1,6 @@ package com.tangem.commands -import com.tangem.CardEnvironment +import com.tangem.SessionEnvironment import com.tangem.SessionError import com.tangem.common.apdu.CommandApdu import com.tangem.common.apdu.Instruction @@ -382,10 +382,10 @@ class Card( */ class ReadCommand : Command() { - override fun serialize(environment: CardEnvironment): CommandApdu { + override fun serialize(environment: SessionEnvironment): CommandApdu { val tlvBuilder = TlvBuilder() /** - * [CardEnvironment] stores the pin1 value. If no pin1 value was set, it will contain + * [SessionEnvironment] stores the pin1 value. If no pin1 value was set, it will contain * default value of ‘000000’. * In order to obtain card’s data, [ReadCommand] should use the correct pin 1 value. * The card will not respond if wrong pin 1 has been submitted. @@ -398,7 +398,7 @@ class ReadCommand : Command() { ) } - override fun deserialize(environment: CardEnvironment, apdu: ResponseApdu): Card { + override fun deserialize(environment: SessionEnvironment, apdu: ResponseApdu): Card { val tlvData = apdu.getTlvData(environment.encryptionKey) ?: throw SessionError.DeserializeApduFailed() diff --git a/tangem-core/src/main/java/com/tangem/commands/ReadIssuerDataCommand.kt b/tangem-core/src/main/java/com/tangem/commands/ReadIssuerDataCommand.kt index 3909e0cbb6..7055bc403f 100644 --- a/tangem-core/src/main/java/com/tangem/commands/ReadIssuerDataCommand.kt +++ b/tangem-core/src/main/java/com/tangem/commands/ReadIssuerDataCommand.kt @@ -1,7 +1,7 @@ package com.tangem.commands -import com.tangem.CardEnvironment import com.tangem.CardSession +import com.tangem.SessionEnvironment import com.tangem.SessionError import com.tangem.commands.common.DefaultIssuerDataVerifier import com.tangem.commands.common.IssuerDataMode @@ -90,7 +90,7 @@ class ReadIssuerDataCommand( } } - override fun serialize(environment: CardEnvironment): CommandApdu { + override fun serialize(environment: SessionEnvironment): CommandApdu { val tlvBuilder = TlvBuilder() tlvBuilder.append(TlvTag.Pin, environment.pin1) tlvBuilder.append(TlvTag.CardId, environment.card?.cardId) @@ -101,7 +101,7 @@ class ReadIssuerDataCommand( ) } - override fun deserialize(environment: CardEnvironment, apdu: ResponseApdu): ReadIssuerDataResponse { + override fun deserialize(environment: SessionEnvironment, apdu: ResponseApdu): ReadIssuerDataResponse { val tlvData = apdu.getTlvData(environment.encryptionKey) ?: throw SessionError.DeserializeApduFailed() diff --git a/tangem-core/src/main/java/com/tangem/commands/ReadIssuerExtraDataCommand.kt b/tangem-core/src/main/java/com/tangem/commands/ReadIssuerExtraDataCommand.kt index 83120f9c41..4140000621 100644 --- a/tangem-core/src/main/java/com/tangem/commands/ReadIssuerExtraDataCommand.kt +++ b/tangem-core/src/main/java/com/tangem/commands/ReadIssuerExtraDataCommand.kt @@ -1,7 +1,7 @@ package com.tangem.commands -import com.tangem.CardEnvironment import com.tangem.CardSession +import com.tangem.SessionEnvironment import com.tangem.SessionError import com.tangem.commands.common.DefaultIssuerDataVerifier import com.tangem.commands.common.IssuerDataMode @@ -142,7 +142,7 @@ class ReadIssuerExtraDataCommand( } } - override fun serialize(environment: CardEnvironment): CommandApdu { + override fun serialize(environment: SessionEnvironment): CommandApdu { val tlvBuilder = TlvBuilder() tlvBuilder.append(TlvTag.Pin, environment.pin1) tlvBuilder.append(TlvTag.CardId, environment.card?.cardId) @@ -154,7 +154,7 @@ class ReadIssuerExtraDataCommand( ) } - override fun deserialize(environment: CardEnvironment, apdu: ResponseApdu): ReadIssuerExtraDataResponse { + override fun deserialize(environment: SessionEnvironment, apdu: ResponseApdu): ReadIssuerExtraDataResponse { val tlvData = apdu.getTlvData(environment.encryptionKey) ?: throw SessionError.DeserializeApduFailed() diff --git a/tangem-core/src/main/java/com/tangem/commands/ReadUserDataCommand.kt b/tangem-core/src/main/java/com/tangem/commands/ReadUserDataCommand.kt index 7209d424df..528ba98fb0 100644 --- a/tangem-core/src/main/java/com/tangem/commands/ReadUserDataCommand.kt +++ b/tangem-core/src/main/java/com/tangem/commands/ReadUserDataCommand.kt @@ -1,6 +1,6 @@ package com.tangem.commands -import com.tangem.CardEnvironment +import com.tangem.SessionEnvironment import com.tangem.SessionError import com.tangem.common.apdu.CommandApdu import com.tangem.common.apdu.Instruction @@ -49,7 +49,7 @@ class ReadUserDataResponse( */ class ReadUserDataCommand: Command() { - override fun serialize(environment: CardEnvironment): CommandApdu { + override fun serialize(environment: SessionEnvironment): CommandApdu { val builder = TlvBuilder() builder.append(TlvTag.CardId, environment.card?.cardId) builder.append(TlvTag.Pin, environment.pin1) @@ -60,7 +60,7 @@ class ReadUserDataCommand: Command() { ) } - override fun deserialize(environment: CardEnvironment, apdu: ResponseApdu): ReadUserDataResponse { + override fun deserialize(environment: SessionEnvironment, apdu: ResponseApdu): ReadUserDataResponse { val tlvData = apdu.getTlvData(environment.encryptionKey) ?: throw SessionError.DeserializeApduFailed() diff --git a/tangem-core/src/main/java/com/tangem/commands/SignCommand.kt b/tangem-core/src/main/java/com/tangem/commands/SignCommand.kt index 0a48b022d6..7707f82197 100644 --- a/tangem-core/src/main/java/com/tangem/commands/SignCommand.kt +++ b/tangem-core/src/main/java/com/tangem/commands/SignCommand.kt @@ -1,6 +1,6 @@ package com.tangem.commands -import com.tangem.CardEnvironment +import com.tangem.SessionEnvironment import com.tangem.SessionError import com.tangem.common.apdu.CommandApdu import com.tangem.common.apdu.Instruction @@ -35,7 +35,7 @@ class SignCommand(private val hashes: Array) private val hashSizes = if (hashes.isNotEmpty()) hashes.first().size else 0 - override fun serialize(environment: CardEnvironment): CommandApdu { + override fun serialize(environment: SessionEnvironment): CommandApdu { val dataToSign = flattenHashes() val tlvBuilder = TlvBuilder() tlvBuilder.append(TlvTag.Pin, environment.pin1) @@ -71,7 +71,7 @@ class SignCommand(private val hashes: Array) * (this key should be generated and securily stored by the application). */ private fun addTerminalSignature( - environment: CardEnvironment, dataToSign: ByteArray, tlvBuilder: TlvBuilder) { + environment: SessionEnvironment, dataToSign: ByteArray, tlvBuilder: TlvBuilder) { environment.terminalKeys?.let { terminalKeyPair -> val signedData = dataToSign.sign(terminalKeyPair.privateKey) tlvBuilder.append(TlvTag.TerminalTransactionSignature, signedData) @@ -79,7 +79,7 @@ class SignCommand(private val hashes: Array) } } - override fun deserialize(environment: CardEnvironment, apdu: ResponseApdu): SignResponse { + override fun deserialize(environment: SessionEnvironment, apdu: ResponseApdu): SignResponse { val tlvData = apdu.getTlvData(environment.encryptionKey) ?: throw SessionError.DeserializeApduFailed() diff --git a/tangem-core/src/main/java/com/tangem/commands/WriteIssuerDataCommand.kt b/tangem-core/src/main/java/com/tangem/commands/WriteIssuerDataCommand.kt index 0345eb300f..a425f5a740 100644 --- a/tangem-core/src/main/java/com/tangem/commands/WriteIssuerDataCommand.kt +++ b/tangem-core/src/main/java/com/tangem/commands/WriteIssuerDataCommand.kt @@ -1,7 +1,7 @@ package com.tangem.commands -import com.tangem.CardEnvironment import com.tangem.CardSession +import com.tangem.SessionEnvironment import com.tangem.SessionError import com.tangem.commands.common.DefaultIssuerDataVerifier import com.tangem.commands.common.IssuerDataMode @@ -76,7 +76,7 @@ class WriteIssuerDataCommand( ) } - override fun serialize(environment: CardEnvironment): CommandApdu { + override fun serialize(environment: SessionEnvironment): CommandApdu { val tlvBuilder = TlvBuilder() tlvBuilder.append(TlvTag.Pin, environment.pin1) tlvBuilder.append(TlvTag.CardId, environment.card?.cardId) @@ -91,7 +91,7 @@ class WriteIssuerDataCommand( ) } - override fun deserialize(environment: CardEnvironment, apdu: ResponseApdu): WriteIssuerDataResponse { + override fun deserialize(environment: SessionEnvironment, apdu: ResponseApdu): WriteIssuerDataResponse { val tlvData = apdu.getTlvData(environment.encryptionKey) ?: throw SessionError.DeserializeApduFailed() diff --git a/tangem-core/src/main/java/com/tangem/commands/WriteIssuerExtraDataCommand.kt b/tangem-core/src/main/java/com/tangem/commands/WriteIssuerExtraDataCommand.kt index 29bf3cd0d5..4a93ebd734 100644 --- a/tangem-core/src/main/java/com/tangem/commands/WriteIssuerExtraDataCommand.kt +++ b/tangem-core/src/main/java/com/tangem/commands/WriteIssuerExtraDataCommand.kt @@ -1,7 +1,7 @@ package com.tangem.commands -import com.tangem.CardEnvironment import com.tangem.CardSession +import com.tangem.SessionEnvironment import com.tangem.SessionError import com.tangem.commands.common.DefaultIssuerDataVerifier import com.tangem.commands.common.IssuerDataMode @@ -119,7 +119,7 @@ class WriteIssuerExtraDataCommand( } - override fun serialize(environment: CardEnvironment): CommandApdu { + override fun serialize(environment: SessionEnvironment): CommandApdu { val tlvBuilder = TlvBuilder() tlvBuilder.append(TlvTag.Pin, environment.pin1) @@ -154,7 +154,7 @@ class WriteIssuerExtraDataCommand( return if (bytesLeft < SINGLE_WRITE_SIZE) bytesLeft else SINGLE_WRITE_SIZE } - override fun deserialize(environment: CardEnvironment, apdu: ResponseApdu): WriteIssuerDataResponse { + override fun deserialize(environment: SessionEnvironment, apdu: ResponseApdu): WriteIssuerDataResponse { val tlvData = apdu.getTlvData(environment.encryptionKey) ?: throw SessionError.DeserializeApduFailed() diff --git a/tangem-core/src/main/java/com/tangem/commands/WriteUserDataCommand.kt b/tangem-core/src/main/java/com/tangem/commands/WriteUserDataCommand.kt index 912c57f7e6..e9771d0b87 100644 --- a/tangem-core/src/main/java/com/tangem/commands/WriteUserDataCommand.kt +++ b/tangem-core/src/main/java/com/tangem/commands/WriteUserDataCommand.kt @@ -1,6 +1,6 @@ package com.tangem.commands -import com.tangem.CardEnvironment +import com.tangem.SessionEnvironment import com.tangem.SessionError import com.tangem.common.apdu.CommandApdu import com.tangem.common.apdu.Instruction @@ -32,7 +32,7 @@ class WriteUserDataCommand(private val userData: ByteArray? = null, private val private val userCounter: Int? = null, private val userProtectedCounter: Int? = null) : Command() { - override fun serialize(environment: CardEnvironment): CommandApdu { + override fun serialize(environment: SessionEnvironment): CommandApdu { val builder = TlvBuilder() builder.append(TlvTag.CardId, environment.card?.cardId) builder.append(TlvTag.Pin, environment.pin1) @@ -49,7 +49,7 @@ class WriteUserDataCommand(private val userData: ByteArray? = null, private val ) } - override fun deserialize(environment: CardEnvironment, apdu: ResponseApdu): WriteUserDataResponse { + override fun deserialize(environment: SessionEnvironment, apdu: ResponseApdu): WriteUserDataResponse { val tlvData = apdu.getTlvData(environment.encryptionKey) ?: throw SessionError.DeserializeApduFailed() return WriteUserDataResponse(TlvDecoder(tlvData).decode(TlvTag.CardId)) diff --git a/tangem-core/src/main/java/com/tangem/commands/personalization/DepersonalizeCommand.kt b/tangem-core/src/main/java/com/tangem/commands/personalization/DepersonalizeCommand.kt index 2fcd962966..2ed9366331 100644 --- a/tangem-core/src/main/java/com/tangem/commands/personalization/DepersonalizeCommand.kt +++ b/tangem-core/src/main/java/com/tangem/commands/personalization/DepersonalizeCommand.kt @@ -1,6 +1,6 @@ package com.tangem.commands.personalization -import com.tangem.CardEnvironment +import com.tangem.SessionEnvironment import com.tangem.commands.Command import com.tangem.commands.CommandResponse import com.tangem.common.apdu.CommandApdu @@ -17,13 +17,13 @@ data class DepersonalizeResponse(val success: Boolean) : CommandResponse */ class DepersonalizeCommand : Command() { - override fun serialize(environment: CardEnvironment): CommandApdu { + override fun serialize(environment: SessionEnvironment): CommandApdu { return CommandApdu( Instruction.Depersonalize, byteArrayOf() ) } - override fun deserialize(environment: CardEnvironment, apdu: ResponseApdu): DepersonalizeResponse { + override fun deserialize(environment: SessionEnvironment, apdu: ResponseApdu): DepersonalizeResponse { return DepersonalizeResponse(true) } } \ No newline at end of file diff --git a/tangem-core/src/main/java/com/tangem/commands/personalization/PersonalizeCommand.kt b/tangem-core/src/main/java/com/tangem/commands/personalization/PersonalizeCommand.kt index 2d81d479a2..24a118322a 100644 --- a/tangem-core/src/main/java/com/tangem/commands/personalization/PersonalizeCommand.kt +++ b/tangem-core/src/main/java/com/tangem/commands/personalization/PersonalizeCommand.kt @@ -1,6 +1,6 @@ package com.tangem.commands.personalization -import com.tangem.CardEnvironment +import com.tangem.SessionEnvironment import com.tangem.SessionError import com.tangem.commands.Card import com.tangem.commands.CardData @@ -36,7 +36,7 @@ class PersonalizeCommand( private val acquirer: Acquirer? = null ) : Command() { - override fun serialize(environment: CardEnvironment): CommandApdu { + override fun serialize(environment: SessionEnvironment): CommandApdu { return CommandApdu( Instruction.Personalize, serializePersonalizationData(config), @@ -44,7 +44,7 @@ class PersonalizeCommand( ) } - override fun deserialize(environment: CardEnvironment, apdu: ResponseApdu): Card { + override fun deserialize(environment: SessionEnvironment, apdu: ResponseApdu): Card { val tlvData = apdu.getTlvData(devPersonalizationKey) ?: throw SessionError.DeserializeApduFailed() diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/TestUserDataActivity.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/TestUserDataActivity.kt index 0df7bd9aa6..7f3ba0639d 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/TestUserDataActivity.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/TestUserDataActivity.kt @@ -5,7 +5,7 @@ import android.view.View import android.widget.CompoundButton import android.widget.TextView import androidx.appcompat.app.AppCompatActivity -import com.tangem.CardEnvironment +import com.tangem.SessionEnvironment import com.tangem.SessionError import com.tangem.TangemSdk import com.tangem.common.CompletionResult @@ -143,7 +143,7 @@ class WriteOptions { } fun updatePin2(chbx: CompoundButton) { - val value = CardEnvironment.DEFAULT_PIN2 + val value = SessionEnvironment.DEFAULT_PIN2 pin2 = if (chbx.isChecked) value else null } } \ No newline at end of file diff --git a/tangem-sdk/src/main/java/com/tangem/tangem_sdk_new/DefaultCardSessionViewDelegate.kt b/tangem-sdk/src/main/java/com/tangem/tangem_sdk_new/DefaultSessionViewDelegate.kt similarity index 97% rename from tangem-sdk/src/main/java/com/tangem/tangem_sdk_new/DefaultCardSessionViewDelegate.kt rename to tangem-sdk/src/main/java/com/tangem/tangem_sdk_new/DefaultSessionViewDelegate.kt index 56cce78f88..d9cdf14304 100644 --- a/tangem-sdk/src/main/java/com/tangem/tangem_sdk_new/DefaultCardSessionViewDelegate.kt +++ b/tangem-sdk/src/main/java/com/tangem/tangem_sdk_new/DefaultSessionViewDelegate.kt @@ -6,10 +6,10 @@ import android.view.View import android.view.animation.DecelerateInterpolator import androidx.fragment.app.FragmentActivity import com.google.android.material.bottomsheet.BottomSheetDialog -import com.tangem.CardSessionViewDelegate import com.tangem.Log import com.tangem.LoggerInterface import com.tangem.Message +import com.tangem.SessionViewDelegate import com.tangem.common.CompletionResult import com.tangem.tangem_sdk_new.extensions.hide import com.tangem.tangem_sdk_new.extensions.show @@ -20,10 +20,10 @@ import kotlinx.android.synthetic.main.layout_touch_card.* import kotlinx.android.synthetic.main.nfc_bottom_sheet.* /** - * Default implementation of [CardSessionViewDelegate]. + * Default implementation of [SessionViewDelegate]. * If no customisation is required, this is the preferred way to use Tangem SDK. */ -class DefaultCardSessionViewDelegate(private val reader: NfcReader) : CardSessionViewDelegate { +class DefaultSessionViewDelegate(private val reader: NfcReader) : SessionViewDelegate { lateinit var activity: FragmentActivity private var readingDialog: BottomSheetDialog? = null diff --git a/tangem-sdk/src/main/java/com/tangem/tangem_sdk_new/extensions/TangemSdk.kt b/tangem-sdk/src/main/java/com/tangem/tangem_sdk_new/extensions/TangemSdk.kt index 90a47a9d29..f69b7c96cf 100644 --- a/tangem-sdk/src/main/java/com/tangem/tangem_sdk_new/extensions/TangemSdk.kt +++ b/tangem-sdk/src/main/java/com/tangem/tangem_sdk_new/extensions/TangemSdk.kt @@ -2,7 +2,7 @@ package com.tangem.tangem_sdk_new.extensions import androidx.fragment.app.FragmentActivity import com.tangem.TangemSdk -import com.tangem.tangem_sdk_new.DefaultCardSessionViewDelegate +import com.tangem.tangem_sdk_new.DefaultSessionViewDelegate import com.tangem.tangem_sdk_new.NfcLifecycleObserver import com.tangem.tangem_sdk_new.TerminalKeysStorage import com.tangem.tangem_sdk_new.nfc.NfcManager @@ -12,7 +12,7 @@ fun TangemSdk.Companion.init(activity: FragmentActivity): TangemSdk { this.setCurrentActivity(activity) activity.lifecycle.addObserver(NfcLifecycleObserver(this)) } - val viewDelegate = DefaultCardSessionViewDelegate(nfcManager.reader).apply { + val viewDelegate = DefaultSessionViewDelegate(nfcManager.reader).apply { this.activity = activity } return TangemSdk(nfcManager.reader, viewDelegate).apply { From 7de4b052fa51ce18536d6c5bf577ea798f7b5ddc Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 10 Apr 2020 18:28:11 +0300 Subject: [PATCH 31/39] Updated on 2026-08-14 --- tangem-core/src/main/java/com/tangem/CardSession.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tangem-core/src/main/java/com/tangem/CardSession.kt b/tangem-core/src/main/java/com/tangem/CardSession.kt index 1852ae25d9..9977e9b17f 100644 --- a/tangem-core/src/main/java/com/tangem/CardSession.kt +++ b/tangem-core/src/main/java/com/tangem/CardSession.kt @@ -7,6 +7,7 @@ import com.tangem.commands.ReadCommand import com.tangem.common.CompletionResult import com.tangem.common.apdu.CommandApdu import com.tangem.common.apdu.ResponseApdu +import com.tangem.common.extensions.calculateSha256 import com.tangem.crypto.EncryptionHelper import com.tangem.crypto.FastEncryptionHelper import com.tangem.crypto.StrongEncryptionHelper @@ -207,7 +208,7 @@ class CardSession( when (result) { is CompletionResult.Success -> { val uid = result.data.uid - val protocolKey = environment.pin1.pbkdf2Hash(uid, 50) + val protocolKey = environment.pin1.calculateSha256().pbkdf2Hash(uid, 50) val secret = encryptionHelper.generateSecret(result.data.sessionKeyB) val sessionKey = (secret + protocolKey).calculateSha256() environment.encryptionKey = sessionKey From 94a04d828014b0bd9f7647b281b42fbf60357c09 Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 10 Apr 2020 20:19:53 +0300 Subject: [PATCH 32/39] Updated on 2026-08-14 --- .../structure/abstraction/ItemViewModel.kt | 40 ++++++--- .../_arch/widget/abstraction/ViewWidget.kt | 20 ++++- .../commons/view/MultiActionView.kt | 85 +++++++++++++++++++ .../tangemtest/ucase/ui/ActionViewModel.kt | 39 ++++++++- .../ucase/ui/BaseCardActionFragment.kt | 7 +- .../personalize/converter/ItemTypes.kt | 75 +++++++++++++++- .../PersonalizationConfigConverter.kt | 2 +- .../personalize/ui/PersonalizationFragment.kt | 26 ++++++ .../ui/widgets/DescriptionWidget.kt | 12 +-- .../variants/responses/ResponseViewModel.kt | 2 +- .../responses/converter/CardConverter.kt | 4 +- .../main/res/layout/view_simple_button.xml | 20 +++++ tangem-demo/src/main/res/values/strings.xml | 3 + 13 files changed, 303 insertions(+), 32 deletions(-) create mode 100644 tangem-demo/src/main/java/com/tangem/tangemtest/commons/view/MultiActionView.kt create mode 100644 tangem-demo/src/main/res/layout/view_simple_button.xml diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/structure/abstraction/ItemViewModel.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/structure/abstraction/ItemViewModel.kt index 8c548ee17c..a542a3be4d 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/structure/abstraction/ItemViewModel.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/structure/abstraction/ItemViewModel.kt @@ -8,30 +8,46 @@ import com.tangem.tangemtest._arch.structure.PayloadHolder /** [REDACTED_AUTHOR] */ -typealias ValueChange = (V?) -> Unit -typealias SafeValueChange = (V) -> Unit +typealias ValueChanged = (V?) -> Unit +typealias SafeValueChanged = (V) -> Unit class KeyValue(val key: String, val value: Any) class ViewState( - var isHidden: Boolean = false, - var backgroundColor: Int? = -1 + isVisible: Boolean? = null, + bgColor: Int? = -1 ) { - var descriptionVisibility: Int = 0x00000008 - set(value) { - field = value - onDescriptionVisibilityChanged?.invoke(value) - } + class State( + stateValue: T, + var onValueChanged: SafeValueChanged? = null + ) { + var value = stateValue + set(value) { + if (preventSameChanges && field == value) return - var onDescriptionVisibilityChanged: SafeValueChange? = null + field = value + onValueChanged?.invoke(value) + } + + internal var preventSameChanges = true + } + + var isVisibleState = State(isVisible) + var backgroundColor = State(bgColor) + var descriptionVisibility = State(0x00000008) + + internal fun preventSameChanges(isPrevented: Boolean) { + val states = listOf(isVisibleState, backgroundColor, descriptionVisibility) + states.forEach { it.preventSameChanges = isPrevented } + } } interface ItemViewModel : PayloadHolder { val viewState: ViewState var data: Any? var defaultData: Any? - var onDataUpdated: ValueChange? + var onDataUpdated: ValueChanged? fun updateDataByView(data: Any?) } @@ -57,7 +73,7 @@ open class BaseItemViewModel( } // Use it for handling data updates in View - override var onDataUpdated: ValueChange? = null + override var onDataUpdated: ValueChanged? = null // When data updates directly it invokes onDataUpdated // return true = data will update diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/widget/abstraction/ViewWidget.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/widget/abstraction/ViewWidget.kt index 20fcb7f37f..9b6c5f9d2c 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/widget/abstraction/ViewWidget.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/widget/abstraction/ViewWidget.kt @@ -10,6 +10,7 @@ import com.tangem.tangemtest.R import com.tangem.tangemtest._arch.structure.StringId import com.tangem.tangemtest._arch.structure.StringResId import com.tangem.tangemtest._arch.structure.abstraction.Item +import com.tangem.tangemtest._arch.structure.abstraction.ViewState import ru.dev.gbixahue.eu4d.lib.android._android.views.colorFrom import ru.dev.gbixahue.eu4d.lib.kotlin.common.LayoutHolder @@ -34,11 +35,22 @@ abstract class BaseViewWidget( private var defaultBackground: Drawable? = view.background init { - if (item.viewModel.viewState.isHidden) { - view.visibility = View.GONE - } else { - setBackgroundColor(item.viewModel.viewState.backgroundColor) + subscribeToViewStateChanges(item.viewModel.viewState) + initViewState(item.viewModel.viewState) + } + + protected open fun subscribeToViewStateChanges(viewState: ViewState) { + viewState.isVisibleState.onValueChanged = { state -> + state?.let { view.visibility = if (it) View.VISIBLE else View.GONE } } + viewState.backgroundColor.onValueChanged = { setBackgroundColor(it) } + } + + protected open fun initViewState(viewState: ViewState) { + viewState.preventSameChanges(false) + viewState.isVisibleState.value = viewState.isVisibleState.value + if (viewState.backgroundColor.value != -1) setBackgroundColor(viewState.backgroundColor.value) + viewState.preventSameChanges(true) } override fun getName(): String { diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/commons/view/MultiActionView.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/commons/view/MultiActionView.kt new file mode 100644 index 0000000000..1586f00232 --- /dev/null +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/commons/view/MultiActionView.kt @@ -0,0 +1,85 @@ +package com.tangem.tangemtest.commons.view + +import android.widget.TextView +import androidx.annotation.StringRes +import com.tangem.tangemtest._arch.structure.Id +import com.tangem.tangemtest._arch.structure.StringId +import com.tangem.tangemtest._arch.structure.abstraction.SafeValueChanged +import ru.dev.gbixahue.eu4d.lib.android.global.log.Log +import ru.dev.gbixahue.eu4d.lib.kotlin.stringOf + +/** +[REDACTED_AUTHOR] + */ +open class MultiActionView( + stateList: MutableList, + val child: V +) { + + interface State { + val id: Id + + fun getAction(): StateButtonAction + fun getResNameId(): Int + } + + var afterAction: SafeValueChanged? = null + + var state: Id = DefaultId.default + set(value) { + if (field == value) return + + field = value + Log.d(this, "state changed to: ${getKey(value)}") + btnState = stateHolder[getKey(value)] + } + + init { + child.setOnClickListener { + val state = btnState ?: return@setOnClickListener + + Log.d(this, "child handled OnClick for: ${getKey(state.id)}") + state.getAction().invoke() + afterAction?.invoke(state.id) + } + } + + protected var btnState: State? = null + set(value) { + if (value == null) return + + field = value + Log.d(this, "btnState changed to: ${getKey(value.id)}") + child.setText(value.getResNameId()) + } + + protected val stateHolder: MutableMap = stateList.associateBy { getKey(it.id) }.toMutableMap() + + fun performAction(id: Id) { + Log.d(this, "performAction ${getKey(id)}") + state = id + child.performClick() + } + + protected open fun getKey(id: Id): String { + return when (id) { + is StringId -> id.value + else -> stringOf(id) + } + } +} + +enum class DefaultId : Id { default } + +typealias StateButtonAction = () -> Unit + +class ButtonState( + override val id: Id, + @StringRes val name: Int, + private val action: StateButtonAction +) : MultiActionView.State { + + override fun getAction(): StateButtonAction = action + + override fun getResNameId(): Int = name +} \ No newline at end of file diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/ActionViewModel.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/ActionViewModel.kt index a44367f98c..fc4956bb70 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/ActionViewModel.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/ActionViewModel.kt @@ -13,7 +13,9 @@ import com.tangem.tangemtest._arch.structure.abstraction.iterate import com.tangem.tangemtest.commons.performAction import com.tangem.tangemtest.ucase.domain.paramsManager.ItemsManager import com.tangem.tangemtest.ucase.domain.responses.ResponseJsonConverter +import com.tangem.tangemtest.ucase.resources.ActionType import com.tangem.tangemtest.ucase.tunnel.ViewScreen +import com.tangem.tangemtest.ucase.variants.personalize.converter.ItemTypes import com.tangem.tasks.ScanEvent import com.tangem.tasks.TaskError import com.tangem.tasks.TaskEvent @@ -73,7 +75,7 @@ class ActionViewModel(private val itemsManager: ItemsManager) : ViewModel(), Lif fun toggleDescriptionVisibility(state: Boolean) { ldItemList.value?.iterate { - it.viewModel.viewState.descriptionVisibility = if (state) View.VISIBLE else View.GONE + it.viewModel.viewState.descriptionVisibility.value = if (state) View.VISIBLE else View.GONE } } @@ -87,6 +89,41 @@ class ActionViewModel(private val itemsManager: ItemsManager) : ViewModel(), Lif itemsManager.payload.filterValues { it is ViewScreen }.forEach { keyList.add(it.key) } keyList.forEach { itemsManager.payload.remove(it) } } + + fun showFields(type: ActionType) { + toggleFieldsVisibility(type, true) + } + + fun hideFields(type: ActionType) { + toggleFieldsVisibility(type, false) + } + + private fun toggleFieldsVisibility(type: ActionType, show: Boolean) { + val oftenUsed = getItemsForTogglingVisibilityState(type) + val hidden = getItemIdsWhichWontShows(type) + itemsManager.getItems().iterate { + if (!hidden.contains(it.id)) { + if (!oftenUsed.contains(it.id)) { + it.viewModel.viewState.isVisibleState.value = show + } + } + + } + } + + private fun getItemsForTogglingVisibilityState(type: ActionType): List { + return when (type) { + ActionType.Personalize -> ItemTypes().oftenUsedList + else -> emptyList() + } + } + + private fun getItemIdsWhichWontShows(type: ActionType): List { + return when (type) { + ActionType.Personalize -> ItemTypes().hiddenList + else -> emptyList() + } + } } internal class Notifier(private val vm: ActionViewModel) { diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/BaseCardActionFragment.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/BaseCardActionFragment.kt index b9186f495a..7148be7ee5 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/BaseCardActionFragment.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/BaseCardActionFragment.kt @@ -47,9 +47,14 @@ abstract class BaseCardActionFragment : BaseFragment(), ActionView { actionVM.attachToPayload(mutableMapOf(PayloadKey.actionView to this as ActionView)) initFab() - createWidgets { subscribeToViewModelChanges() } + createWidgets { + widgetsWasCreated() + subscribeToViewModelChanges() + } } + protected open fun widgetsWasCreated() {} + protected open fun bindViews() { itemContainer = mainView.findViewById(R.id.ll_container) actionFab = mainView.findViewById(R.id.fab_action) diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/converter/ItemTypes.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/converter/ItemTypes.kt index 238a0aeaa0..b1013de610 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/converter/ItemTypes.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/converter/ItemTypes.kt @@ -1,5 +1,6 @@ package com.tangem.tangemtest.ucase.variants.personalize.converter +import com.tangem.tangemtest._arch.structure.Id import com.tangem.tangemtest.ucase.variants.personalize.* class ItemTypes { @@ -36,11 +37,81 @@ class ItemTypes { CardNumber.Number, Common.MaxSignatures, SignHashExProp.PinLessFloorLimit, Denomination.Denomination, Token.Decimal ) - val hiddenList = mutableListOf( + 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 ) -} \ No newline at end of file + val oftenUsedList = listOf( +// BlockId.CardNumber, + BlockId.Common, +// BlockId.SigningMethod, +// BlockId.SignHashExProp, +// BlockId.Denomination, +// BlockId.Token, + BlockId.ProdMask, +// BlockId.SettingsMask, +// BlockId.SettingsMaskProtocolEnc, + BlockId.SettingsMaskNdef, +// BlockId.Pins, + Common.Curve, + Common.Blockchain, + Common.BlockchainCustom, +// Common.MaxSignatures, + Common.CreateWallet, +// SigningMethod.SignTx, +// SigningMethod.SignTxRaw, +// SigningMethod.SignValidatedTx, +// SigningMethod.SignValidatedTxRaw, +// SigningMethod.SignValidatedTxIssuer, +// SigningMethod.SignValidatedTxRawIssuer, +// SigningMethod.SignExternal, +// SignHashExProp.PinLessFloorLimit, +// SignHashExProp.CryptoExKey, +// SignHashExProp.RequireTerminalCertSig, +// SignHashExProp.RequireTerminalTxSig, +// SignHashExProp.CheckPin3, +// Denomination.WriteOnPersonalize, +// Denomination.Denomination, +// Token.ItsToken, +// Token.Symbol, +// Token.ContractAddress, +// Token.Decimal, + 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, + SettingsMaskNdef.Aar, + SettingsMaskNdef.AarCustom, + SettingsMaskNdef.Uri, +// Pins.Pin, +// Pins.Pin2, +// Pins.Pin3, +// Pins.Cvc, + Pins.PauseBeforePin2 + ) + +} diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/converter/PersonalizationConfigConverter.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/converter/PersonalizationConfigConverter.kt index b5c7250462..d450ab54d9 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/converter/PersonalizationConfigConverter.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/converter/PersonalizationConfigConverter.kt @@ -147,7 +147,7 @@ class PersonalizationConfigToItems : ModelToItems { blocList.add(pins()) blocList.iterate { if (itemTypes.hiddenList.contains(it.id)) { - it.viewModel.viewState.isHidden = true + it.viewModel.viewState.isVisibleState.value = false } } return blocList diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/PersonalizationFragment.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/PersonalizationFragment.kt index d0216c725b..97d152d51b 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/PersonalizationFragment.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/ui/PersonalizationFragment.kt @@ -2,19 +2,26 @@ package com.tangem.tangemtest.ucase.variants.personalize.ui import android.os.Bundle import android.view.View +import android.view.ViewGroup +import android.widget.Button import androidx.lifecycle.Observer 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.Item import com.tangem.tangemtest._arch.widget.WidgetBuilder +import com.tangem.tangemtest.commons.view.ButtonState +import com.tangem.tangemtest.commons.view.MultiActionView import com.tangem.tangemtest.ucase.domain.paramsManager.ItemsManager import com.tangem.tangemtest.ucase.domain.paramsManager.PayloadKey import com.tangem.tangemtest.ucase.domain.paramsManager.managers.PersonalizationItemsManager +import com.tangem.tangemtest.ucase.resources.ActionType import com.tangem.tangemtest.ucase.tunnel.ActionView import com.tangem.tangemtest.ucase.tunnel.ItemError import com.tangem.tangemtest.ucase.ui.BaseCardActionFragment import com.tangem.tangemtest.ucase.variants.personalize.PersonalizationConfigStore import com.tangem.tangemtest.ucase.variants.personalize.ui.widgets.PersonalizationItemBuilder +import ru.dev.gbixahue.eu4d.lib.android._android.views.inflate import ru.dev.gbixahue.eu4d.lib.android.global.log.Log /** @@ -31,6 +38,25 @@ class PersonalizationFragment : BaseCardActionFragment() { lifecycle.addObserver(itemsManager as PersonalizationItemsManager) } + override fun widgetsWasCreated() { + super.widgetsWasCreated() + + val btnContainer = itemContainer.inflate(R.layout.view_simple_button) + val btn = btnContainer.findViewById