Updated on 2026-08-14
This commit is contained in:
commit
b4bcd8200c
115 changed files with 2302 additions and 1564 deletions
4
.gitignore
vendored
4
.gitignore
vendored
|
|
@ -13,6 +13,10 @@ local.properties
|
|||
.idea/caches/
|
||||
.idea/libraries/
|
||||
.idea/*.xml
|
||||
.idea/codeStyles
|
||||
|
||||
# LayoutInspector files
|
||||
.captures
|
||||
|
||||
# OS-specific files
|
||||
.DS_Store
|
||||
|
|
|
|||
116
.idea/codeStyles/Project.xml
generated
116
.idea/codeStyles/Project.xml
generated
|
|
@ -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>
|
||||
5
.idea/codeStyles/codeStyleConfig.xml
generated
5
.idea/codeStyles/codeStyleConfig.xml
generated
|
|
@ -1,5 +0,0 @@
|
|||
<component name="ProjectCodeStyleConfiguration">
|
||||
<state>
|
||||
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
|
||||
</state>
|
||||
</component>
|
||||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ enum class AnalyticsParam(val param: String) {
|
|||
FIRMWARE("firmware"),
|
||||
;
|
||||
}
|
||||
|
||||
class Analytics {
|
||||
companion object {
|
||||
fun setCardData(ctx: TangemContext): Bundle {
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ public class BtcData extends CoinData {
|
|||
super();
|
||||
}
|
||||
|
||||
private Long balanceConfirmed, balanceUnconfirmed;
|
||||
protected Long balanceConfirmed, balanceUnconfirmed;
|
||||
|
||||
private boolean useBlockcypher = false;
|
||||
|
||||
|
|
|
|||
16
app/src/main/java/com/tangem/wallet/ducatus/DucatusData.java
Normal file
16
app/src/main/java/com/tangem/wallet/ducatus/DucatusData.java
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
||||
}
|
||||
|
|
@ -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() {
|
||||
|
|
@ -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)
|
||||
|
|
@ -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))
|
||||
}
|
||||
|
|
@ -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>)
|
||||
|
|
@ -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?
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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
|
||||
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
package com.tangem.tangemtest.commons
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
interface Store<M> {
|
||||
fun save(config: M)
|
||||
fun restore(): M
|
||||
}
|
||||
|
|
@ -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()
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
|
|
@ -43,4 +43,6 @@ abstract class BaseAction : Action {
|
|||
}
|
||||
callback(commandResult, modifiedItems)
|
||||
}
|
||||
|
||||
override fun getActionByTag(payload: PayloadHolder, id: Id, attrs: AttrForAction): ((ActionCallback) -> Unit)? = null
|
||||
}
|
||||
|
|
@ -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) }
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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) }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
|
@ -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")
|
||||
))
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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>) {
|
||||
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
|
@ -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,
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
}
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -9,7 +9,7 @@ import com.tangem.commands.personalization.entities.Manufacturer
|
|||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
|
||||
interface DefaultPersonalizeParams {
|
||||
interface DefaultPersonalizationParams {
|
||||
companion object {
|
||||
|
||||
fun issuer(): Issuer {
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -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?) {
|
||||
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
@ -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()
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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()
|
||||
}
|
||||
|
|
@ -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 }
|
||||
}
|
||||
|
|
@ -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,
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -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)))
|
||||
}
|
||||
}
|
||||
|
|
@ -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())
|
||||
}
|
||||
}
|
||||
|
|
@ -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)))
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -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
Loading…
Add table
Add a link
Reference in a new issue