Updated on 2026-08-14

This commit is contained in:
Tangem 2020-04-10 13:57:14 +03:00
commit b4bcd8200c
115 changed files with 2302 additions and 1564 deletions

4
.gitignore vendored
View file

@ -13,6 +13,10 @@ local.properties
.idea/caches/
.idea/libraries/
.idea/*.xml
.idea/codeStyles
# LayoutInspector files
.captures
# OS-specific files
.DS_Store

View file

@ -1,116 +0,0 @@
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
<codeStyleSettings language="XML">
<indentOptions>
<option name="CONTINUATION_INDENT_SIZE" value="4" />
</indentOptions>
<arrangement>
<rules>
<section>
<rule>
<match>
<AND>
<NAME>xmlns:android</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>xmlns:.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:id</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:name</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>name</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>style</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
<order>ANDROID_ATTRIBUTE_ORDER</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>.*</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
</rules>
</arrangement>
</codeStyleSettings>
</code_scheme>
</component>

View file

@ -1,5 +0,0 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
</state>
</component>

View file

@ -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 {

View file

@ -20,7 +20,7 @@ public enum Blockchain {
Rootstock("RSK", "RBTC", 1.0, R.drawable.tangem2, "RSK"),
RootstockToken("RskToken", "RBTC", 1.0, R.drawable.tangem2, "RSK"),
Cardano("CARDANO", "ADA", 1000000.0, R.drawable.tangem2, "Cardano"),
Ripple ("XRP", "XRP", 1000000.0, R.drawable.ic_logo_xrp, "XRP"),
Ripple("XRP", "XRP", 1000000.0, R.drawable.ic_logo_xrp, "XRP"),
Binance("BINANCE", "BNB", 100000000.0, R.drawable.ic_logo_binance, "Binance"),
BinanceTestNet("BINANCE/test", "BNB", 100000000.0, R.drawable.ic_logo_binance, "Binance Testnet"),
Matic("MATIC", "MTX", 1.0, R.drawable.tangem2, "Matic"),
@ -89,37 +89,18 @@ public enum Blockchain {
return mImageResource;
}
public int getImageResource(android.content.Context context, String name) {
if (name==null || name.isEmpty())
return getImageResource();
name = name.toLowerCase();
int resourceId = context.getResources().getIdentifier(name + "_token", "drawable", context.getPackageName());
if (resourceId <= 0)
return R.drawable.ic_logo_ethereum;
return resourceId;
}
public static int getLogoImageResource(String blockchainID, String symbolName) {
switch (blockchainID) {
case "BTC":
return R.drawable.ic_logo_bitcoin;
case "Token":
public int getLogoImageResource(String symbolName) {
switch (this) {
case Token:
if (symbolName.equals("SEED"))
return R.drawable.ic_logo_seed;
else
return R.drawable.ic_logo_ethereum;
case "ETH":
return R.drawable.ic_logo_ethereum;
case "XLM":
return R.drawable.ic_logo_stellar;
break;
case RootstockToken:
if (symbolName.equals("RIF"))
return R.drawable.ic_logo_rif;
}
return R.drawable.tangem2;
return getImageResource();
}
}

View file

@ -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())

View file

@ -281,7 +281,7 @@ class VerifyCardFragment : BaseFragment(), NavigationResultListener, NfcAdapter.
tvInputs.text = engine!!.unspentInputsDescription
ivBlockchain.setImageResource(Blockchain.getLogoImageResource(ctx.card!!.blockchainID, ctx.card!!.tokenSymbol))
ivBlockchain.setImageResource(ctx.blockchain.getLogoImageResource(ctx.card!!.tokenSymbol))
var s = ""
for (signingM in ctx.card!!.allowedSigningMethod) {

View file

@ -22,6 +22,7 @@ enum class AnalyticsParam(val param: String) {
FIRMWARE("firmware"),
;
}
class Analytics {
companion object {
fun setCardData(ctx: TangemContext): Bundle {

View file

@ -15,7 +15,7 @@ public class BtcData extends CoinData {
super();
}
private Long balanceConfirmed, balanceUnconfirmed;
protected Long balanceConfirmed, balanceUnconfirmed;
private boolean useBlockcypher = false;

View file

@ -0,0 +1,16 @@
package com.tangem.wallet.ducatus;
import com.tangem.wallet.CoinEngine;
import com.tangem.wallet.btc.BtcData;
import java.math.BigDecimal;
public class DucatusData extends BtcData {
public CoinEngine.InternalAmount getBalanceInInternalUnits() {
if (balanceConfirmed != null) {
return new CoinEngine.InternalAmount(BigDecimal.valueOf(balanceConfirmed), "Satoshi");
} else {
return null;
}
}
}

View file

@ -46,15 +46,15 @@ import io.reactivex.observers.DisposableSingleObserver;
public class DucatusEngine extends BtcEngine {
private static final String TAG = DucatusEngine.class.getSimpleName();
public BtcData coinData = null;
public DucatusData coinData = null;
public DucatusEngine(TangemContext context) throws Exception {
super(context);
this.ctx = context;
if (context.getCoinData() == null) {
coinData = new BtcData();
coinData = new DucatusData();
context.setCoinData(coinData);
} else if (context.getCoinData() instanceof BtcData) {
coinData = (BtcData) context.getCoinData();
} else if (context.getCoinData() instanceof DucatusData) {
coinData = (DucatusData) context.getCoinData();
} else {
throw new Exception("Invalid type of Blockchain data for DucatusEngine");
}
@ -380,7 +380,7 @@ public class DucatusEngine extends BtcEngine {
@Override
public CoinData createCoinData() {
return new BtcData();
return new DucatusData();
}
@Override

View file

@ -7,14 +7,11 @@ import com.tangem.tangem_card.reader.CardProtocol.TangemException
import com.tangem.tangem_card.tasks.SignTask
import com.tangem.tangem_card.util.Util
import com.tangem.wallet.*
import com.tangem.wallet.binance.client.domain.Account
import com.tangem.wallet.flowDemo.proto.Observation
import com.tangem.wallet.flowDemo.proto.ObserveServiceGrpc
import io.grpc.ManagedChannelBuilder
import io.reactivex.Observable
import io.reactivex.Single
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.functions.Function
import io.reactivex.observers.DisposableSingleObserver
import io.reactivex.schedulers.Schedulers
import java.util.concurrent.TimeUnit
@ -247,4 +244,8 @@ class FlowDemoEngine : CoinEngine {
override fun createCoinData(): CoinData {
return FlowDemoData()
}
override fun isNftToken(): Boolean {
return true
}
}

View file

@ -109,8 +109,8 @@ public class XlmAssetEngine extends CoinEngine {
@Override
public boolean isBalanceNotZero() {
if (coinData == null) return false;
return (coinData.getXlmBalance() != null && coinData.getXlmBalance().notZero() &&
coinData.getAssetBalance() != null && coinData.getAssetBalance().notZero());
return (coinData.getXlmBalance() != null && coinData.getXlmBalance().notZero()) ||
(coinData.getAssetBalance() != null && coinData.getAssetBalance().notZero());
}
@Override
@ -128,6 +128,8 @@ public class XlmAssetEngine extends CoinEngine {
ctx.setMessage(R.string.general_wallet_empty);
} else if (awaitingConfirmation()) {
ctx.setMessage(R.string.loaded_wallet_message_wait);
} else if (coinData.getXlmBalance() == null || coinData.getXlmBalance().isZero()) {
ctx.setMessage(ctx.getString(R.string.confirm_transaction_error_not_enough_xlm_for_fee));
} else {
return true;
}
@ -259,7 +261,7 @@ public class XlmAssetEngine extends CoinEngine {
balanceValidator.setScore(100);
balanceValidator.setFirstLine(R.string.balance_validator_first_line_verified_balance);
balanceValidator.setSecondLine(R.string.balance_validator_second_line_confirmed_in_blockchain);
if (coinData.getXlmBalance().isZero()) {
if (!isBalanceNotZero()) {
balanceValidator.setFirstLine(R.string.balance_validator_first_line_empty_wallet);
balanceValidator.setSecondLine(R.string.empty_string);
}

View file

@ -210,6 +210,7 @@
<string name="confirm_transaction_error_cannot_calculate_fee">Cannot calculate fee! Wrong data received from the node</string>
<string name="confirm_transaction_error_incoming_transaction_unconfirmed">Please wait for confirmation of incoming transaction</string>
<string name="confirm_transaction_error_not_enough_eth_for_fee">Not enough ETH funds for fee</string>
<string name="confirm_transaction_error_not_enough_xlm_for_fee">Not enough XLM funds for fee</string>
<string name="confirm_transaction_error_not_enough_rbtc_for_fee">Not enough RBTC funds for fee</string>
<string name="confirm_transaction_error_not_enough_xlm_for_create">Target account is not created! Send 1+ XLM to create it</string>
<string name="confirm_transaction_error_pin_2_is_required">PIN2 is required to sign the payment</string>

View file

@ -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)

View file

@ -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
}

View file

@ -3,10 +3,6 @@ package com.tangem.tangemtest._arch.structure
/**
[REDACTED_AUTHOR]
*/
interface DataHolder<D> {
var viewModel: D
}
typealias Payload = MutableMap<String, Any?>
interface PayloadHolder {
@ -17,12 +13,4 @@ interface PayloadHolder {
fun set(key: String, value: Any?) {
payload[key] = value
}
}
interface ItemListHolder<I> {
fun setItems(list: MutableList<I>)
fun getItems(): MutableList<I>
fun addItem(item: I)
fun removeItem(item: I)
fun clear()
}

View file

@ -5,6 +5,10 @@ package com.tangem.tangemtest._arch.structure
*/
interface Id
class StringId(val value: String) : Id
class StringResId(val value: Int) : Id
enum class Additional : Id {
UNDEFINED,
JSON_INCOMING,

View file

@ -5,11 +5,11 @@ import com.tangem.tangemtest._arch.structure.Id
/**
[REDACTED_AUTHOR]
*/
fun List<Item>.findDataItem(id: Id): BaseItem<Any?>? {
var foundItem: BaseItem<Any?>? = null
fun List<Item>.findItem(id: Id): Item? {
var foundItem: Item? = null
iterate {
if (it.id == id) {
foundItem = it as? BaseItem<Any?>
foundItem = it
return@iterate
}
}
@ -19,8 +19,8 @@ fun List<Item>.findDataItem(id: Id): BaseItem<Any?>? {
fun List<Item>.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)
}
}
}

View file

@ -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<D>(
override var viewModel: ItemViewModel<D>
) : Item, DataHolder<ItemViewModel<D>> {
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 <D> getData(): D? = viewModel.data as? D
fun setData(value: Any?) {
viewModel.data = value
}
fun restoreDefaultData(){
fun restoreDefaultData() {
setData(viewModel.defaultData)
}
}
open class BaseItem(
override val id: Id,
override var viewModel: ItemViewModel
) : Item {
override var parent: Item? = null
}

View file

@ -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<Item>
fun setItems(list: MutableList<Item>)
fun getItems(): MutableList<Item>
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<Item> = mutableListOf()
override val payload: MutableMap<String, Any?> = mutableMapOf()
}
open class ListItemBlock(
override val id: Id
) : BaseBlock(), ItemListHolder<Item> {
override fun setItems(list: MutableList<Item>) {
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<Item> {
return itemList
}
override fun getItems(): MutableList<Item> = 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() {

View file

@ -11,57 +11,12 @@ import com.tangem.tangemtest._arch.structure.PayloadHolder
typealias ValueChange<V> = (V?) -> Unit
typealias SafeValueChange<V> = (V) -> Unit
interface ItemViewModel<D : Any?> : PayloadHolder {
val viewState: ViewState
var data: D?
var defaultData: D?
var onDataUpdated: ValueChange<D>?
class KeyValue(val key: String, val value: Any)
fun updateDataByView(data: D?)
}
open class BaseItemViewModel<D> : ItemViewModel<D> {
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<D>? = 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 ViewState {
var isHiddenField = false
class ViewState(
var isHidden: Boolean = false,
var backgroundColor: Int? = -1
) {
var descriptionVisibility: Int = 0x00000008
set(value) {
@ -70,4 +25,61 @@ class ViewState {
}
var onDescriptionVisibilityChanged: SafeValueChange<Int>? = null
}
}
interface ItemViewModel : PayloadHolder {
val viewState: ViewState
var data: Any?
var defaultData: Any?
var onDataUpdated: ValueChange<Any?>?
fun updateDataByView(data: Any?)
}
open class BaseItemViewModel(
value: Any? = null,
override val viewState: ViewState = ViewState()
) : ItemViewModel {
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<Any?>? = 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(
val itemList: List<KeyValue>,
var selectedItem: Any?,
override val viewState: ViewState = ViewState()
) : BaseItemViewModel(selectedItem)

View file

@ -1,19 +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.*
/**
[REDACTED_AUTHOR]
*/
class AnyItem(override val id: Id, value: Any? = null) : BaseItem<Any>(AnyViewModel(value))
class TextItem(override val id: Id, value: String? = null) : BaseItem<String>(StringViewModel(value))
class EditTextItem(override val id: Id, value: String? = null) : BaseItem<String>(StringViewModel(value))
class NumberItem(override val id: Id, value: Number? = null) : BaseItem<Number>(NumberViewModel(value))
class BoolItem(override val id: Id, value: Boolean? = null) : BaseItem<Boolean>(BoolViewModel(value))
class ListItem(override val id: Id, value: List<KeyValue>, selectedValue: Any?)
: BaseItem<ListValueWrapper>(ListViewModel(ListValueWrapper(selectedValue, value))
)
open class TypedItem<D>(id: Id, viewModel: ItemViewModel) : BaseItem(id, viewModel) {
open fun getTypedData(): D? = viewModel.data as? D
}
inline fun<reified T> BaseItem<T>.getData(): T? = viewModel.data
class TextItem(id: Id, viewModel: ItemViewModel) : TypedItem<String>(id, viewModel) {
constructor(id: Id, value: String? = null, viewState: ViewState = ViewState())
: this(id, BaseItemViewModel(value, viewState))
}
class NumberItem(id: Id, viewModel: ItemViewModel) : TypedItem<Number>(id, viewModel) {
constructor(id: Id, value: Number? = null, viewState: ViewState = ViewState())
: this(id, BaseItemViewModel(value, viewState))
}
class BoolItem(id: Id, viewModel: ItemViewModel) : TypedItem<Boolean>(id, viewModel) {
constructor(id: Id, value: Boolean? = null, viewState: ViewState = ViewState())
: this(id, BaseItemViewModel(value, viewState))
}
class EditTextItem(id: Id, viewModel: ItemViewModel) : TypedItem<String>(id, viewModel) {
constructor(id: Id, value: String? = null, viewState: ViewState = ViewState())
: this(id, BaseItemViewModel(value, viewState))
}
class SpinnerItem(id: Id, viewModel: ListViewModel) : TypedItem<ListViewModel>(id, viewModel) {
constructor(id: Id, list: List<KeyValue>, selectedValue: Any?, viewState: ViewState = ViewState())
: this(id, ListViewModel(list, selectedValue, viewState))
}

View file

@ -1,29 +0,0 @@
package com.tangem.tangemtest._arch.structure.impl
import com.tangem.tangemtest._arch.structure.abstraction.BaseItemViewModel
/**
[REDACTED_AUTHOR]
*/
open class TransitiveViewModel<D>(value: D?) : BaseItemViewModel<D>() {
init {
defaultData = value
}
}
class AnyViewModel(value: Any? = null) : TransitiveViewModel<Any>(value)
class StringViewModel(value: String? = null) : TransitiveViewModel<String>(value)
class NumberViewModel(value: Number? = null) : TransitiveViewModel<Number>(value)
class BoolViewModel(value: Boolean? = null) : TransitiveViewModel<Boolean>(value)
class ListViewModel(value: ListValueWrapper? = null) : TransitiveViewModel<ListValueWrapper>(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<KeyValue>)

View file

@ -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?
}

View file

@ -0,0 +1,36 @@
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.Item
import com.tangem.tangemtest._arch.structure.abstraction.ItemGroup
import com.tangem.tangemtest._arch.widget.abstraction.ViewWidget
import com.tangem.tangemtest._arch.widget.impl.LinearGroupWidget
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 ItemGroup -> buildBlock(item, parent)
is BaseItem -> itemBuilder.build(item, parent)
else -> StubWidget(item.id, parent)
}
}
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(itemGroup.id, parent)
}
}
}

View file

@ -0,0 +1,67 @@
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
/**
[REDACTED_AUTHOR]
*/
interface ViewWidget : LayoutHolder {
val view: View
var item: Item
fun getName(): String
fun setBackgroundColor(@ColorRes colorId: Int?)
}
abstract class BaseViewWidget(
parent: ViewGroup,
override var item: Item
) : ViewWidget {
override val view: View = inflate(getLayoutId(), parent)
private var defaultBackground: Drawable? = view.background
init {
if (item.viewModel.viewState.isHidden) {
view.visibility = View.GONE
} else {
setBackgroundColor(item.viewModel.viewState.backgroundColor)
}
}
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)
}
}
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 {
val layoutId = if (id <= 0) R.layout.w_empty else id
val inflatedView = LayoutInflater.from(parent.context).inflate(layoutId, parent, false)
parent.addView(inflatedView)
return inflatedView
}

View file

@ -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
}

View file

@ -0,0 +1,15 @@
package com.tangem.tangemtest._arch.widget.impl
import android.view.ViewGroup
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.BaseItemViewModel
import com.tangem.tangemtest._arch.widget.abstraction.BaseViewWidget
/**
[REDACTED_AUTHOR]
*/
class StubWidget(id: Id, parent: ViewGroup) : BaseViewWidget(parent, BaseItem(id, BaseItemViewModel())) {
override fun getLayoutId(): Int = R.layout.w_empty
}

View file

@ -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")
}
}

View file

@ -2,6 +2,8 @@ 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]
@ -9,7 +11,16 @@ import androidx.lifecycle.ViewModel
class MainViewModel : ViewModel() {
val ldDescriptionSwitch = MutableLiveData<Boolean>(false)
var responseEvent: TaskEvent<*>? = null
fun switchToggled(state: Boolean) {
ldDescriptionSwitch.postValue(state)
}
fun changeResponseEvent(event: TaskEvent<*>?) {
Log.d(this, "changeResponseEvent")
val reqEvent = event ?: return
responseEvent = reqEvent
}
}

View file

@ -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<ActionType> {
return mutableListOf(
ActionType.Scan,

View file

@ -0,0 +1,9 @@
package com.tangem.tangemtest.commons
/**
[REDACTED_AUTHOR]
*/
interface Store<M> {
fun save(config: M)
fun restore(): M
}

View file

@ -3,6 +3,7 @@ package com.tangem.tangemtest.commons
/**
[REDACTED_AUTHOR]
*/
fun <A, B> performAction(a: A?, b: B?, action: (A, B) -> Unit) {
fun <A, B> performAction(a: A?, b: B?, action: (A, B) -> Unit, onFail: (() -> Unit)? = null) {
if (a != null && b != null) action(a, b)
else onFail?.invoke()
}

View file

@ -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)
}

View file

@ -43,4 +43,6 @@ abstract class BaseAction : Action {
}
callback(commandResult, modifiedItems)
}
override fun getActionByTag(payload: PayloadHolder, id: Id, attrs: AttrForAction): ((ActionCallback) -> Unit)? = null
}

View file

@ -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.tangemSdk.depersonalize(cardId) { handleResult(payload, it, null, attrs, callback) }

View file

@ -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
@ -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.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.converter.PersonalizationConfigConverter
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
/**
@ -33,12 +33,12 @@ 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 cardConfig = PersonalizeConfigToCardConfig().convert(personalizeConfig)
val personalizeConfig = PersonalizationConfigConverter().convert(itemList, PersonalizationConfig())
val cardConfig = PersonalizationConfigToCardConfig().convert(personalizeConfig)
attrs.tangemSdk.personalize(cardConfig, issuer, manufacturer, acquirer) {
handleResult(payload, it, null, attrs, callback)
@ -46,18 +46,18 @@ class PersonalizeAction : BaseAction() {
}
private fun checkSeries(itemList: List<Item>): 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<Item>): Boolean {
val seriesItem = itemList.findDataItem(CardNumber.Series) as? EditTextItem ?: return false
val numberItem = itemList.findDataItem(CardNumber.Number) as? NumberItem ?: return false
val seriesData = seriesItem.getData() ?: return false
val numberData = stringOf(numberItem.getData())
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)

View file

@ -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.tangemSdk.sign(arrayOf(hash), stringOf(cardId)) { handleResult(payload, it, null, attrs, callback) }
}

View file

@ -1,25 +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.kotlin.common.BaseTypedHolder
/**
[REDACTED_AUTHOR]
*/
class ParamsManagerFactory : BaseTypedHolder<ActionType, ItemsManager>() {
companion object {
fun createFactory(): ParamsManagerFactory {
return ParamsManagerFactory().apply {
register(ActionType.Scan, ScanItemsManager())
register(ActionType.Sign, SignItemsManager())
register(ActionType.Personalize, PersonalizeItemsManager())
register(ActionType.Depersonalize, DepersonalizeItemsManager())
}
}
}
}

View file

@ -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<Item>)
fun getItems(): List<Item>
fun setItemChangeConsequences(consequence: ItemsChangeConsequence?)
fun invokeMainAction(tangemSdk: TangemSdk, callback: ActionCallback)
fun getActionByTag(id: Id, tangemSdk: TangemSdk): ((ActionCallback) -> Unit)?
fun attachPayload(payload: Payload)
val consequence: ItemsChangeConsequence?
}
interface PayloadKey {

View file

@ -1,39 +1,59 @@
package com.tangem.tangemtest.ucase.domain.paramsManager.managers
import androidx.lifecycle.LifecycleObserver
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
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
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<Item> by lazy { createItemsList() }
override val consequence: ItemsChangeConsequence? = null
init {
Log.d(this, "new instance created")
}
override val payload: MutableMap<String, Any?> = mutableMapOf()
protected var changeConsequence: ItemsChangeConsequence? = null
protected val itemList: MutableList<Item> = mutableListOf()
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)
}
override fun getItems(): MutableList<Item> = itemList.toMutableList()
override fun setItems(items: List<Item>) {
itemList.clear()
itemList.addAll(items)
}
override fun getActionByTag(id: Id, tangemSdk: TangemSdk): ((ActionCallback) -> Unit)? = null
override fun getItems(): List<Item> = itemList
override fun setItemChangeConsequences(consequence: ItemsChangeConsequence?) {
this.changeConsequence = consequence
}
override fun invokeMainAction(tangemSdk: TangemSdk, callback: ActionCallback) {
action.executeMainAction(this, getAttrsForAction(tangemSdk), callback)
}
override fun getActionByTag(id: Id, tangemSdk: TangemSdk): ((ActionCallback) -> Unit)? {
return action.getActionByTag(this, id, getAttrsForAction(tangemSdk))
}
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(tangemSdk: TangemSdk)
: AttrForAction = AttrForAction(tangemSdk, itemList, payload, consequence)
abstract fun createItemsList(): List<Item>
: AttrForAction = AttrForAction(tangemSdk, itemList, payload, changeConsequence)
}

View file

@ -1,26 +0,0 @@
package com.tangem.tangemtest.ucase.domain.paramsManager.managers
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
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<Item> {
return listOf(EditTextItem(TlvId.CardId, null))
}
override fun invokeMainAction(tangemSdk: TangemSdk, callback: ActionCallback) {
action.executeMainAction(this, getAttrsForAction(tangemSdk), callback)
}
override fun getActionByTag(id: Id, tangemSdk: TangemSdk): ((ActionCallback) -> Unit)? {
return action.getActionByTag(this, id, getAttrsForAction(tangemSdk))
}
}

View file

@ -0,0 +1,35 @@
package com.tangem.tangemtest.ucase.domain.paramsManager.managers
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.OnLifecycleEvent
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.PersonalizationConfigConverter
import com.tangem.tangemtest.ucase.variants.personalize.dto.PersonalizationConfig
/**
[REDACTED_AUTHOR]
*/
class PersonalizationItemsManager(
private val store: Store<PersonalizationConfig>
) : BaseItemsManager(PersonalizeAction()) {
private val converter = PersonalizationConfigConverter()
init {
val config = store.restore()
setItems(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, PersonalizationConfig())
store.save(config)
}
}

View file

@ -1,17 +0,0 @@
package com.tangem.tangemtest.ucase.domain.paramsManager.managers
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
/**
[REDACTED_AUTHOR]
*/
class PersonalizeItemsManager : BaseItemsManager(PersonalizeAction()) {
override fun createItemsList(): List<Item> = listOf()
override fun invokeMainAction(tangemSdk: TangemSdk, callback: ActionCallback) {
action.executeMainAction(this, getAttrsForAction(tangemSdk), callback)
}
}

View file

@ -1,17 +0,0 @@
package com.tangem.tangemtest.ucase.domain.paramsManager.managers
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
/**
[REDACTED_AUTHOR]
*/
class ScanItemsManager : BaseItemsManager(ScanAction()) {
override fun createItemsList(): List<Item> = listOf()
override fun invokeMainAction(tangemSdk: TangemSdk, callback: ActionCallback) {
action.executeMainAction(this, getAttrsForAction(tangemSdk), callback)
}
}

View file

@ -1,34 +0,0 @@
package com.tangem.tangemtest.ucase.domain.paramsManager.managers
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
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<Item> {
return listOf(
EditTextItem(TlvId.CardId, null),
EditTextItem(TlvId.TransactionOutHash, "Data used for hashing")
)
}
override fun invokeMainAction(tangemSdk: TangemSdk, callback: ActionCallback) {
action.executeMainAction(this, getAttrsForAction(tangemSdk), callback)
}
override fun getActionByTag(id: Id, tangemSdk: TangemSdk): ((ActionCallback) -> Unit)? {
return action.getActionByTag(this, id, getAttrsForAction(tangemSdk))
}
}

View file

@ -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")
))
}
}

View file

@ -5,7 +5,7 @@ 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
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,18 +18,18 @@ import ru.dev.gbixahue.eu4d.lib.android.global.threading.postUI
*/
class AfterScanModifier : AfterActionModification {
override fun modify(payload: PayloadHolder, commandResult: CompletionResult<*>, itemList: List<Item>): List<Item> {
val foundItem = itemList.findDataItem(TlvId.CardId) ?: return listOf()
val foundItem = itemList.findItem(TlvId.CardId) ?: return listOf()
val card = smartCast(commandResult) ?: return listOf()
val actionView = payload.get(PayloadKey.actionView) as? ActionView ?: return listOf()
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)
}
}

View file

@ -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
@ -22,20 +22,20 @@ interface ItemsChangeConsequence {
fun affectChanges(payload: PayloadHolder, changedItem: Item, itemList: List<Item>): List<Item>?
}
class SignScanConsequence: ItemsChangeConsequence {
class SignScanConsequence : ItemsChangeConsequence {
override fun affectChanges(payload: PayloadHolder, changedItem: Item, itemList: List<Item>): List<Item>? {
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) {
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) {
val hashedData = when (card.curve) {
EllipticCurve.Secp256k1 -> dataForHashing.calculateSha256()
EllipticCurve.Ed25519 -> dataForHashing.calculateSha512()
else -> return null

View file

@ -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<ByteArray> {
@ -57,7 +65,7 @@ class SigningMethodTypeAdapter : JsonSerializer<SigningMethod> {
}
}
class DateTypeAdapter: JsonSerializer<Date> {
class DateTypeAdapter : JsonSerializer<Date> {
override fun serialize(src: Date, typeOfSrc: Type, context: JsonSerializationContext): JsonElement {
val formatter = DateFormat.getDateInstance(DateFormat.DEFAULT, Locale("en_US"))
return JsonPrimitive(formatter.format(src).toString())

View file

@ -1,9 +1,13 @@
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.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
@ -14,13 +18,31 @@ open class ResourceHolder<T> : BaseTypedHolder<T, Resources>()
object MainResourceHolder : ResourceHolder<Id>() {
init {
PersonalizeResources().init(this)
PersonalizationResources().init(this)
ActionResources().init(this)
TlvResources().init(this)
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 <reified Res : Resources> safeGet(id: Id): Res {
val result = super.get(id) ?: Resources(R.string.unknown, R.string.unknown)
return result as Res
}
}
}

View file

@ -10,7 +10,7 @@ import ru.dev.gbixahue.eu4d.lib.kotlin.common.TypedHolder
/**
[REDACTED_AUTHOR]
*/
class PersonalizeResources() {
class PersonalizationResources() {
fun init(holder: ResourceHolder<Id>) {
initBlock(holder)
initCardNumber(holder)
@ -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<Id, Resources>) {
private fun initSettingsMaskNdef(holder: TypedHolder<Id, Resources>) {
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<Id, Resources>) {

View file

@ -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))
}
}

View file

@ -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,
}

View file

@ -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, additionalHandler: ((Id) -> Int)? = null)
}

View file

@ -0,0 +1,145 @@
package com.tangem.tangemtest.ucase.ui
import android.view.View
import androidx.annotation.UiThread
import androidx.lifecycle.*
import com.google.gson.Gson
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
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
/**
[REDACTED_AUTHOR]
*/
class ActionViewModelFactory(private val manager: ItemsManager) : ViewModelProvider.NewInstanceFactory() {
override fun <T : ViewModel?> create(modelClass: Class<T>): T = ActionViewModel(manager) as T
}
class ActionViewModel(private val itemsManager: ItemsManager) : ViewModel(), LifecycleObserver {
val seResponseEvent = SingleLiveEvent<TaskEvent<*>>()
val seReadResponse = SingleLiveEvent<String>()
val seResponse = SingleLiveEvent<String>()
val ldItemList = MutableLiveData(itemsManager.getItems())
val seError: MutableLiveData<String> = SingleLiveEvent()
val seChangedItems: MutableLiveData<List<Item>> = SingleLiveEvent()
private val notifier: Notifier = Notifier(this)
private lateinit var tangemSdk: TangemSdk
fun setCardManager(tangemSdk: TangemSdk) {
this.tangemSdk = tangemSdk
}
@Deprecated("Events must be send directly from the Widget")
fun userChangedItem(id: Id, value: Any?) {
itemChanged(id, value)
}
private fun itemChanged(id: Id, value: Any?) {
itemsManager.itemChanged(id, value) { notifier.notifyItemsChanged(it) }
}
//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)
}
})
}
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<String>()
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(response: CompletionResult<*>, list: List<Item>) {
if (list.isNotEmpty()) notifyItemsChanged(list)
handleResponse(response)
}
@UiThread
fun notifyItemsChanged(list: List<Item>) {
vm.seChangedItems.postValue(list)
}
fun handleResponse(response: CompletionResult<*>) {
val taskEvent = response as? CompletionResult<CommandResponse> ?: return
when (commandResponse) {
is CompletionResult.Success -> handleDataEvent(commandResponse.data)
is CompletionResult.Failure -> handleError(commandResponse.error)
}
}
private fun handleDataEvent(event: CommandResponse?) {
when (event) {
is ScanEvent.OnReadEvent -> vm.seReadResponse.postValue(gson.toJson(event))
is ScanEvent.OnVerifyEvent -> {
}
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
}
}
}
}

View file

@ -4,9 +4,7 @@ import android.os.Bundle
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.view.plusAssign
import androidx.fragment.app.activityViewModels
import androidx.fragment.app.viewModels
import androidx.lifecycle.Observer
@ -17,134 +15,127 @@ import com.tangem.TangemSdk
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.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.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(), ActionView {
protected val incomingParamsContainer: ViewGroup by lazy {
mainView.findViewById<LinearLayout>(R.id.ll_incoming_params_container)
}
protected lateinit var mainView: View
protected val actionFab: FloatingActionButton by lazy { mainView.findViewById<FloatingActionButton>(R.id.fab_action) }
protected lateinit var itemContainer: ViewGroup
protected lateinit var actionFab: FloatingActionButton
protected val paramsVM: ParamsViewModel by viewModels { ActionViewModelFactory(itemsManager) }
protected val widgetList = mutableListOf<ParameterWidget>()
protected abstract val itemsManager: ItemsManager
protected val mainActivityVM by activityViewModels<MainViewModel>()
protected val actionVM: ActionViewModel by viewModels { ActionViewModelFactory(itemsManager) }
protected val UNDEFINED = -1
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
}
private val paramsWidgetList = mutableListOf<ParameterWidget>()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
Log.d(this, "onViewCreated")
viewLifecycleOwner.lifecycle.addObserver(paramsVM)
paramsVM.setCardManager(TangemSdk.init(requireActivity()))
paramsVM.attachToPayload(mutableMapOf(PayloadKey.actionView to this as ActionView))
bindViews()
viewLifecycleOwner.lifecycle.addObserver(actionVM)
actionVM.setCardManager(TangemSdk.init(requireActivity()))
actionVM.attachToPayload(mutableMapOf(PayloadKey.actionView to this as ActionView))
initFab()
showActionFab(false)
createWidgets { subscribeToViewModelChanges() }
}
private fun initFab() {
Log.d(this, "initFab")
actionFab.setOnClickListener { paramsVM.invokeMainAction() }
protected open fun bindViews() {
itemContainer = mainView.findViewById(R.id.ll_container)
actionFab = mainView.findViewById(R.id.fab_action)
}
private fun createWidgets(widgetCreatedCallback: () -> Unit) {
protected open fun initFab() {
enableActionFab(false)
actionFab.setOnClickListener { actionVM.invokeMainAction() }
}
protected open fun createWidgets(widgetCreatedCallback: () -> Unit) {
Log.d(this, "createWidgets")
paramsVM.ldParams.observe(viewLifecycleOwner, Observer { itemList ->
actionVM.ldItemList.observe(viewLifecycleOwner, Observer { itemList ->
itemList.forEach { param ->
val widget = ParameterWidget(inflateParamView(incomingParamsContainer), param)
widget.onValueChanged = { id, value -> paramsVM.userChangedItem(id, value) }
widget.onActionBtnClickListener = paramsVM.getItemAction(param.id)
widgetList.add(widget)
val widget = ParameterWidget(inflateParamView(itemContainer), param)
widget.onValueChanged = { id, value -> actionVM.userChangedItem(id, value) }
widget.onActionBtnClickListener = actionVM.getItemAction(param.id)
paramsWidgetList.add(widget)
}
widgetCreatedCallback()
})
}
private fun subscribeToViewModelChanges() {
protected open fun subscribeToViewModelChanges() {
Log.d(this, "subscribeToViewModelChanges")
listenEvent()
listenReadResponse()
listenResponse()
listenError()
listenChangedItems()
listenDescriptionSwitchChanges()
}
protected open fun listenEvent() {
actionVM.seResponseEvent.observe(viewLifecycleOwner, Observer {
mainActivityVM.changeResponseEvent(it)
})
}
protected open fun listenReadResponse() {}
protected open fun listenResponse() {
val tvResponse by lazy { mainView.findViewById<TextView>(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
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<Any?> ?: 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)
Log.d(this, "item changed from VM - name: ${item.id}, value:${item.viewModel.data}")
paramsWidgetList.firstOrNull { it.id == item.id }?.changeParamValue(item.viewModel.data)
}
})
}
protected open fun listenDescriptionSwitchChanges() {
val mainActViewModel by activityViewModels<MainViewModel>()
mainActViewModel.ldDescriptionSwitch.observe(viewLifecycleOwner, Observer { isEnabled ->
widgetList.forEach { it.toggleDescriptionVisibility(isEnabled) }
mainActivityVM.ldDescriptionSwitch.observe(viewLifecycleOwner, Observer {
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
}
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) {
// 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
}
}
override fun showSnackbar(id: Int) {
showSnackbar(requireContext().getString(id))
if (resourceId != UNDEFINED) showSnackbar(resourceId)
}
override fun showSnackbar(message: String) {
Snackbar.make(mainView, message, BaseTransientBottomBar.LENGTH_SHORT).show()
}
abstract fun getAction(): ActionType
}

View file

@ -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()
}
}

View file

@ -1,133 +0,0 @@
package com.tangem.tangemtest.ucase.ui
import androidx.annotation.UiThread
import androidx.lifecycle.*
import com.google.gson.Gson
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
import com.tangem.tangemtest._arch.structure.abstraction.Item
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 ru.dev.gbixahue.eu4d.lib.android.global.log.Log
/**
[REDACTED_AUTHOR]
*/
class ActionViewModelFactory(private val manager: ItemsManager) : ViewModelProvider.NewInstanceFactory() {
override fun <T : ViewModel?> create(modelClass: Class<T>): T = ParamsViewModel(manager) as T
}
class ParamsViewModel(private val itemsManager: ItemsManager) : ViewModel(), LifecycleObserver {
val ldCard = MutableLiveData<Card>()
val ldIsVerified = MutableLiveData<Boolean>()
val ldResponse = MutableLiveData<String>()
val ldReadResponse = MutableLiveData<String>()
val ldParams = MutableLiveData(itemsManager.getItems())
val seError: MutableLiveData<String> = SingleLiveEvent()
val seChangedItems: MutableLiveData<List<Item>> = SingleLiveEvent()
private val notifier: Notifier = Notifier(this)
private lateinit var tangemSdk: TangemSdk
fun setCardManager(tangemSdk: TangemSdk) {
this.tangemSdk = tangemSdk
}
fun userChangedItem(id: Id, value: Any?) {
itemChanged(id, value)
}
private fun itemChanged(id: Id, value: Any?) {
itemsManager.itemChanged(id, value) { notifier.notifyItemsChanged(it) }
}
//invokes Scan, Sign etc...
fun invokeMainAction() {
performAction(itemsManager, tangemSdk) { paramsManager, cardManager ->
paramsManager.invokeMainAction(cardManager) { response, listOfChangedParams ->
notifier.handleActionResult(response, listOfChangedParams)
}
}
}
fun getItemAction(id: Id): (() -> Unit)? {
val itemFunction = itemsManager.getActionByTag(id, tangemSdk) ?: return null
return {
itemFunction { response, listOfChangedParams ->
notifier.handleActionResult(response, listOfChangedParams)
}
}
}
fun attachToPayload(payload: Payload) {
itemsManager.attachPayload(payload)
}
@OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
fun viewOnDestroy() {
val keyList = mutableListOf<String>()
itemsManager.payload.filterValues { it is ViewScreen }.forEach { keyList.add(it.key) }
keyList.forEach { itemsManager.payload.remove(it) }
}
}
internal class Notifier(private val vm: ParamsViewModel) {
private var notShowedError: SessionError? = null
private val gson: Gson = GsonInitializer().gson
fun handleActionResult(response: CompletionResult<*>, list: List<Item>) {
if (list.isNotEmpty()) notifyItemsChanged(list)
handleResponse(response)
}
@UiThread
fun notifyItemsChanged(list: List<Item>) {
vm.seChangedItems.postValue(list)
}
fun handleResponse(response: CompletionResult<*>) {
val commandResponse = response as? CompletionResult<CommandResponse> ?: return
when (commandResponse) {
is CompletionResult.Failure -> handleError(commandResponse.error)
is CompletionResult.Success -> handleDataEvent(commandResponse.data)
}
}
private fun handleDataEvent(data: CommandResponse?) {
when (data) {
is Card -> {
vm.ldCard.postValue(data)
vm.ldReadResponse.postValue(gson.toJson(data))
}
else -> vm.ldResponse.postValue(gson.toJson(data))
}
}
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 = error
}
}
}

View file

@ -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<Any> = item as BaseItem<Any>
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<TextView>(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))

View file

@ -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
}

View file

@ -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,20 +92,21 @@ 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,
Aar,
AarCustom
AarCustom,
Uri,
}
enum class Pins : BlockItem {
enum class Pins : PersonalizationId {
Pin,
Pin2,
Pin3,

View file

@ -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.PersonalizationConfig
class PersonalizationConfigStore(context: Context) : Store<PersonalizationConfig> {
private val key = "personalization_config"
private val sp: SharedPreferences = (context.applicationContext as AppTangemDemo).sharedPreferences()
private val gson: Gson = Gson()
override fun save(config: PersonalizationConfig) {
sp.edit(true) { putString(key, gson.toJson(config)) }
}
override fun restore(): PersonalizationConfig {
val json = sp.getString(key, gson.toJson(PersonalizationConfig()))
return gson.fromJson(json, PersonalizationConfig::class.java)
}
}

View file

@ -2,18 +2,18 @@ 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.PersonalizeConfig
import com.tangem.tangemtest.ucase.variants.personalize.dto.PersonalizationConfig
import ru.dev.gbixahue.eu4d.lib.kotlin.common.BaseTypedHolder
/**
[REDACTED_AUTHOR]
*/
class PersonalizeConfigValuesHolder : BaseTypedHolder<Id, Value>() {
class ConfigValuesHolder : BaseTypedHolder<Id, Value>() {
// 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))
@ -67,6 +67,7 @@ class PersonalizeConfigValuesHolder : BaseTypedHolder<Id, Value>() {
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))

View file

@ -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.*
@ -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.IdCard, ProductMask.IdIssuerCard,SettingsMask.IsReusable, SettingsMask.NeedActivation,
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,
@ -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(

View file

@ -0,0 +1,303 @@
package com.tangem.tangemtest.ucase.variants.personalize.converter
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]
*/
class PersonalizationConfigConverter : ModelConverter<PersonalizationConfig> {
private val toModel: ItemsToModel<PersonalizationConfig> = ItemsToPersonalizationConfig()
private val toItems: ModelToItems<PersonalizationConfig> = PersonalizationConfigToItems()
override fun convert(from: List<Item>, default: PersonalizationConfig): PersonalizationConfig {
return toModel.convert(from, default)
}
override fun convert(from: PersonalizationConfig): List<Item> {
return toItems.convert(from)
}
}
class ItemsToPersonalizationConfig : ItemsToModel<PersonalizationConfig> {
protected val valuesHolder = ConfigValuesHolder()
override fun convert(from: List<Item>, default: PersonalizationConfig): PersonalizationConfig {
valuesHolder.init(default)
mapListItems(from)
return createModel()
}
private fun mapListItems(itemList: List<Item>) {
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 <reified Type> getTyped(id: Id): Type {
return getTypedBy<Type>(valuesHolder, id)!!
}
private inline fun <reified Type> 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<PersonalizationConfig> {
private val valuesHolder = ConfigValuesHolder()
private val itemTypes = ItemTypes()
override fun convert(from: PersonalizationConfig): List<Item> {
valuesHolder.init(from)
val blocList = mutableListOf<Item>()
blocList.add(cardNumber())
blocList.add(common())
blocList.add(signingMethod())
blocList.add(signHashExProperties())
blocList.add(denomination())
blocList.add(token())
blocList.add(productMask())
blocList.add(settingsMask())
blocList.add(settingsMaskProtocolEnc())
blocList.add(settingsMaskNdef())
blocList.add(pins())
blocList.iterate {
if (itemTypes.hiddenList.contains(it.id)) {
it.viewModel.viewState.isHidden = 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<KeyValue>, holder.get())
itemTypes.boolList.contains(id) -> BoolItem(id, holder.get() as? Boolean)
itemTypes.editTextList.contains(id) -> EditTextItem(id, holder.get() as? String)
itemTypes.numberList.contains(id) -> NumberItem(id, holder.get() as? Number)
else -> SimpleItemGroup(Additional.UNDEFINED)
}
itemGroup.addItem(item)
}
}

View file

@ -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<PersonalizationConfig, CardConfig> {
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<NdefRecord>()
if (from.uri.isNotEmpty()) {
ndefs.add(NdefRecord(NdefRecord.Type.URI, from.uri))
}
when (from.aar) {
"None" -> null
"--- CUSTOM ---" -> NdefRecord(NdefRecord.Type.AAR, from.aarCustom)
else -> NdefRecord(NdefRecord.Type.AAR, from.aar)
}?.let { ndefs.add(it) }
return CardConfig(
"Tangem",
"Tangem Test",
from.series,
from.startNumber,
1000,
from.PIN,
from.PIN2,
from.PIN3,
from.hexCrExKey,
from.CVC,
from.pauseBeforePIN2.toInt(),
from.smartSecurityDelay,
EllipticCurve.byName(from.curveID) ?: EllipticCurve.Secp256k1,
signingMethod,
from.MaxSignatures.toInt(),
from.isReusable,
from.allowSwapPIN,
from.allowSwapPIN2,
from.useActivation,
from.useCVC,
from.useNDEF,
from.useDynamicNDEF,
from.oneApdu,
from.useBlock,
from.allowSelectBlockchain,
from.forbidPurgeWallet,
from.protocolAllowUnencrypted,
from.protocolAllowStaticEncryption,
from.protectIssuerDataAgainstReplay,
from.forbidDefaultPIN,
from.disablePrecomputedNDEF,
from.skipSecurityDelayIfValidatedByIssuer,
from.skipCheckPIN2andCVCIfValidatedByIssuer,
from.skipSecurityDelayIfValidatedByLinkedTerminal,
from.restrictOverwriteIssuerDataEx,
from.requireTerminalTxSignature,
from.requireTerminalCertSignature,
from.checkPIN3onCard,
from.createWallet,
cardData,
ndefs
)
}
}

View file

@ -1,26 +0,0 @@
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.ItemsToPersonalizeConfig
import com.tangem.tangemtest.ucase.variants.personalize.converter.fromTo.PersonalizeConfigToItem
import com.tangem.tangemtest.ucase.variants.personalize.dto.PersonalizeConfig
/**
[REDACTED_AUTHOR]
*/
class PersonalizeConfigConverter : ModelConverter<PersonalizeConfig> {
private val toModel: ItemsToModel<PersonalizeConfig> = ItemsToPersonalizeConfig()
private val toItems: ModelToItems<PersonalizeConfig> = PersonalizeConfigToItem()
override fun convert(from: List<Item>, default: PersonalizeConfig): PersonalizeConfig {
return toModel.convert(from, default)
}
override fun convert(from: PersonalizeConfig): List<Item> {
return toItems.convert(from)
}
}

View file

@ -1,117 +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.BaseItem
import com.tangem.tangemtest._arch.structure.abstraction.Block
import com.tangem.tangemtest._arch.structure.abstraction.Item
import com.tangem.tangemtest._arch.structure.abstraction.ItemsToModel
import com.tangem.tangemtest._arch.structure.impl.KeyValue
import com.tangem.tangemtest._arch.structure.impl.ListValueWrapper
import com.tangem.tangemtest.ucase.variants.personalize.*
import com.tangem.tangemtest.ucase.variants.personalize.converter.PersonalizeConfigValuesHolder
import com.tangem.tangemtest.ucase.variants.personalize.dto.PersonalizeConfig
import ru.dev.gbixahue.eu4d.lib.android.global.log.Log
/**
[REDACTED_AUTHOR]
*/
class ItemsToPersonalizeConfig : ItemsToModel<PersonalizeConfig> {
protected val valuesHolder = PersonalizeConfigValuesHolder()
override fun convert(from: List<Item>, default: PersonalizeConfig): PersonalizeConfig {
valuesHolder.init(default)
mapListItems(from)
return createModel()
}
private fun mapListItems(itemList: List<Item>) {
itemList.forEach { 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(): PersonalizeConfig {
val export = PersonalizeConfig()
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.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 <reified Type> getTyped(id: Id): Type {
return getTypedBy<Type>(valuesHolder, id)!!
}
private inline fun <reified Type> getTypedBy(holder: PersonalizeConfigValuesHolder, id: Id): Type? {
Log.d(this, "getTyped for id: $id")
var typedValue = holder.get(id)?.get()
typedValue = when (typedValue) {
is ListValueWrapper -> (typedValue.selectedItem as KeyValue).value as Type
else -> typedValue as? Type ?: null
}
return typedValue
}
}

View file

@ -1,301 +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.PersonalizeConfigValuesHolder
import com.tangem.tangemtest.ucase.variants.personalize.dto.PersonalizeConfig
import ru.dev.gbixahue.eu4d.lib.kotlin.common.Converter
import java.util.*
/**
[REDACTED_AUTHOR]
*/
class PersonalizeConfigToItem : ModelToItems<PersonalizeConfig> {
private val valuesHolder = PersonalizeConfigValuesHolder()
private val itemTypes = ItemTypes()
override fun convert(from: PersonalizeConfig): List<Item> {
valuesHolder.init(from)
val blocList = mutableListOf<Item>()
blocList.add(cardNumber())
blocList.add(common())
blocList.add(signingMethod())
blocList.add(signHashExProperties())
blocList.add(denomination())
blocList.add(token())
blocList.add(productMask())
blocList.add(settingsMask())
blocList.add(settingsMaskProtocolEnc())
blocList.add(settingsMaskNdef())
blocList.add(pins())
val payloadBlock = ListItemBlock(Additional.JSON_TAILS)
addPayload(payloadBlock, from)
blocList.add(payloadBlock)
blocList.iterate {
if (itemTypes.hiddenList.contains(it.id)) {
(it as BaseItem<*>)?.viewModel.viewState.isHiddenField = true
}
}
return blocList
}
private fun cardNumber(): Block {
val block = createBlock(BlockId.CardNumber)
mutableListOf(
CardNumber.Series,
CardNumber.Number,
CardNumber.BatchId
).forEach { createItem(block, it as Id) }
return block
}
private fun common(): Block {
val block = createBlock(BlockId.Common)
mutableListOf(
Common.Curve,
Common.Blockchain,
Common.BlockchainCustom,
Common.MaxSignatures,
Common.CreateWallet
).forEach { createItem(block, it) }
return block
}
private fun signingMethod(): Block {
val block = createBlock(BlockId.SigningMethod)
mutableListOf(
SigningMethod.SignTx,
SigningMethod.SignTxRaw,
SigningMethod.SignValidatedTx,
SigningMethod.SignValidatedTxRaw,
SigningMethod.SignValidatedTxIssuer,
SigningMethod.SignValidatedTxRawIssuer,
SigningMethod.SignExternal
).forEach { createItem(block, it) }
return block
}
private fun signHashExProperties(): Block {
val block = createBlock(BlockId.SignHashExProp)
mutableListOf(
SignHashExProp.PinLessFloorLimit,
SignHashExProp.CryptoExKey,
SignHashExProp.RequireTerminalCertSig,
SignHashExProp.RequireTerminalTxSig,
SignHashExProp.CheckPin3
).forEach { createItem(block, it) }
return block
}
private fun denomination(): Block {
val block = createBlock(BlockId.Denomination)
mutableListOf(
Denomination.WriteOnPersonalize,
Denomination.Denomination
).forEach { createItem(block, it) }
return block
}
private fun token(): Block {
val block = createBlock(BlockId.Token)
mutableListOf(
Token.ItsToken,
Token.Symbol,
Token.ContractAddress,
Token.Decimal
).forEach { createItem(block, it) }
return block
}
private fun productMask(): Block {
val block = createBlock(BlockId.ProdMask)
mutableListOf(
ProductMask.Note,
ProductMask.Tag,
ProductMask.IdCard,
ProductMask.IdIssuerCard
).forEach { createItem(block, it) }
return block
}
private fun settingsMask(): Block {
val block = createBlock(BlockId.SettingsMask)
mutableListOf(
SettingsMask.IsReusable,
SettingsMask.NeedActivation,
SettingsMask.ForbidPurge,
SettingsMask.AllowSelectBlockchain,
SettingsMask.UseBlock,
SettingsMask.OneApdu,
SettingsMask.UseCvc,
SettingsMask.AllowSwapPin,
SettingsMask.AllowSwapPin2,
SettingsMask.ForbidDefaultPin,
SettingsMask.SmartSecurityDelay,
SettingsMask.ProtectIssuerDataAgainstReplay,
SettingsMask.SkipSecurityDelayIfValidated,
SettingsMask.SkipPin2CvcIfValidated,
SettingsMask.SkipSecurityDelayOnLinkedTerminal,
SettingsMask.RestrictOverwriteExtraIssuerData
).forEach { createItem(block, it) }
return block
}
private fun settingsMaskProtocolEnc(): Block {
val block = createBlock(BlockId.SettingsMaskProtocolEnc)
mutableListOf(
SettingsMaskProtocolEnc.AllowUnencrypted,
SettingsMaskProtocolEnc.AllowStaticEncryption
).forEach { createItem(block, it) }
return block
}
private fun settingsMaskNdef(): Block {
val block = createBlock(BlockId.SettingsMaskNdef)
mutableListOf(
SettingsMaskNdef.UseNdef,
SettingsMaskNdef.DynamicNdef,
SettingsMaskNdef.DisablePrecomputedNdef,
SettingsMaskNdef.Aar,
SettingsMaskNdef.AarCustom
).forEach { createItem(block, it) }
return block
}
private fun pins(): Block {
val block = createBlock(BlockId.Pins)
mutableListOf(Pins.Pin, Pins.Pin2, Pins.Pin3, Pins.Cvc, Pins.PauseBeforePin2)
.forEach { createItem(block, it) }
return block
}
private fun createBlock(id: Id): ListItemBlock {
return ListItemBlock(id).apply { addItem(TextItem(id)) }
}
private fun addPayload(block: Block, from: PersonalizeConfig) {
block.payload[PayloadKey.incomingJson] = from
}
private fun createItem(block: ListItemBlock, id: Id) {
val holder = valuesHolder.get(id) ?: return
val item = when {
itemTypes.blockIdList.contains(id) -> TextItem(id, holder.get() as? String)
itemTypes.listItemList.contains(id) -> ListItem(id, holder.list as List<KeyValue>, holder.get())
itemTypes.boolList.contains(id) -> BoolItem(id, holder.get() as? Boolean)
itemTypes.editTextList.contains(id) -> EditTextItem(id, holder.get() as? String)
itemTypes.numberList.contains(id) -> NumberItem(id, holder.get() as? Number)
else -> ListItemBlock(Additional.UNDEFINED)
}
block.addItem(item)
}
}
class PersonalizeConfigToCardConfig : Converter<PersonalizeConfig, CardConfig> {
override fun convert(from: PersonalizeConfig): CardConfig {
val signingMethod = com.tangem.commands.SigningMethod.build(
signHash = from.SigningMethod0,
signRaw = from.SigningMethod1,
signHashValidatedByIssuer = from.SigningMethod2,
signRawValidatedByIssuer = from.SigningMethod3,
signHashValidatedByIssuerAndWriteIssuerData = from.SigningMethod4,
signRawValidatedByIssuerAndWriteIssuerData = from.SigningMethod5,
signPos = from.SigningMethod6
)
val isNote = from.cardData.product_note
val isTag = from.cardData.product_tag
val isIdCard = from.cardData.product_id_card
val isIdIssuer = from.cardData.product_id_issuer
val productMaskBuilder = ProductMaskBuilder()
if (isNote) productMaskBuilder.add(com.tangem.commands.ProductMask.note)
if (isTag) productMaskBuilder.add(com.tangem.commands.ProductMask.tag)
if (isIdCard) productMaskBuilder.add(com.tangem.commands.ProductMask.idCard)
if (isIdIssuer) productMaskBuilder.add(com.tangem.commands.ProductMask.idIssuer)
val productMask = productMaskBuilder.build()
var tokenSymbol: String? = null
var tokenContractAddress: String? = null
var tokenDecimal: Int? = null
if (from.itsToken) {
tokenSymbol = from.symbol
tokenContractAddress = from.contractAddress
tokenDecimal = from.decimal.toInt()
}
val blockchain = if (from.blockchain.isNotEmpty()) from.blockchain else from.blockchainCustom
val cardData = CardData(
blockchainName = blockchain,
batchId = from.batchId,
productMask = productMask,
tokenSymbol = tokenSymbol,
tokenContractAddress = tokenContractAddress,
tokenDecimal = tokenDecimal,
issuerName = null,
manufactureDateTime = Calendar.getInstance().time,
manufacturerSignature = null)
val ndefs = mutableListOf<NdefRecord>()
when (from.aar) {
"None" -> null
"--- CUSTOM ---" -> NdefRecord(NdefRecord.Type.AAR, from.aarCustom)
else -> NdefRecord(NdefRecord.Type.AAR, from.aar)
}?.let { ndefs.add(it) }
return CardConfig(
"Tangem",
"Tangem Test",
from.series,
from.startNumber,
1000,
from.PIN,
from.PIN2,
from.PIN3,
from.hexCrExKey,
from.CVC,
from.pauseBeforePIN2.toInt(),
from.smartSecurityDelay,
EllipticCurve.byName(from.curveID) ?: EllipticCurve.Secp256k1,
signingMethod,
from.MaxSignatures.toInt(),
from.isReusable,
from.allowSwapPIN,
from.allowSwapPIN2,
from.useActivation,
from.useCVC,
from.useNDEF,
from.useDynamicNDEF,
from.oneApdu,
from.useBlock,
from.allowSelectBlockchain,
from.forbidPurgeWallet,
from.protocolAllowUnencrypted,
from.protocolAllowStaticEncryption,
from.protectIssuerDataAgainstReplay,
from.forbidDefaultPIN,
from.disablePrecomputedNDEF,
from.skipSecurityDelayIfValidatedByIssuer,
from.skipCheckPIN2andCVCIfValidatedByIssuer,
from.skipSecurityDelayIfValidatedByLinkedTerminal,
from.restrictOverwriteIssuerDataEx,
from.requireTerminalTxSignature,
from.requireTerminalCertSignature,
from.checkPIN3onCard,
from.createWallet,
cardData,
ndefs
)
}
}

View file

@ -9,7 +9,7 @@ import com.tangem.commands.personalization.entities.Manufacturer
[REDACTED_AUTHOR]
*/
interface DefaultPersonalizeParams {
interface DefaultPersonalizationParams {
companion object {
fun issuer(): Issuer {

View file

@ -3,7 +3,7 @@ package com.tangem.tangemtest.ucase.variants.personalize.dto
/**
[REDACTED_AUTHOR]
*/
class PersonalizeConfig {
class PersonalizationConfig {
// Card number
var series = "BB"
@ -80,6 +80,7 @@ class PersonalizeConfig {
var disablePrecomputedNDEF = false
var aar = "com.tangem.wallet"
var aarCustom = ""
var uri = "https://tangem.com"
// Pins

View file

@ -0,0 +1,78 @@
package com.tangem.tangemtest.ucase.variants.personalize.ui
import android.os.Bundle
import android.view.View
import androidx.lifecycle.Observer
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.domain.paramsManager.managers.PersonalizationItemsManager
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.global.log.Log
/**
[REDACTED_AUTHOR]
*/
class PersonalizationFragment : BaseCardActionFragment() {
override val itemsManager: ItemsManager by lazy { PersonalizationItemsManager(PersonalizationConfigStore(requireContext())) }
override fun getLayoutId(): Int = R.layout.fg_personalization
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
lifecycle.addObserver(itemsManager as PersonalizationItemsManager)
}
override fun initFab() {
actionFab.setOnClickListener { actionVM.invokeMainAction() }
}
override fun createWidgets(widgetCreatedCallback: () -> Unit) {
Log.d(this, "createWidgets")
val itemList = mutableListOf<Item>()
actionVM.ldItemList.observe(viewLifecycleOwner, Observer { list ->
Log.d(this, "ldBlockList size: ${list.size}")
itemList.clear()
itemList.addAll(list)
itemList.forEach { WidgetBuilder(PersonalizationItemBuilder()).build(it, itemContainer) }
actionVM.attachToPayload(mutableMapOf(
PayloadKey.actionView to this as ActionView,
PayloadKey.itemList to itemList
))
widgetCreatedCallback()
})
}
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) {
ItemError.BadSeries -> R.string.card_error_bad_series
ItemError.BadCardNumber -> R.string.card_error_bad_series_number
else -> additionalHandler?.invoke(id) ?: UNDEFINED
}
}
}
}

View file

@ -1,116 +0,0 @@
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.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.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._main.MainViewModel
import com.tangem.tangemtest.ucase.domain.paramsManager.ItemsManager
import com.tangem.tangemtest.ucase.domain.paramsManager.ParamsManagerFactory
import com.tangem.tangemtest.ucase.domain.paramsManager.PayloadKey
import com.tangem.tangemtest.ucase.resources.ActionType
import com.tangem.tangemtest.ucase.tunnel.ActionView
import com.tangem.tangemtest.ucase.tunnel.CardError
import com.tangem.tangemtest.ucase.tunnel.ItemError
import com.tangem.tangemtest.ucase.ui.ActionViewModelFactory
import com.tangem.tangemtest.ucase.ui.ParamsViewModel
import com.tangem.tangemtest.ucase.variants.personalize.dto.PersonalizeConfig
import com.tangem.tangemtest.ucase.variants.personalize.ui.widgets.WidgetBuilder
import ru.dev.gbixahue.eu4d.lib.android.global.log.Log
/**
[REDACTED_AUTHOR]
*/
class PersonalizeFragment : Fragment(), 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<LinearLayout>(R.id.ll_container) }
private val actionFab: FloatingActionButton by lazy { mainView.findViewById<FloatingActionButton>(R.id.fab_action) }
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
Log.d(this, "onCreateView")
mainView = inflater.inflate(R.layout.fg_personalize, container, false)
return mainView
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val blockList = mutableListOf<Item>()
personalizeVM.ldBlockList.observe(viewLifecycleOwner, Observer { list ->
blockList.clear()
blockList.addAll(list)
blockList.forEach { WidgetBuilder().build(it, blockContainer) }
paramsVM.attachToPayload(mutableMapOf(
PayloadKey.actionView to this as ActionView,
PayloadKey.itemList to blockList
))
})
mainActivityVM.ldDescriptionSwitch.observe(viewLifecycleOwner, Observer {
personalizeVM.toggleDescriptionVisibility(it)
})
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}")
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) {
// MainResourceHolder.safeGet<>()
when (id) {
CardError.NotPersonalized -> showSnackbar(R.string.card_error_not_personalized)
ItemError.BadSeries -> showSnackbar(R.string.card_error_bad_series)
ItemError.BadCardNumber -> showSnackbar(R.string.card_error_bad_series_number)
else -> showSnackbar(requireContext().getString(R.string.unknown))
}
}
override fun showSnackbar(id: Int) {
showSnackbar(requireContext().getString(id))
}
override fun showSnackbar(message: String) {
Snackbar.make(mainView, message, BaseTransientBottomBar.LENGTH_SHORT).show()
}
}

View file

@ -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 <T : ViewModel?> create(modelClass: Class<T>): T = PersonalizeViewModel(config) as T
}
class PersonalizeViewModel(private val config: PersonalizeConfig) : ViewModel() {
val ldBlockList: MutableLiveData<List<Item>> by lazy { MutableLiveData(initBlockList()) }
private fun initBlockList(): List<Item> = 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
}
}
}

View file

@ -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.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 {
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<Resources>(item.id).resName
fun ViewWidget.getResDescriptionId(): Int? = MainResourceHolder.get(item.id)?.resDescription

View file

@ -1,38 +1,36 @@
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 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.structure.abstraction.Item
import ru.dev.gbixahue.eu4d.lib.android.global.log.Log
/**
[REDACTED_AUTHOR]
*/
abstract class DescriptionWidget<D>(
abstract class DescriptionWidget(
parent: ViewGroup,
data: BaseItem<D>
) : BaseWidget<D>(parent, data) {
item: Item
) : BaseAppWidget(parent, item) {
private val descriptionContainer: ViewGroup by lazy { view.findViewById<ViewGroup>(R.id.container_description) }
private val tvDescription: TextView? by lazy { descriptionContainer.findViewById<TextView>(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 fun changeDescriptionVisibility(state: Int) {
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
@ -40,15 +38,4 @@ abstract class DescriptionWidget<D>(
TransitionManager.beginDelayedTransition(view.parent as ViewGroup, AutoTransition())
descriptionContainer.visibility = state
}
}
}

View file

@ -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,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.ucase.variants.personalize.ui.widgets.abstraction.getResNameId
import ru.dev.gbixahue.eu4d.lib.android._android.views.moveCursorToEnd
import ru.dev.gbixahue.eu4d.lib.kotlin.stringOf
/**
[REDACTED_AUTHOR]
*/
class EditTextWidget(parent: ViewGroup, data: EditTextItem) : DescriptionWidget<String>(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<TextInputLayout>(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) {}
@ -30,10 +33,10 @@ class EditTextWidget(parent: ViewGroup, data: EditTextItem) : DescriptionWidget<
}
init {
tilItem.hint = tilItem.context.getString(getResNameId())
etItem.setText(dataItem.viewModel.data)
tilItem.hint = getName()
etItem.setText(typedItem.getTypedData())
etItem.addTextChangedListener(watcher)
dataItem.viewModel.onDataUpdated = { silentUpdate(it) }
item.viewModel.onDataUpdated = { silentUpdate(it as? String) }
}
private fun silentUpdate(value: String?) {

View file

@ -1,20 +1,19 @@
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
/**
[REDACTED_AUTHOR]
*/
class TextWidget(parent: ViewGroup, data: TextItem) : DescriptionWidget<String>(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<TextView>(R.id.tv_name)
init {
tvName?.setText(getResNameId())
tvName.text = getName()
}
}

View file

@ -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
@ -9,7 +9,6 @@ import com.google.android.material.textfield.TextInputLayout
import com.tangem.tangemtest.R
import com.tangem.tangemtest._arch.structure.impl.NumberItem
import com.tangem.tangemtest.ucase.variants.personalize.CardNumber
import com.tangem.tangemtest.ucase.variants.personalize.ui.widgets.abstraction.getResNameId
import ru.dev.gbixahue.eu4d.lib.android._android.views.addInputFilter
import ru.dev.gbixahue.eu4d.lib.android._android.views.moveCursorToEnd
import ru.dev.gbixahue.eu4d.lib.kotlin.stringOf
@ -17,7 +16,11 @@ import ru.dev.gbixahue.eu4d.lib.kotlin.stringOf
/**
[REDACTED_AUTHOR]
*/
class NumberWidget(parent: ViewGroup, data: NumberItem) : DescriptionWidget<Number>(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<TextInputLayout>(R.id.til_item)
@ -25,7 +28,7 @@ class NumberWidget(parent: ViewGroup, data: NumberItem) : DescriptionWidget<Numb
private val watcher = object : TextWatcher {
override fun afterTextChanged(s: Editable?) {
dataItem.viewModel.updateDataByView(getValue(stringOf(s)))
typedItem.viewModel.updateDataByView(getValue(stringOf(s)))
}
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
@ -33,14 +36,14 @@ class NumberWidget(parent: ViewGroup, data: NumberItem) : DescriptionWidget<Numb
}
init {
tilItem.hint = tilItem.context.getString(getResNameId())
tilItem.hint = getName()
//TODO: remove from Widget
if (dataItem.id == CardNumber.Number) etItem.addInputFilter(InputFilter.LengthFilter(13))
if (item.id == CardNumber.Number) etItem.addInputFilter(InputFilter.LengthFilter(13))
etItem.setText(stringOf(dataItem.viewModel.data))
etItem.setText(stringOf(typedItem.getTypedData()))
etItem.addTextChangedListener(watcher)
dataItem.viewModel.onDataUpdated = { silentUpdate(it) }
item.viewModel.onDataUpdated = { silentUpdate(it as? Number) }
}
private fun silentUpdate(value: Number?) {
@ -53,5 +56,4 @@ class NumberWidget(parent: ViewGroup, data: NumberItem) : DescriptionWidget<Numb
private fun getValue(value: String): Long {
return if (value.isEmpty()) 0L else value.toLong()
}
}

View file

@ -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 PersonalizationItemBuilder : ItemWidgetBuilder {
override fun build(item: BaseItem, parent: ViewGroup): ViewWidget? {
return when (item) {
is TextItem -> GroupTitleWidget(parent, item)
is EditTextItem -> EditTextWidget(parent, item)
is NumberItem -> NumberWidget(parent, item)
is BoolItem -> SwitchWidget(parent, item)
is SpinnerItem -> SpinnerWidget(parent, item)
else -> null
}
}
}

View file

@ -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
@ -7,50 +7,52 @@ 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.ucase.variants.personalize.ui.widgets.abstraction.getResNameId
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<ListValueWrapper>(parent, listItem) {
class SpinnerWidget(
parent: ViewGroup,
typedItem: SpinnerItem
) : DescriptionWidget(parent, typedItem) {
override fun getLayoutId(): Int = R.layout.w_personalize_item_spinner
private val data: ListValueWrapper = listItem.getData()!!
private val viewModel: ListViewModel = typedItem.viewModel as ListViewModel
private val spItem = view.findViewById<Spinner>(R.id.sp_item)
private val spAdapter = SpItemAdapter(data.itemList)
private val spinner = view.findViewById<Spinner>(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]
dataItem.viewModel.updateDataByView(data)
viewModel.selectedItem = viewModel.itemList[position].value
item.viewModel.updateDataByView(viewModel)
}
}
init {
val name = view.findViewById<TextView>(R.id.tv_name)
name.setText(getResNameId())
spItem.adapter = spAdapter
spAdapter.getItemPosition(stringOf(data.selectedItem))?.let {
spItem.setSelection(it)
name.text = getName()
spinner.adapter = spAdapter
spAdapter.getItemPosition(stringOf(viewModel.selectedItem))?.let {
spinner.setSelection(it)
}
spItem.onItemSelectedListener = onItemSelectedListener
dataItem.viewModel.onDataUpdated = {
it?.apply {
spItem.onItemSelectedListener = null
this.itemList.firstOrNull { item -> item.value == selectedItem }?.let {
val position = itemList.indexOf(it)
spItem.setSelection(position)
}
spItem.onItemSelectedListener = onItemSelectedListener
spinner.onItemSelectedListener = onItemSelectedListener
item.viewModel.onDataUpdated = {
val selectedItem = it as? String
spinner.onItemSelectedListener = null
viewModel.itemList.firstOrNull { item -> item.value == selectedItem }?.let { keyValue ->
val position = viewModel.itemList.indexOf(keyValue)
spinner.setSelection(position)
}
spinner.onItemSelectedListener = onItemSelectedListener
}
}
}

View file

@ -1,31 +1,34 @@
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
/**
[REDACTED_AUTHOR]
*/
class SwitchWidget(parent: ViewGroup, data: BoolItem) : DescriptionWidget<Boolean>(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<SwitchCompat>(R.id.sw_item)
private val changeListener = CompoundButton.OnCheckedChangeListener { buttonView, isChecked ->
dataItem.viewModel.updateDataByView(isChecked)
typedItem.viewModel.updateDataByView(isChecked)
}
init {
switchItem.setText(getResNameId())
switchItem.isChecked = dataItem.viewModel.data ?: false
switchItem.text = getName()
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)
}
}

View file

@ -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
}
}
}

View file

@ -1,54 +0,0 @@
package com.tangem.tangemtest.ucase.variants.personalize.ui.widgets.abstraction
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.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.kotlin.common.LayoutHolder
/**
[REDACTED_AUTHOR]
*/
interface ViewWidget : LayoutHolder {
val view: View
}
interface DataWidget<D> : ViewWidget {
var dataItem: BaseItem<D>
}
interface BlockViewWidget : ViewWidget, DataHolder<List<ItemViewModel<*>>>
abstract class BaseWidget<D>(
parent: ViewGroup,
override var dataItem: BaseItem<D>
) : ViewWidget, DataWidget<D> {
override val view: View = inflate(getLayoutId(), parent)
init {
if (dataItem.viewModel.viewState.isHiddenField) {
view.visibility = View.GONE
}
}
}
abstract class BaseBlockWidget(parent: ViewGroup) : BlockViewWidget {
override val view: View = inflate(getLayoutId(), parent)
}
fun DataWidget<*>.getResNameId(): Int = MainResourceHolder.safeGet<Resources>(dataItem.id).resName
fun DataWidget<*>.getResDescription(): Int? = MainResourceHolder.safeGet<Resources>(dataItem.id).resDescription
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)
parent.addView(inflatedView)
return inflatedView
}

View file

@ -1,14 +0,0 @@
package com.tangem.tangemtest.ucase.variants.personalize.ui.widgets.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
/**
[REDACTED_AUTHOR]
*/
class StubWidget(parent: ViewGroup) : BaseBlockWidget(parent) {
override fun getLayoutId(): Int = R.layout.w_empty
override var viewModel: List<ItemViewModel<*>> = listOf()
}

View file

@ -1,21 +0,0 @@
package com.tangem.tangemtest.ucase.variants.personalize.ui.widgets.impl.block
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
/**
[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<ItemViewModel<*>> = listOf()
get() = children.itemList.map { it as BaseItem<*> }.map { it.viewModel }
}

View file

@ -0,0 +1,44 @@
package com.tangem.tangemtest.ucase.variants.responses
import com.tangem.tangemtest._arch.structure.Id
/**
[REDACTED_AUTHOR]
*/
interface ResponseId : Id
enum class CardId : ResponseId {
cardId,
manufacturerName,
status,
firmwareVersion,
cardPublicKey,
settingsMask,
cardData,
issuerPublicKey,
curve,
maxSignatures,
signingMethod,
pauseBeforePin2,
walletPublicKey,
walletRemainingSignatures,
walletSignedHashes,
health,
isActivated,
activationSeed,
paymentFlowVersion,
userCounter,
userProtectedCounter,
}
enum class CardDataId : ResponseId {
batchId,
manufactureDateTime,
issuerName,
blockchainName,
manufacturerSignature,
productMask,
tokenSymbol,
tokenContractAddress,
tokenDecimal,
}

View file

@ -0,0 +1,56 @@
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
/**
[REDACTED_AUTHOR]
*/
class ResponseViewModel : ViewModel() {
private val convertersHolder = ConvertersStore()
private var itemList: List<Item>? = null
fun createItemList(taskEvent: TaskEvent<*>?): List<Item> {
Log.d(this, "createItemList: itemList size: ${itemList?.size ?: 0}")
val event = taskEvent as? TaskEvent.Event<Any> ?: return emptyList()
val type = event.data::class.java
val converter = convertersHolder.get(type) as? ModelToItems<Any> ?: return emptyList()
itemList = converter.convert(event.data)
return itemList!!
}
fun toggleDescriptionVisibility(state: Boolean) {
itemList?.iterate {
it.viewModel.viewState.descriptionVisibility = if (state) View.VISIBLE else View.GONE
}
}
fun determineTitleId(taskEvent: TaskEvent<*>?): Int {
val event = taskEvent as? TaskEvent.Event<Any> ?: 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
}
}
}

View file

@ -0,0 +1,105 @@
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.common.extensions.toHexString
import com.tangem.tangemtest.R
import com.tangem.tangemtest._arch.structure.Id
import com.tangem.tangemtest._arch.structure.StringId
import com.tangem.tangemtest._arch.structure.abstraction.*
import com.tangem.tangemtest._arch.structure.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<Card> {
override fun convert(from: Card): List<Item> {
val itemList = mutableListOf<Item>()
// val holder = GsonInitializer()
// itemList.add(TextItem(Additional.JSON_INCOMING, holder.gson.toJson(from)))
itemList.add(simpleFields(from))
itemList.add(cardData(from.cardData))
itemList.add(settingsMask(from.settingsMask))
hideEmptyNullFields(itemList)
return itemList
}
private fun hideEmptyNullFields(itemList: MutableList<Item>) {
itemList.iterate {
val data = it.getData<Any?>()
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))
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, 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?.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)))
group.addItem(TextItem(CardId.walletSignedHashes, stringOf(from.walletSignedHashes)))
group.addItem(TextItem(CardId.health, stringOf(from.health)))
group.addItem(TextItem(CardId.isActivated, stringOf(from.isActivated)))
group.addItem(TextItem(CardId.activationSeed, stringOf(from.activationSeed)))
group.addItem(TextItem(CardId.paymentFlowVersion, stringOf(from.paymentFlowVersion)))
group.addItem(TextItem(CardId.userCounter, stringOf(from.userCounter)))
// block.addItem(TextItem(CardId.UserProtectedCounter, stringOf(from.userProtectedCounter)))
return group
}
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)))
group.addItem(TextItem(CardDataId.issuerName, data.issuerName))
group.addItem(TextItem(CardDataId.blockchainName, data.blockchainName))
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))
return group
}
private fun settingsMask(from: SettingsMask?): Item {
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, colorId: Int? = null): ItemGroup {
return if (colorId == null) SimpleItemGroup(id)
else SimpleItemGroup(id, BaseItemViewModel(viewState = ViewState(backgroundColor = colorId)))
}
}

View file

@ -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<Type, Any>() {
init {
register(ScanEvent.OnReadEvent::class.java, ReadEventConverter())
register(SignResponse::class.java, SignResponseConverter())
register(Card::class.java, CardConverter())
register(DepersonalizeResponse::class.java, DepersonalizeResponseConverter())
}
}

View file

@ -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<ScanEvent.OnReadEvent> {
override fun convert(from: ScanEvent.OnReadEvent): List<Item> = CardConverter().convert(from.card)
}
class SignResponseConverter : ModelToItems<SignResponse> {
override fun convert(from: SignResponse): List<Item> {
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<DepersonalizeResponse> {
override fun convert(from: DepersonalizeResponse): List<Item> {
return listOf(TextItem(StringId("Is success"), stringOf(from.success)))
}
}

View file

@ -0,0 +1,75 @@
package com.tangem.tangemtest.ucase.variants.responses.ui
import android.os.Bundle
import android.view.*
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.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
/**
[REDACTED_AUTHOR]
*/
open class ResponseFragment : BaseFragment() {
private val mainActivityVM: MainViewModel by activityViewModels()
private val selfVM: ResponseViewModel by viewModels()
private val itemContainer: ViewGroup by lazy { mainView.findViewById<LinearLayout>(R.id.ll_container) }
override fun getLayoutId(): Int = R.layout.fg_card_response
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setTittle()
}
private fun setTittle() {
val titleId = selfVM.determineTitleId(mainActivityVM.responseEvent)
activity?.setTitle(titleId)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setHasOptionsMenu(true)
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 {
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(ResponseJsonConverter().convertEvent(mainActivityVM.responseEvent))
}
}
return super.onOptionsItemSelected(item)
}
}

View file

@ -0,0 +1,27 @@
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 ru.dev.gbixahue.eu4d.lib.android._android.views.colorFrom
/**
[REDACTED_AUTHOR]
*/
class CheckBoxWidget(
parent: ViewGroup,
private val typedItem: BoolItem
) : ResponseWidget(parent, typedItem) {
override fun getLayoutId(): Int = R.layout.w_response_item_checkbox
private val switchItem = view.findViewById<MaterialCheckBox>(R.id.sw_item)
init {
switchItem.text = getName()
switchItem.isChecked = typedItem.getData() ?: false
switchItem.isClickable = false
switchItem.setTextColor(switchItem.colorFrom(R.color.action_name))
}
}

View file

@ -0,0 +1,21 @@
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
/**
[REDACTED_AUTHOR]
*/
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
}
}
}

View file

@ -0,0 +1,30 @@
package com.tangem.tangemtest.ucase.variants.responses.ui.widget
import android.view.ViewGroup
import android.widget.TextView
import com.tangem.tangemtest.R
import com.tangem.tangemtest._arch.structure.impl.TextItem
/**
[REDACTED_AUTHOR]
*/
class ResponseTextWidget(
parent: ViewGroup,
private val typedItem: TextItem
) : ResponseWidget(parent, typedItem) {
override fun getLayoutId(): Int = R.layout.w_response_item
private val tvName: TextView = view.findViewById(R.id.tv_name)
private val tvValue: TextView = view.findViewById(R.id.tv_value)
init {
initWidgets()
}
private fun initWidgets() {
val data = typedItem.getData() as? String
tvName.text = getName()
tvValue.text = data
}
}

View file

@ -0,0 +1,33 @@
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<Any?>())
val clipboard = view.context.getSystemService(Context.CLIPBOARD_SERVICE) as? ClipboardManager
?: return@setOnClickListener
val clipboardData = "${getName()} - $data"
val clip: ClipData = ClipData.newPlainText("FieldValue", clipboardData)
clipboard.setPrimaryClip(clip)
val copyMessage = view.stringFrom(R.string.copy_to_clipboard)
view.toast("$copyMessage\n$clipboardData")
}
}
}

View file

@ -2,36 +2,29 @@ package com.tangem.tangemtest.ucase.variants.scan.ui
import android.os.Bundle
import android.view.View
import android.widget.TextView
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 ru.dev.gbixahue.eu4d.lib.android._android.views.show
import ru.dev.gbixahue.eu4d.lib.android.global.log.Log
/**
[REDACTED_AUTHOR]
*/
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)
showActionFab(true)
enableActionFab(true)
}
override fun listenResponse() {
val tvStub = mainView.findViewById<TextView>(R.id.tv_screen_stub)
val tvResponse by lazy { mainView.findViewById<TextView>(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() {
actionVM.seReadResponse.observe(viewLifecycleOwner, Observer {
navigateTo(R.id.action_nav_card_action_to_response_screen)
})
}
}

View file

@ -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
}

Some files were not shown because too many files have changed in this diff Show more