diff --git a/app/build.gradle b/app/build.gradle index c30dedd2c2..10bac3b34e 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -158,4 +158,7 @@ dependencies { //dependencies for flow demo implementation 'io.grpc:grpc-okhttp:1.28.0' implementation 'io.grpc:grpc-stub:1.28.0' + + //dependencies for TokenEmvEngine + implementation 'com.github.walleth.kethereum:extensions_kotlin:0.81.4' } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/data/Blockchain.java b/app/src/main/java/com/tangem/data/Blockchain.java index ed1ab10387..402348982e 100644 --- a/app/src/main/java/com/tangem/data/Blockchain.java +++ b/app/src/main/java/com/tangem/data/Blockchain.java @@ -6,7 +6,7 @@ import com.tangem.tangem_sdk.R; * Created by dvol on 06.08.2017. */ public enum Blockchain { - Unknown("", "", 1.0, R.drawable.ic_logo_unknown, ""), + Unknown("", "", 1.0, R.drawable.ic_logo_unknown, "Unknown"), Bitcoin("BTC", "BTC", 100000000.0, R.drawable.ic_logo_bitcoin, "Bitcoin"), BitcoinTestNet("BTC/test", "BTC", 100000000.0, R.drawable.ic_logo_bitcoin_testnet, "Bitcoin Testnet"), BitcoinDual("BTC/dual", "BTC", 100000000.0, R.drawable.ic_logo_bitcoin, "Bitcoin"), @@ -32,7 +32,8 @@ public enum Blockchain { Eos("EOS", "EOS", 10000.0, R.drawable.tangem2, "EOS"), Ducatus("DUC", "DUC", 100000000.0, R.drawable.tangem2, "Ducatus"), Tezos("TEZOS", "XTZ", 10000000.0, R.drawable.ic_logo_tezos, "Tezos"), - FlowDemo("FLOW/demo", "", 1.0, R.drawable.tangem2, "Flow demo"); + FlowDemo("FLOW/demo", "", 1.0, R.drawable.tangem2, "Flow demo"), + TokenEmv("TTW", "ETH", 1.0, R.drawable.ic_logo_ethereum, "Ethereum"); Blockchain(String ID, String currency, double multiplier, int imageResource, String officialName) { mID = ID; @@ -67,7 +68,7 @@ public enum Blockchain { for (Blockchain blockchain : values()) { if (blockchain.getID().equals(id)) return blockchain; } - return null; + return Blockchain.Unknown; } public static Blockchain fromCurrency(String currency) { diff --git a/app/src/main/java/com/tangem/data/network/Server.java b/app/src/main/java/com/tangem/data/network/Server.java index c31e4cf46b..8ef83ebd52 100644 --- a/app/src/main/java/com/tangem/data/network/Server.java +++ b/app/src/main/java/com/tangem/data/network/Server.java @@ -40,6 +40,14 @@ public class Server { } } + public static class ApiInfuraRopsten { + public static final String URL_INFURA_ROPSTEN = ServerURL.API_INFURA_ROPSTEN; + + public static class Method { + public static final String MAIN = "v3/613a0b14833145968b1f656240c7d245"; + } + } + public static class ApiSoChain { public static final String URL = ServerURL.API_SOCHAIN_V2; diff --git a/app/src/main/java/com/tangem/data/network/ServerApiInfura.java b/app/src/main/java/com/tangem/data/network/ServerApiInfura.java index e168293fa4..29ded17af1 100644 --- a/app/src/main/java/com/tangem/data/network/ServerApiInfura.java +++ b/app/src/main/java/com/tangem/data/network/ServerApiInfura.java @@ -42,6 +42,8 @@ public class ServerApiInfura { public ServerApiInfura(Blockchain blockchain) { if (blockchain == Blockchain.EthereumTestNet) { infuraApi = App.Companion.getNetworkComponent().getRetrofitInfuraTestnet().create(InfuraApi.class); + } else if (blockchain == Blockchain.TokenEmv) { + infuraApi = App.Companion.getNetworkComponent().getRetrofitInfuraRopsten().create(InfuraApi.class); } } diff --git a/app/src/main/java/com/tangem/data/network/ServerApiTokenEmv.kt b/app/src/main/java/com/tangem/data/network/ServerApiTokenEmv.kt new file mode 100644 index 0000000000..d3fdba5f87 --- /dev/null +++ b/app/src/main/java/com/tangem/data/network/ServerApiTokenEmv.kt @@ -0,0 +1,53 @@ +package com.tangem.data.network + +import com.jakewharton.retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory +import com.tangem.data.network.model.TokenEmvGetTransferFeeAnswer +import com.tangem.data.network.model.TokenEmvGetTransferFeeBody +import com.tangem.data.network.model.TokenEmvTransferAnswer +import com.tangem.data.network.model.TokenEmvTransferBody +import com.tangem.tangem_card.util.Log +import io.reactivex.SingleObserver +import io.reactivex.android.schedulers.AndroidSchedulers +import io.reactivex.schedulers.Schedulers +import okhttp3.OkHttpClient +import okhttp3.logging.HttpLoggingInterceptor +import retrofit2.Retrofit +import retrofit2.converter.gson.GsonConverterFactory +import retrofit2.converter.scalars.ScalarsConverterFactory +import java.util.concurrent.TimeUnit + +class ServerApiTokenEmv { + private val TAG = ServerApiTokenEmv::class.java.simpleName + + private val tangemServer = "https://emvsupport.appspot.com/" + + private val tokenEmvApi = Retrofit.Builder() + .baseUrl(tangemServer) + .addConverterFactory(GsonConverterFactory.create()) + .addConverterFactory(ScalarsConverterFactory.create()) + .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) //logging for testing + .client(OkHttpClient.Builder().addInterceptor( + HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY) + ).build()) + .build() + .create(TokenEmvApi::class.java) + + fun transfer(tokenEmvTransferBody: TokenEmvTransferBody, transferObserver: SingleObserver) { + Log.i(TAG, "new transfer request") + + tokenEmvApi.transfer(tokenEmvTransferBody) + .timeout(30, TimeUnit.SECONDS) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(transferObserver) + } + + fun getTransferFee(tokenEmvGetTransferFeeBody: TokenEmvGetTransferFeeBody, transferObserver: SingleObserver) { + Log.i(TAG, "new get transfer fee request") + + tokenEmvApi.getTransferFee(tokenEmvGetTransferFeeBody) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(transferObserver) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/data/network/ServerURL.java b/app/src/main/java/com/tangem/data/network/ServerURL.java index b30fc802ce..dedda6858d 100644 --- a/app/src/main/java/com/tangem/data/network/ServerURL.java +++ b/app/src/main/java/com/tangem/data/network/ServerURL.java @@ -5,6 +5,7 @@ class ServerURL { static final String API_COINMARKETCAP = "https://pro-api.coinmarketcap.com/"; static final String API_INFURA = "https://mainnet.infura.io/"; static final String API_INFURA_TESTNET = "https://rinkeby.infura.io/"; + static final String API_INFURA_ROPSTEN = "https://ropsten.infura.io/"; static final String API_SOCHAIN_V2 = "https://chain.so/"; static final String API_ESTIMATEFEE = "https://estimatefee.com/"; static final String API_UPDATE_VERSION = "https://raw.githubusercontent.com/"; diff --git a/app/src/main/java/com/tangem/data/network/TokenEmvApi.kt b/app/src/main/java/com/tangem/data/network/TokenEmvApi.kt new file mode 100644 index 0000000000..43e09897c0 --- /dev/null +++ b/app/src/main/java/com/tangem/data/network/TokenEmvApi.kt @@ -0,0 +1,17 @@ +package com.tangem.data.network + +import com.tangem.data.network.model.TokenEmvGetTransferFeeAnswer +import com.tangem.data.network.model.TokenEmvGetTransferFeeBody +import com.tangem.data.network.model.TokenEmvTransferAnswer +import com.tangem.data.network.model.TokenEmvTransferBody +import io.reactivex.Completable +import io.reactivex.Single +import retrofit2.http.Body +import retrofit2.http.POST + +interface TokenEmvApi { + @POST("./card/transfer") + fun transfer(@Body tokenEmvTransferBody: TokenEmvTransferBody): Single + @POST("./card/transfer/fee") + fun getTransferFee(@Body tokenEmvGetTransferFeeBody: TokenEmvGetTransferFeeBody): Single +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/data/network/model/TokenEmvBody.kt b/app/src/main/java/com/tangem/data/network/model/TokenEmvBody.kt new file mode 100644 index 0000000000..ea89dd88d9 --- /dev/null +++ b/app/src/main/java/com/tangem/data/network/model/TokenEmvBody.kt @@ -0,0 +1,36 @@ +package com.tangem.data.network.model + +import com.google.gson.annotations.SerializedName + +data class TokenEmvTransferBody( + val CID: String, + val publicKey: String, + val amount: String, + val currency: String, + val recipient: String, + @SerializedName("fee_limit") + val feeLimit: String, + val sequence: Int, + val signature: String +) + +data class TokenEmvTransferAnswer( + val error: String?, + val errorCode: Int?, + val success: Boolean?, + val tx_id: String?, + val blockchain_tx_id: String? +) + +data class TokenEmvGetTransferFeeBody( + val CID: String, + val publicKey: String +) + +data class TokenEmvGetTransferFeeAnswer( + val error: String?, + val errorCode: Int?, + val success: Boolean?, + val fee: String?, + val currency: String? +) \ No newline at end of file diff --git a/app/src/main/java/com/tangem/di/NetworkComponent.kt b/app/src/main/java/com/tangem/di/NetworkComponent.kt index b09c235aba..3d100a99e6 100644 --- a/app/src/main/java/com/tangem/di/NetworkComponent.kt +++ b/app/src/main/java/com/tangem/di/NetworkComponent.kt @@ -17,6 +17,9 @@ interface NetworkComponent { @get:Named(Server.ApiInfuraTestnet.URL_INFURA_TESTNET) val retrofitInfuraTestnet: Retrofit + @get:Named(Server.ApiInfuraRopsten.URL_INFURA_ROPSTEN) + val retrofitInfuraRopsten: Retrofit + @get:Named(Server.ApiMaticTesnet.URL_MATIC_TESTNET) val retrofitMaticTesnet: Retrofit diff --git a/app/src/main/java/com/tangem/di/NetworkModule.kt b/app/src/main/java/com/tangem/di/NetworkModule.kt index 0358c3bd9b..339a0ca5d8 100644 --- a/app/src/main/java/com/tangem/di/NetworkModule.kt +++ b/app/src/main/java/com/tangem/di/NetworkModule.kt @@ -44,6 +44,18 @@ internal class NetworkModule { return builder.build() } + @Singleton + @Provides + @Named(Server.ApiInfuraRopsten.URL_INFURA_ROPSTEN) + fun provideRetrofitInfuraRopsten(): Retrofit { + val builder = Retrofit.Builder() + .baseUrl(Server.ApiInfuraRopsten.URL_INFURA_ROPSTEN) + .addConverterFactory(GsonConverterFactory.create()) + if (BuildConfig.DEBUG) + builder.client(createOkHttpClient()) + return builder.build() + } + @Singleton @Provides @Named(Server.ApiRootstock.URL_ROOTSTOCK) diff --git a/app/src/main/java/com/tangem/ui/activity/MainActivity.kt b/app/src/main/java/com/tangem/ui/activity/MainActivity.kt index c5bdb69201..8c6f685715 100644 --- a/app/src/main/java/com/tangem/ui/activity/MainActivity.kt +++ b/app/src/main/java/com/tangem/ui/activity/MainActivity.kt @@ -8,6 +8,7 @@ import android.nfc.Tag import android.os.Bundle import android.util.Log import androidx.appcompat.app.AppCompatActivity +import androidx.fragment.app.Fragment import androidx.lifecycle.ViewModelProviders import androidx.navigation.findNavController import com.scottyab.rootbeer.RootBeer @@ -16,6 +17,7 @@ import com.tangem.di.ToastHelper import com.tangem.tangem_sdk.android.nfc.NfcLifecycleObserver import com.tangem.tangem_sdk.android.reader.NfcManager import com.tangem.ui.dialog.RootFoundDialog +import com.tangem.ui.fragment.MainFragment import com.tangem.wallet.BuildConfig import com.tangem.wallet.R import javax.inject.Inject @@ -44,6 +46,8 @@ class MainActivity : AppCompatActivity(), NfcAdapter.ReaderCallback { } private fun navigateSafelyToMainFragment() { + if (getActiveFragment() is MainFragment) return + try { findNavController(R.id.nav_host_fragment).popBackStack() } catch (e: IllegalArgumentException) { @@ -73,8 +77,7 @@ class MainActivity : AppCompatActivity(), NfcAdapter.ReaderCallback { } override fun onTagDiscovered(tag: Tag) { - val activeFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment) - ?.childFragmentManager?.primaryNavigationFragment + val activeFragment = getActiveFragment() if (activeFragment is NfcAdapter.ReaderCallback) { activeFragment.onTagDiscovered(tag) } else { @@ -82,4 +85,8 @@ class MainActivity : AppCompatActivity(), NfcAdapter.ReaderCallback { } } + private fun getActiveFragment(): Fragment? { + return supportFragmentManager.findFragmentById(R.id.nav_host_fragment) + ?.childFragmentManager?.primaryNavigationFragment + } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/ui/dialog/WaitSecurityDelayDialog.java b/app/src/main/java/com/tangem/ui/dialog/WaitSecurityDelayDialog.java index f1f542e8ae..6e428897c4 100644 --- a/app/src/main/java/com/tangem/ui/dialog/WaitSecurityDelayDialog.java +++ b/app/src/main/java/com/tangem/ui/dialog/WaitSecurityDelayDialog.java @@ -147,21 +147,23 @@ public class WaitSecurityDelayDialog extends DialogFragment { } private void setRemainingTimeout(final int msec) { - progressBar.post(() -> { - int progress = WaitSecurityDelayDialog.this.progressBar.getProgress(); - if (timer != null) { - // we get delay latency from card for first time - don't change progress by timer, only by card answer - progressBar.setMax(progress + msec); - timer.cancel(); - timer = null; - } else { - int newProgress = progressBar.getMax() - msec; - if (newProgress > progress) - progressBar.setProgress(newProgress); - else + if (progressBar != null) { + progressBar.post(() -> { + int progress = WaitSecurityDelayDialog.this.progressBar.getProgress(); + if (timer != null) { + // we get delay latency from card for first time - don't change progress by timer, only by card answer progressBar.setMax(progress + msec); - } - }); + timer.cancel(); + timer = null; + } else { + int newProgress = progressBar.getMax() - msec; + if (newProgress > progress) + progressBar.setProgress(newProgress); + else + progressBar.setMax(progress + msec); + } + }); + } } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/wallet/CoinEngineFactory.kt b/app/src/main/java/com/tangem/wallet/CoinEngineFactory.kt index 310f567aea..8bfbb7f361 100644 --- a/app/src/main/java/com/tangem/wallet/CoinEngineFactory.kt +++ b/app/src/main/java/com/tangem/wallet/CoinEngineFactory.kt @@ -20,6 +20,7 @@ import com.tangem.wallet.rsk.RskEngine import com.tangem.wallet.rsk.RskTokenEngine import com.tangem.wallet.token.TokenEngine import com.tangem.wallet.tezos.TezosEngine +import com.tangem.wallet.tokenEmv.TokenEmvEngine import com.tangem.wallet.xlm.XlmAssetEngine import com.tangem.wallet.xlm.XlmEngine import com.tangem.wallet.xlmTag.XlmTagEngine @@ -60,6 +61,7 @@ object CoinEngineFactory { Blockchain.Tezos -> TezosEngine() Blockchain.BitcoinDual -> BtcMultisigEngine() Blockchain.FlowDemo -> FlowDemoEngine() + Blockchain.TokenEmv -> TokenEmvEngine() else -> null } } @@ -109,6 +111,8 @@ object CoinEngineFactory { BtcMultisigEngine(context) else if (Blockchain.FlowDemo == context.blockchain) FlowDemoEngine(context) + else if (Blockchain.TokenEmv == context.blockchain) + TokenEmvEngine(context) else return null } catch (e: Exception) { diff --git a/app/src/main/java/com/tangem/wallet/EthTransaction.java b/app/src/main/java/com/tangem/wallet/EthTransaction.java index c7ba6e98b3..48e86ecf6a 100644 --- a/app/src/main/java/com/tangem/wallet/EthTransaction.java +++ b/app/src/main/java/com/tangem/wallet/EthTransaction.java @@ -110,7 +110,7 @@ public class EthTransaction { return kec.digest(plainMsg); } - public int BruteRecoveryID2(ECDSASignatureETH sig, byte[] messageHash, byte[] thisKey) { + public static int BruteRecoveryID2(ECDSASignatureETH sig, byte[] messageHash, byte[] thisKey) { Log.e("ETH_KZ", BTCUtils.toHex(thisKey)); int recId = -1; for (int i = 0; i < 4; i++) { diff --git a/app/src/main/java/com/tangem/wallet/token/TokenData.java b/app/src/main/java/com/tangem/wallet/token/TokenData.java index ef67680617..77e0c7ac42 100644 --- a/app/src/main/java/com/tangem/wallet/token/TokenData.java +++ b/app/src/main/java/com/tangem/wallet/token/TokenData.java @@ -8,31 +8,46 @@ import com.tangem.wallet.eth.EthData; public class TokenData extends EthData { private CoinEngine.InternalAmount balanceAlter = null; - +// private Integer sequence = null; @Override public void clearInfo() { super.clearInfo(); balanceAlter = null; +// sequence = null; } public CoinEngine.InternalAmount getBalanceAlterInInternalUnits() { return balanceAlter; } + public void setBalanceAlterInInternalUnits(CoinEngine.InternalAmount value) { balanceAlter = value; } +// public Integer getSequence() { +// return sequence; +// } +// +// public void setSequence(Integer sequence) { +// this.sequence = sequence; +// } + @Override public void loadFromBundle(Bundle B) { super.loadFromBundle(B); - if( B.containsKey("BalanceDecimalAlter" )) { + if (B.containsKey("BalanceDecimalAlter")) { balanceAlter = new CoinEngine.InternalAmount(B.getString("BalanceDecimalAlter"), "wei"); - }else{ - balanceAlter=null; + } else { + balanceAlter = null; } +// if (B.containsKey("Sequence")) { +// sequence = B.getInt("Sequence"); +// } else { +// sequence = null; +// } } @Override @@ -40,9 +55,12 @@ public class TokenData extends EthData { super.saveToBundle(B); try { - if( balanceAlter!=null ) { + if (balanceAlter != null) { B.putString("BalanceDecimalAlter", balanceAlter.toString()); } +// if (sequence != null) { +// B.putInt("Sequence", sequence); +// } } catch (Exception e) { Log.e("Can't save to bundle ", e.getMessage()); } diff --git a/app/src/main/java/com/tangem/wallet/tokenEmv/TokenEmvEngine.kt b/app/src/main/java/com/tangem/wallet/tokenEmv/TokenEmvEngine.kt new file mode 100644 index 0000000000..c63851d57c --- /dev/null +++ b/app/src/main/java/com/tangem/wallet/tokenEmv/TokenEmvEngine.kt @@ -0,0 +1,349 @@ +package com.tangem.wallet.tokenEmv + +import android.net.Uri +import android.text.InputFilter +import android.util.Log +import com.google.gson.Gson +import com.tangem.data.Blockchain +import com.tangem.data.network.ServerApiInfura +import com.tangem.data.network.ServerApiTokenEmv +import com.tangem.data.network.model.* +import com.tangem.tangem_card.data.TangemCard +import com.tangem.tangem_card.reader.CardProtocol.TangemException +import com.tangem.tangem_card.reader.TLV +import com.tangem.tangem_card.reader.TLVList +import com.tangem.tangem_card.tasks.SignTask +import com.tangem.tangem_card.util.Util +import com.tangem.util.DecimalDigitsInputFilter +import com.tangem.wallet.EthTransaction +import com.tangem.wallet.Keccak256 +import com.tangem.wallet.R +import com.tangem.wallet.TangemContext +import com.tangem.wallet.token.TokenEngine +import io.reactivex.observers.DisposableSingleObserver +import org.apache.commons.lang3.SerializationUtils +import org.kethereum.extensions.toBytesPadded +import java.math.BigInteger + +class TokenEmvEngine : TokenEngine { + constructor() : super() + constructor(context: TangemContext) : super(context) + + private val TAG = TokenEmvEngine::class.java.simpleName + + private fun hasLinkedContract(): Boolean { + return ctx.card.issuerData != null && ctx.card.issuerData.size == 44 + } + + override fun getBlockchain(): Blockchain { + return Blockchain.TokenEmv + } + + override fun getChainIdNum(): Int { + return EthTransaction.ChainEnum.Ropsten.value + } + + override fun getBalance(): Amount? { + return if (!hasBalanceInfo()) { + null + } else { + convertToAmount(coinData.balanceInInternalUnits) + } + } + + override fun getBalanceHTML(): String? { + return if (hasLinkedContract()) { + if (balance != null) { + balance!!.toDescriptionString(tokenDecimals) + } else { + "" + } + } else { + "NO LINKED CONTRACT" + } + } + + override fun getBalanceCurrency(): String? { + return ctx.card.getTokenSymbol() + } + + override fun getAmountInputFilters(): Array? { + return arrayOf(DecimalDigitsInputFilter(tokenDecimals)) + } + + override fun getFeeCurrency(): String? { + return balanceCurrency + } + + override fun isBalanceNotZero(): Boolean { + if (coinData == null) return false + return if (coinData.balanceInInternalUnits == null) { + false + } else { + coinData.balanceInInternalUnits.notZero() + } + } + + override fun getBalanceEquivalent(): String? { + return "" + } + + override fun evaluateFeeEquivalent(fee: String?): String? { + return "" + } + + override fun defineWallet() { + try { + if (hasLinkedContract()) { + ctx.coinData.wallet = TLVList.fromBytes(ctx.card.issuerData).getTLV(TLV.Tag.TAG_Token_Contract_Address).asString + } else { + ctx.coinData.wallet = calculateAddress(ctx.card.walletPublicKey) + } + } catch (e: Exception) { + ctx.coinData.wallet = "ERROR" + throw TangemException("Can't define wallet address") + } + } + + override fun hasBalanceInfo(): Boolean { + return coinData.balanceInInternalUnits != null + } + + override fun getWalletExplorerUri(): Uri { + return Uri.parse("https://ropsten.etherscan.io/token/" + getContractAddress(ctx.card) + "?a=" + ctx.coinData.wallet) + } + + override fun isExtractPossible(): Boolean { + if (!hasBalanceInfo()) { + ctx.setMessage(R.string.loaded_wallet_error_obtaining_blockchain_data) + } else if (!isBalanceNotZero) { + ctx.setMessage(R.string.general_wallet_empty) + } else if (awaitingConfirmation()) { + ctx.setMessage(R.string.loaded_wallet_message_wait) + } else { + return true + } + return false + } + + override fun checkNewTransactionAmountAndFee(amount: Amount, fee: Amount?, isFeeIncluded: Boolean): Boolean { + try { + if (isFeeIncluded && (amount > balance || amount < fee)) return false + if (!isFeeIncluded && amount.add(fee) > balance) return false + } catch (e: NumberFormatException) { + e.printStackTrace() + } + return true + } + + override fun constructTransaction(amountValue: Amount, feeValue: Amount?, IncFee: Boolean, targetAddress: String?): SignTask.TransactionToSign? { + val functionBytes = "transfer".toByteArray() + + val contractHex = coinData.wallet.substring(2) + val contractBytes = Util.hexToBytes(contractHex) + val recipientHex = targetAddress!!.substring(2) + val recipientBytes = Util.hexToBytes(recipientHex) + + val amountBytes = convertToInternalAmount(amountValue).toBigInteger().toBytesPadded(32) + val feeLimitBytes = convertToInternalAmount(feeValue).toBigInteger().toBytesPadded(32) + + val sequence = ctx.card.SignedHashes + val sequenceBytes = sequence.toBigInteger().toBytesPadded(4) + + val hashToSign = Keccak256().digest(contractBytes + functionBytes + amountBytes + recipientBytes + feeLimitBytes + sequenceBytes) + + return object : SignTask.TransactionToSign { + override fun isSigningMethodSupported(signingMethod: TangemCard.SigningMethod): Boolean { + return signingMethod == TangemCard.SigningMethod.Sign_Hash + } + + override fun getHashesToSign(): Array { + return arrayOf(hashToSign) + } + + @Throws(java.lang.Exception::class) + override fun getRawDataToSign(): ByteArray { + throw java.lang.Exception("Signing of raw transaction not supported for " + this.javaClass.simpleName) + } + + @Throws(java.lang.Exception::class) + override fun getHashAlgToSign(): String { + throw java.lang.Exception("Signing of raw transaction not supported for " + this.javaClass.simpleName) + } + + @Throws(java.lang.Exception::class) + override fun getIssuerTransactionSignature(dataToSignByIssuer: ByteArray): ByteArray { + throw java.lang.Exception("Transaction validation by issuer not supported in this version") + } + + @Throws(java.lang.Exception::class) + override fun onSignCompleted(signFromCard: ByteArray): ByteArray { +// val r = BigInteger(1, Arrays.copyOfRange(signFromCard, 0, 32)) +// var s: BigInteger? = BigInteger(1, Arrays.copyOfRange(signFromCard, 32, 64)) +// s = CryptoUtil.toCanonicalised(s) +// +// val publicKey = ctx.getCard().getWalletPublicKey() +// +// val verified = ECKey.verify(hashToSign, ECKey.ECDSASignature(r, s), publicKey) +// if (!verified) { +// Log.e(this.javaClass.simpleName + "-CHECK", "sign Failed.") +// } +// +// val v = BruteRecoveryID2(ECDSASignatureETH(r, s), hashToSign, publicKey) +// if (v != 27 && v != 28) { +// Log.e(TAG, "invalid v") +// throw java.lang.Exception("Error in " + this.javaClass.simpleName + " - invalid v") +// } +// Log.e(TAG, this.javaClass.simpleName + " V: " + v.toString()) +// +// var rBytes = r.toByteArray() +// if (rBytes.size == 33) { +// rBytes = rBytes.copyOfRange(1,33) +// } +// val sBytes = s.toByteArray() +// +// val tokenEmvTransferBody = TokenEmvTransferBody( +// CID = Util.bytesToHex(ctx.card.cid), +// publicKey = Util.bytesToHex(ctx.card.walletPublicKey), +// contract = contractHex, +// amount = Util.byteArrayToHexString(amountBytes), +// recipient = recipientHex, +// feeLimit = Util.byteArrayToHexString(feeLimitBytes), +// sequence = sequence, +// r = Util.byteArrayToHexString(rBytes), +// s = Util.byteArrayToHexString(sBytes), +// v = v +// ) + + val tokenEmvTransferBody = TokenEmvTransferBody( + CID = Util.bytesToHex(ctx.card.cid), + publicKey = Util.bytesToHex(ctx.card.walletPublicKey), + amount = amountValue.toValueString(), + currency = amountValue.currency, + recipient = targetAddress, + feeLimit = feeValue!!.toValueString(), + sequence = sequence, + signature = Util.bytesToHex(signFromCard) + ) + + val jsonBody = Gson().toJson(tokenEmvTransferBody) + val serializedBody = SerializationUtils.serialize(jsonBody) + + notifyOnNeedSendTransaction(serializedBody) + return serializedBody + } + } + } + + override fun requestBalanceAndUnspentTransactions(blockchainRequestsCallbacks: BlockchainRequestsCallbacks) { + val serverApiInfura = ServerApiInfura(ctx.blockchain) + + val responseListener: ServerApiInfura.ResponseListener = object : ServerApiInfura.ResponseListener { + override fun onSuccess(method: String, infuraResponse: InfuraResponse) { + try { + var balanceCap = infuraResponse.result + balanceCap = balanceCap!!.substring(2) + val l = BigInteger(balanceCap, 16) + coinData.balanceInInternalUnits = InternalAmount(l, ctx.card.tokenSymbol) + coinData.isBalanceReceived = true + // Log.i("$TAG eth_call", balanceCap) + } catch (e: java.lang.Exception) { + onFail(method, e.message ?: "invalid response") + } + blockchainRequestsCallbacks.onComplete(true) + } + + override fun onFail(method: String, message: String) { + Log.e(TAG, "onFail: $method $message") + ctx.error = message + blockchainRequestsCallbacks.onComplete(false) + } + } + + serverApiInfura.setResponseListener(responseListener) + + if (validateAddress(getContractAddress(ctx.card))) { + serverApiInfura.requestData(ServerApiInfura.INFURA_ETH_CALL, 67, coinData.wallet, getContractAddress(ctx.card), "") + } else { + ctx.error = "Smart contract address not defined" + blockchainRequestsCallbacks.onComplete(false) + } + } + + override fun requestFee(blockchainRequestsCallbacks: BlockchainRequestsCallbacks, targetAddress: String?, amount: Amount) { + + val tokenEmvTransferBody = TokenEmvGetTransferFeeBody( + CID = Util.bytesToHex(ctx.card.cid), + publicKey = Util.bytesToHex(ctx.card.walletPublicKey) + ) + + val observer = object : DisposableSingleObserver() { + override fun onError(e: Throwable) { + ctx.error = "Can't get transfer fee, ${e.message}" + blockchainRequestsCallbacks.onComplete(false) + } + + override fun onSuccess(t: TokenEmvGetTransferFeeAnswer) { + if (t.success != null && t.success) { + ctx.error = null + coinData.minFee = Amount(t.fee, t.currency) + coinData.normalFee = Amount(t.fee, t.currency) + coinData.maxFee = Amount(t.fee, t.currency) + blockchainRequestsCallbacks.onComplete(true) + } else { + if (t.error != null) { + ctx.error = t.error + } else { + ctx.error = "Can't get transfer fee, code ${t.errorCode}" + } + blockchainRequestsCallbacks.onComplete(false) + } + } + + } + + ServerApiTokenEmv().getTransferFee(tokenEmvTransferBody, observer) + blockchainRequestsCallbacks.onComplete(true) + } + + override fun requestSendTransaction(blockchainRequestsCallbacks: BlockchainRequestsCallbacks, txForSend: ByteArray?) { + val jsonBody = SerializationUtils.deserialize(txForSend) + + Log.e(TAG, jsonBody) + + val tokenEmvTransferBody = Gson().fromJson(jsonBody, TokenEmvTransferBody::class.java) + + val observer = object : DisposableSingleObserver() { + override fun onError(e: Throwable) { + ctx.error = "Can't send transfer, ${e.message}" + blockchainRequestsCallbacks.onComplete(false) + } + + override fun onSuccess(t: TokenEmvTransferAnswer) { + if (t.success != null && t.success) { + ctx.error = null + blockchainRequestsCallbacks.onComplete(true) + } else { + if (t.error != null) { + ctx.error = t.error + } else { + ctx.error = "Can't send transfer, code ${t.errorCode}" + } + blockchainRequestsCallbacks.onComplete(false) + } + + } + } + + ServerApiTokenEmv().transfer(tokenEmvTransferBody, observer) + } + + override fun allowSelectFeeLevel(): Boolean { + return false + } + + override fun pendingTransactionTimeoutInSeconds(): Int { + return 60 + } + +} \ No newline at end of file diff --git a/tangem-core/src/main/java/com/tangem/CardSession.kt b/tangem-core/src/main/java/com/tangem/CardSession.kt index 9c338ca586..450c056997 100644 --- a/tangem-core/src/main/java/com/tangem/CardSession.kt +++ b/tangem-core/src/main/java/com/tangem/CardSession.kt @@ -74,7 +74,10 @@ class CardSession( } runnable.run(this) { result -> - stop() + when (result) { + is CompletionResult.Success -> stop() + is CompletionResult.Failure -> stopWithError(result.error) + } callback(result) } } @@ -99,7 +102,6 @@ class CardSession( } is CompletionResult.Success -> { callback(this, null) - } } } diff --git a/tangem-core/src/main/java/com/tangem/commands/Command.kt b/tangem-core/src/main/java/com/tangem/commands/Command.kt index f78e5663cb..225fa97430 100644 --- a/tangem-core/src/main/java/com/tangem/commands/Command.kt +++ b/tangem-core/src/main/java/com/tangem/commands/Command.kt @@ -91,7 +91,7 @@ abstract class Command : CardSessionRunnable { } } is CompletionResult.Failure -> - if (result.error == SessionError.TagLost()) { + if (result.error is SessionError.TagLost) { session.viewDelegate.onTagLost() } else { callback(CompletionResult.Failure(result.error)) diff --git a/tangem-core/src/main/java/com/tangem/commands/ReadCommand.kt b/tangem-core/src/main/java/com/tangem/commands/ReadCommand.kt index 09c188072d..e3b5d4ccee 100644 --- a/tangem-core/src/main/java/com/tangem/commands/ReadCommand.kt +++ b/tangem-core/src/main/java/com/tangem/commands/ReadCommand.kt @@ -14,67 +14,50 @@ import java.util.* /** * Determines which type of data is required for signing. */ -data class SigningMethod(val rawValue: Int) { +data class SigningMethodMask(val rawValue: Int) { - fun contains(value: Int): Boolean { + fun contains(signingMethod: SigningMethod): Boolean { return if (rawValue and 0x80 == 0) { - value == rawValue + signingMethod.code == rawValue } else { - rawValue and (0x01 shl value) != 0 + rawValue and (0x01 shl signingMethod.code) != 0 } } +} - companion object { - const val signHash = 0 - const val signRaw = 1 - const val signHashValidatedByIssuer = 2 - const val signRawValidatedByIssuer = 3 - const val signHashValidatedByIssuerAndWriteIssuerData = 4 - const val signRawValidatedByIssuerAndWriteIssuerData = 5 - const val signPos = 6 +enum class SigningMethod(val code: Int) { + SignHash(0), + SignRaw(1), + SignHashValidateByIssuer(2), + SignRawValidateByIssuer(3), + SignHashValidateByIssuerWriteIssuerData(4), + SignRawValidateByIssuerWriteIssuerData(5), + SignPos(6) +} - fun build( - signHash: Boolean = false, - signRaw: Boolean = false, - signHashValidatedByIssuer: Boolean = false, - signRawValidatedByIssuer: Boolean = false, - signHashValidatedByIssuerAndWriteIssuerData: Boolean = false, - signRawValidatedByIssuerAndWriteIssuerData: Boolean = false, - signPos: Boolean = false +class SigningMethodMaskBuilder() { - ): SigningMethod { - fun Boolean.toInt() = if (this) 1 else 0 + private val signingMethods = mutableSetOf() - val signingMethodsCount = 0 + - signHash.toInt() + - signRaw.toInt() + - signHashValidatedByIssuer.toInt() + - signRawValidatedByIssuer.toInt() + - signHashValidatedByIssuerAndWriteIssuerData.toInt() + - signRawValidatedByIssuerAndWriteIssuerData.toInt() + - signPos.toInt() + fun add(signingMethod: SigningMethod) { + signingMethods.add(signingMethod) + } - var signingMethod: Int = 0 - if (signingMethodsCount == 1) { - if (signHash) signingMethod += SigningMethod.signHash - if (signRaw) signingMethod += SigningMethod.signRaw - if (signHashValidatedByIssuer) signingMethod += SigningMethod.signHashValidatedByIssuer - if (signRawValidatedByIssuer) signingMethod += SigningMethod.signRawValidatedByIssuer - if (signHashValidatedByIssuerAndWriteIssuerData) signingMethod += SigningMethod.signHashValidatedByIssuerAndWriteIssuerData - if (signRawValidatedByIssuerAndWriteIssuerData) signingMethod += SigningMethod.signRawValidatedByIssuerAndWriteIssuerData - if (signPos) signingMethod += SigningMethod.signPos - } else if (signingMethodsCount > 1) { - signingMethod = 0x80 - if (signHash) signingMethod += 0x01 - if (signRaw) signingMethod += 0x01 shl SigningMethod.signRaw - if (signHashValidatedByIssuer) signingMethod += 0x01 shl SigningMethod.signHashValidatedByIssuer - if (signRawValidatedByIssuer) signingMethod += 0x01 shl SigningMethod.signRawValidatedByIssuer - if (signHashValidatedByIssuerAndWriteIssuerData) signingMethod += 0x01 shl SigningMethod.signHashValidatedByIssuerAndWriteIssuerData - if (signRawValidatedByIssuerAndWriteIssuerData) signingMethod += 0x01 shl SigningMethod.signRawValidatedByIssuerAndWriteIssuerData - if (signPos) signingMethod += 0x01 shl SigningMethod.signPos + fun build(): SigningMethodMask { + val rawValue: Int = when { + signingMethods.count() == 0 -> { + 0 + } + signingMethods.count() == 1 -> { + signingMethods.iterator().next().code + } + else -> { + signingMethods.fold( + 0x80, { acc, singingMethod -> acc + (0x01 shl singingMethod.code) } + ) } - return SigningMethod(signingMethod) } + return SigningMethodMask(rawValue) } } @@ -113,22 +96,23 @@ enum class CardStatus(val code: Int) { */ data class ProductMask(val rawValue: Int) { - fun contains(value: Int): Boolean = (rawValue and value) != 0 + fun contains(product: Product): Boolean = (rawValue and product.code) != 0 - companion object { - const val note = 0x01 - const val tag = 0x02 - const val idCard = 0x04 - const val idIssuer = 0x08 - } +} + +enum class Product(val code: Int) { + Note(0x01), + Tag(0x02), + IdCard(0x04), + IdIssuer(0x08) } class ProductMaskBuilder() { private var productMaskValue = 0 - fun add(productCode: Int) { - productMaskValue = productMaskValue or productCode + fun add(product: Product) { + productMaskValue = productMaskValue or product.code } fun build() = ProductMask(productMaskValue) @@ -299,7 +283,7 @@ class Card( /** * Defines what data should be submitted to SIGN command. */ - val signingMethod: SigningMethod?, + val signingMethods: SigningMethodMask?, /** * Delay in seconds before COS executes commands protected by PIN2. @@ -415,7 +399,7 @@ class ReadCommand : Command() { issuerPublicKey = decoder.decodeOptional(TlvTag.IssuerDataPublicKey), curve = decoder.decodeOptional(TlvTag.CurveId), maxSignatures = decoder.decodeOptional(TlvTag.MaxSignatures), - signingMethod = decoder.decodeOptional(TlvTag.SigningMethod), + signingMethods = decoder.decodeOptional(TlvTag.SigningMethod), pauseBeforePin2 = decoder.decodeOptional(TlvTag.PauseBeforePin2), walletPublicKey = decoder.decodeOptional(TlvTag.WalletPublicKey), walletRemainingSignatures = decoder.decodeOptional(TlvTag.RemainingSignatures), diff --git a/tangem-core/src/main/java/com/tangem/commands/personalization/PersonalizeCommand.kt b/tangem-core/src/main/java/com/tangem/commands/personalization/PersonalizeCommand.kt index 24a118322a..e4fe1a8b15 100644 --- a/tangem-core/src/main/java/com/tangem/commands/personalization/PersonalizeCommand.kt +++ b/tangem-core/src/main/java/com/tangem/commands/personalization/PersonalizeCommand.kt @@ -60,7 +60,7 @@ class PersonalizeCommand( issuerPublicKey = decoder.decodeOptional(TlvTag.IssuerDataPublicKey), curve = decoder.decodeOptional(TlvTag.CurveId), maxSignatures = decoder.decodeOptional(TlvTag.MaxSignatures), - signingMethod = decoder.decodeOptional(TlvTag.SigningMethod), + signingMethods = decoder.decodeOptional(TlvTag.SigningMethod), pauseBeforePin2 = decoder.decodeOptional(TlvTag.PauseBeforePin2), walletPublicKey = decoder.decodeOptional(TlvTag.WalletPublicKey), walletRemainingSignatures = decoder.decodeOptional(TlvTag.RemainingSignatures), @@ -105,7 +105,7 @@ class PersonalizeCommand( tlvBuilder.append(TlvTag.CardId, cardId) tlvBuilder.append(TlvTag.CurveId, config.curveID) tlvBuilder.append(TlvTag.MaxSignatures, config.maxSignatures) - tlvBuilder.append(TlvTag.SigningMethod, config.signingMethod) + tlvBuilder.append(TlvTag.SigningMethod, config.signingMethods) tlvBuilder.append(TlvTag.SettingsMask, config.createSettingsMask()) tlvBuilder.append(TlvTag.PauseBeforePin2, config.pauseBeforePin2 / 10) tlvBuilder.append(TlvTag.Cvc, config.cvc.toByteArray()) diff --git a/tangem-core/src/main/java/com/tangem/commands/personalization/entities/CardConfig.kt b/tangem-core/src/main/java/com/tangem/commands/personalization/entities/CardConfig.kt index bc1a082237..2cfd173be1 100644 --- a/tangem-core/src/main/java/com/tangem/commands/personalization/entities/CardConfig.kt +++ b/tangem-core/src/main/java/com/tangem/commands/personalization/entities/CardConfig.kt @@ -2,7 +2,7 @@ package com.tangem.commands.personalization.entities import com.tangem.commands.CardData import com.tangem.commands.EllipticCurve -import com.tangem.commands.SigningMethod +import com.tangem.commands.SigningMethodMask data class NdefRecord( val type: Type, @@ -33,7 +33,7 @@ data class CardConfig( val pauseBeforePin2: Int, val smartSecurityDelay: Boolean, val curveID: EllipticCurve, - val signingMethod: SigningMethod, + val signingMethods: SigningMethodMask, val maxSignatures: Int, val isReusable: Boolean, val allowSwapPin: Boolean, diff --git a/tangem-core/src/main/java/com/tangem/common/tlv/TlvDecoder.kt b/tangem-core/src/main/java/com/tangem/common/tlv/TlvDecoder.kt index 48c14edec4..4954c38c46 100644 --- a/tangem-core/src/main/java/com/tangem/common/tlv/TlvDecoder.kt +++ b/tangem-core/src/main/java/com/tangem/common/tlv/TlvDecoder.kt @@ -122,9 +122,9 @@ class TlvDecoder(val tlvList: List) { } } TlvValueType.SigningMethod -> { - typeCheck(tag) + typeCheck(tag) try { - SigningMethod(tlvValue.toInt()) as T + SigningMethodMask(tlvValue.toInt()) as T } catch (exception: Exception) { logException(tag, tlvValue.toInt().toString(), exception) throw SessionError.DecodingFailed() diff --git a/tangem-core/src/main/java/com/tangem/common/tlv/TlvEncoder.kt b/tangem-core/src/main/java/com/tangem/common/tlv/TlvEncoder.kt index 1855badcab..057b0807e8 100644 --- a/tangem-core/src/main/java/com/tangem/common/tlv/TlvEncoder.kt +++ b/tangem-core/src/main/java/com/tangem/common/tlv/TlvEncoder.kt @@ -87,8 +87,8 @@ class TlvEncoder { (value as CardStatus).code.toByteArray() } TlvValueType.SigningMethod -> { - typeCheck(tag) - byteArrayOf((value as SigningMethod).rawValue.toByte()) + typeCheck(tag) + byteArrayOf((value as SigningMethodMask).rawValue.toByte()) } TlvValueType.IssuerDataMode -> { typeCheck(tag) diff --git a/tangem-core/src/main/java/com/tangem/tasks/ScanTask.kt b/tangem-core/src/main/java/com/tangem/tasks/ScanTask.kt index 207cb6f705..39dea59f0b 100644 --- a/tangem-core/src/main/java/com/tangem/tasks/ScanTask.kt +++ b/tangem-core/src/main/java/com/tangem/tasks/ScanTask.kt @@ -20,7 +20,7 @@ internal class ScanTask : CardSessionRunnable { if (card == null) { callback(CompletionResult.Failure(SessionError.MissingPreflightRead())) - } else if (card.cardData?.productMask?.contains(ProductMask.tag) != false) { + } else if (card.cardData?.productMask?.contains(Product.Tag) != false) { callback(CompletionResult.Success(card)) } else if (card.status != CardStatus.Loaded) { diff --git a/tangem-core/src/test/java/com/tangem/common/tlv/TlvDecoderTest.kt b/tangem-core/src/test/java/com/tangem/common/tlv/TlvDecoderTest.kt index f9915fc7b8..fbd4514c11 100644 --- a/tangem-core/src/test/java/com/tangem/common/tlv/TlvDecoderTest.kt +++ b/tangem-core/src/test/java/com/tangem/common/tlv/TlvDecoderTest.kt @@ -82,8 +82,8 @@ class TlvDecoderTest { @Test fun `map SigningMethods single value returns correct value`() { - val signingMethods: SigningMethod = tlvMapper.decode(TlvTag.SigningMethod) - assertThat(signingMethods.contains(SigningMethod.signHash)) + val signingMethods: SigningMethodMask = tlvMapper.decode(TlvTag.SigningMethod) + assertThat(signingMethods.contains(SigningMethod.SignHash)) .isTrue() } @@ -91,20 +91,20 @@ class TlvDecoderTest { fun `map SigningMethods set of methods returns correct value`() { val localMapper = TlvDecoder(Tlv.deserialize("070195".hexToBytes())!!) - val signingMethod: SigningMethod = localMapper.decode(TlvTag.SigningMethod) - assertThat(signingMethod.contains(SigningMethod.signHash)) + val signingMethods: SigningMethodMask = localMapper.decode(TlvTag.SigningMethod) + assertThat(signingMethods.contains(SigningMethod.SignHash)) .isTrue() - assertThat(signingMethod.contains(SigningMethod.signHashValidatedByIssuer)) + assertThat(signingMethods.contains(SigningMethod.SignHashValidateByIssuer)) .isTrue() - assertThat(signingMethod.contains(SigningMethod.signHashValidatedByIssuerAndWriteIssuerData)) + assertThat(signingMethods.contains(SigningMethod.SignHashValidateByIssuerWriteIssuerData)) .isTrue() - assertThat(signingMethod.contains(SigningMethod.signRaw)) + assertThat(signingMethods.contains(SigningMethod.SignRaw)) .isFalse() - assertThat(signingMethod.contains(SigningMethod.signRawValidatedByIssuer)) + assertThat(signingMethods.contains(SigningMethod.SignRawValidateByIssuer)) .isFalse() - assertThat(signingMethod.contains(SigningMethod.signRawValidatedByIssuerAndWriteIssuerData)) + assertThat(signingMethods.contains(SigningMethod.SignRawValidateByIssuerWriteIssuerData)) .isFalse() - assertThat(signingMethod.contains(SigningMethod.signPos)) + assertThat(signingMethods.contains(SigningMethod.SignPos)) .isFalse() } @@ -119,7 +119,7 @@ class TlvDecoderTest { fun `map ProductMask with raw value 5 returns correct value`() { val localMapper = TlvDecoder(listOf(Tlv(TlvTag.ProductMask, byteArrayOf(5)))) val productMask: ProductMask = localMapper.decode(TlvTag.ProductMask) - assertThat(productMask.contains(ProductMask.note) && productMask.contains(ProductMask.idCard)) + assertThat(productMask.contains(Product.Note) && productMask.contains(Product.IdCard)) .isTrue() } @@ -127,7 +127,7 @@ class TlvDecoderTest { fun `map ProductMask with raw value 1 returns correct value`() { val localMapper = TlvDecoder(listOf(Tlv(TlvTag.ProductMask, byteArrayOf(1)))) val productMask: ProductMask = localMapper.decode(TlvTag.ProductMask) - assertThat(productMask.contains(ProductMask.note)) + assertThat(productMask.contains(Product.Note)) .isTrue() } diff --git a/tangem-demo/build.gradle b/tangem-demo/build.gradle index fa9e64d58f..fd56546bed 100644 --- a/tangem-demo/build.gradle +++ b/tangem-demo/build.gradle @@ -46,10 +46,11 @@ dependencies { implementation "androidx.constraintlayout:constraintlayout:2.0.0-beta4" implementation "androidx.navigation:navigation-fragment-ktx:2.2.1" implementation "androidx.navigation:navigation-ui-ktx:2.2.1" - implementation "androidx.recyclerview:recyclerview:1.2.0-alpha01" + implementation "androidx.recyclerview:recyclerview:1.2.0-alpha02" + implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.0.0" implementation "com.google.android.material:material:1.2.0-alpha05" implementation "androidx.viewpager2:viewpager2:1.0.0" implementation 'com.google.code.gson:gson:2.8.6' - implementation 'com.github.gbIxaHue:eu4d:0.3.7' + implementation 'com.github.gbIxaHue:eu4d:0.3.8' } diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/structure/abstraction/Extension.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/structure/abstraction/Extension.kt index 76b43ce50a..2aa3fb6af5 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/structure/abstraction/Extension.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/structure/abstraction/Extension.kt @@ -20,7 +20,10 @@ fun List.iterate(func: (Item) -> Unit) { forEach { when (it) { is BaseItem -> func(it) - is ItemGroup -> it.itemList.iterate(func) + is ItemGroup -> { + func(it) + it.itemList.iterate(func) + } } } } \ No newline at end of file diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/structure/abstraction/Item.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/structure/abstraction/Item.kt index eaf3d7afa4..7800192199 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/structure/abstraction/Item.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/structure/abstraction/Item.kt @@ -5,7 +5,12 @@ import com.tangem.tangemtest._arch.structure.Id /** [REDACTED_AUTHOR] */ -interface Item { + +interface UpdateBy{ + fun update(value: B) +} + +interface Item: UpdateBy { val id: Id var parent: Item? var viewModel: ItemViewModel @@ -36,4 +41,7 @@ open class BaseItem( override var parent: Item? = null + override fun update(value: Item) { + viewModel.update(value.viewModel) + } } \ No newline at end of file diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/structure/abstraction/ItemGroup.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/structure/abstraction/ItemGroup.kt index 61a43a0d1c..20a8def0e6 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/structure/abstraction/ItemGroup.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/structure/abstraction/ItemGroup.kt @@ -49,4 +49,8 @@ open class SimpleItemGroup( ILog.d(this, "clear $id") itemList.clear() } + + override fun update(value: Item) { + // nothing to do + } } \ No newline at end of file diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/structure/abstraction/ItemViewModel.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/structure/abstraction/ItemViewModel.kt index a542a3be4d..fc106588e6 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/structure/abstraction/ItemViewModel.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/structure/abstraction/ItemViewModel.kt @@ -16,7 +16,7 @@ class KeyValue(val key: String, val value: Any) class ViewState( isVisible: Boolean? = null, bgColor: Int? = -1 -) { +) : UpdateBy { class State( stateValue: T, @@ -41,9 +41,15 @@ class ViewState( val states = listOf(isVisibleState, backgroundColor, descriptionVisibility) states.forEach { it.preventSameChanges = isPrevented } } + + override fun update(value: ViewState) { +// isVisibleState.update(value.isVisibleState) +// backgroundColor.update(value.backgroundColor) +// descriptionVisibility.update(value.descriptionVisibility) + } } -interface ItemViewModel : PayloadHolder { +interface ItemViewModel : PayloadHolder, UpdateBy { val viewState: ViewState var data: Any? var defaultData: Any? @@ -92,6 +98,14 @@ open class BaseItemViewModel( this.data = data onDataUpdated = callback } + + override fun update(value: ItemViewModel) { + viewState.update(value.viewState) + defaultData = value.defaultData + data = value.data + payload.clear() + payload.putAll(value.payload) + } } class ListViewModel( diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/structure/impl/Items.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/structure/impl/Items.kt index e402650802..d4e4dde775 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/structure/impl/Items.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/_arch/structure/impl/Items.kt @@ -11,29 +11,29 @@ open class TypedItem(id: Id, viewModel: ItemViewModel) : BaseItem(id, viewMod open fun getTypedData(): D? = viewModel.data as? D } -class TextItem(id: Id, viewModel: ItemViewModel) : TypedItem(id, viewModel) { +open class TextItem(id: Id, viewModel: ItemViewModel) : TypedItem(id, viewModel) { constructor(id: Id, value: String? = null, viewState: ViewState = ViewState()) : this(id, BaseItemViewModel(value, viewState)) } -class NumberItem(id: Id, viewModel: ItemViewModel) : TypedItem(id, viewModel) { +open class NumberItem(id: Id, viewModel: ItemViewModel) : TypedItem(id, viewModel) { constructor(id: Id, value: Number? = null, viewState: ViewState = ViewState()) : this(id, BaseItemViewModel(value, viewState)) } -class BoolItem(id: Id, viewModel: ItemViewModel) : TypedItem(id, viewModel) { +open class BoolItem(id: Id, viewModel: ItemViewModel) : TypedItem(id, viewModel) { constructor(id: Id, value: Boolean? = null, viewState: ViewState = ViewState()) : this(id, BaseItemViewModel(value, viewState)) } -class EditTextItem(id: Id, viewModel: ItemViewModel) : TypedItem(id, viewModel) { +open class EditTextItem(id: Id, viewModel: ItemViewModel) : TypedItem(id, viewModel) { constructor(id: Id, value: String? = null, viewState: ViewState = ViewState()) : this(id, BaseItemViewModel(value, viewState)) } -class SpinnerItem(id: Id, viewModel: ListViewModel) : TypedItem(id, viewModel) { +open class SpinnerItem(id: Id, viewModel: ListViewModel) : TypedItem(id, viewModel) { constructor(id: Id, list: List, selectedValue: Any?, viewState: ViewState = ViewState()) : this(id, ListViewModel(list, selectedValue, viewState)) } \ No newline at end of file diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/_main/MainActivity.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/_main/MainActivity.kt index 92e093d2c8..c47ac39601 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/_main/MainActivity.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/_main/MainActivity.kt @@ -3,9 +3,9 @@ package com.tangem.tangemtest._main import android.content.res.Resources import android.os.Bundle import android.view.Menu +import android.view.MenuItem import androidx.activity.viewModels import androidx.appcompat.app.AppCompatActivity -import androidx.appcompat.widget.SwitchCompat import androidx.appcompat.widget.Toolbar import androidx.navigation.findNavController import androidx.navigation.fragment.NavHostFragment @@ -20,7 +20,7 @@ import ru.dev.gbixahue.eu4d.lib.android.global.log.Log */ class MainActivity : AppCompatActivity() { - private val vm: MainViewModel by viewModels() + private val mainVM: MainViewModel by viewModels() private lateinit var appBarConfiguration: AppBarConfiguration override fun onCreate(savedInstanceState: Bundle?) { @@ -52,11 +52,24 @@ class MainActivity : AppCompatActivity() { } override fun onCreateOptionsMenu(menu: Menu): Boolean { - menuInflater.inflate(R.menu.main_menu, menu) - val switchMenu = menu.findItem(R.id.action_favorite) - (switchMenu.actionView as? SwitchCompat)?.let { - it.setOnCheckedChangeListener { buttonView, isChecked -> vm.switchToggled(isChecked) } + menuInflater.inflate(R.menu.menu_activity_main, menu) + return true + } + + override fun onPrepareOptionsMenu(menu: Menu): Boolean { + val switchItem = menu.findItem(R.id.action_toggle_description_visibility) + switchItem.isChecked = mainVM.descriptionSwitchState + return super.onPrepareOptionsMenu(menu) + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + val result = when (item.itemId) { + R.id.action_toggle_description_visibility -> { + item.isChecked = !item.isChecked + mainVM.switchToggled(item.isChecked) + } + else -> null } - return super.onCreateOptionsMenu(menu) + return if (result == null) super.onOptionsItemSelected(item) else true } } \ No newline at end of file diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/_main/MainViewModel.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/_main/MainViewModel.kt index a50da93a68..3900d86530 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/_main/MainViewModel.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/_main/MainViewModel.kt @@ -10,10 +10,12 @@ import ru.dev.gbixahue.eu4d.lib.android.global.log.Log */ class MainViewModel : ViewModel() { val ldDescriptionSwitch = MutableLiveData(false) + var descriptionSwitchState = false var commandResponse: CommandResponse? = null fun switchToggled(state: Boolean) { + descriptionSwitchState = state ldDescriptionSwitch.postValue(state) } diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/_main/entryPoint/ActionListFragment.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/_main/entryPoint/ActionListFragment.kt index ea17194439..4e10211223 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/_main/entryPoint/ActionListFragment.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/_main/entryPoint/ActionListFragment.kt @@ -2,16 +2,14 @@ package com.tangem.tangemtest._main.entryPoint import android.os.Bundle import android.view.View -import android.view.ViewGroup import androidx.fragment.app.activityViewModels import androidx.lifecycle.Observer import androidx.recyclerview.widget.DividerItemDecoration import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView -import androidx.transition.AutoTransition -import androidx.transition.TransitionManager import com.tangem.tangemtest.R import com.tangem.tangemtest._main.MainViewModel +import com.tangem.tangemtest.extensions.view.beginDelayedTransition import com.tangem.tangemtest.ucase.getDefaultNavigationOptions import com.tangem.tangemtest.ucase.resources.ActionType import com.tangem.tangemtest.ucase.resources.MainResourceHolder @@ -47,7 +45,7 @@ class ActionListFragment : BaseFragment() { } mainActivityVM.ldDescriptionSwitch.observe(viewLifecycleOwner, Observer { vhDataWrapper.descriptionIsVisible = it - TransitionManager.beginDelayedTransition(rvActions as ViewGroup, AutoTransition()) + rvActions.beginDelayedTransition() rvActions.adapter?.notifyDataSetChanged() }) } diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/_main/entryPoint/RvActions.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/_main/entryPoint/RvActions.kt index df045a572d..5433027e92 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/_main/entryPoint/RvActions.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/_main/entryPoint/RvActions.kt @@ -59,8 +59,6 @@ class RvActionsVH( containerDescription.visibility = if (wrapper.descriptionIsVisible) View.VISIBLE else View.GONE } - - override fun onDataBound(data: ActionType) {} } fun RecyclerView.ViewHolder.getString(@StringRes id: Int?, ifNull: String = ""): String { diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/commons/Abstractions.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/commons/Abstractions.kt index f5f02c0ace..6c3b1abc26 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/commons/Abstractions.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/commons/Abstractions.kt @@ -4,6 +4,13 @@ package com.tangem.tangemtest.commons [REDACTED_AUTHOR] */ interface Store { - fun save(config: M) + fun save(value: M) fun restore(): M +} + +interface KeyedStore { + fun save(key: String, value: M) + fun restore(key: String): M + fun restoreAll(): MutableMap + fun delete(key: String) } \ No newline at end of file diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/commons/DialogController.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/commons/DialogController.kt new file mode 100644 index 0000000000..de7db7ae3e --- /dev/null +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/commons/DialogController.kt @@ -0,0 +1,76 @@ +package com.tangem.tangemtest.commons + +import android.app.Activity +import android.app.Dialog +import android.view.LayoutInflater +import android.view.View +import androidx.appcompat.app.AlertDialog + +class DialogController { + var onDismissCallback: (() -> Unit)? = null + var onShowCallback: (() -> Unit)? = null + var view: View? = null + + private var rawDialog: Dialog? = null + + private var inShowingProcess = false + private var inDismissingProcess = false + private var autoReleaseOnDismiss = false + + fun createAlert(context: Activity, resLayout: Int): AlertDialog { + view = LayoutInflater.from(context).inflate(resLayout, null) + rawDialog = AlertDialog.Builder(context).setView(view).create().apply { + setOnShowListener { onShow() } + setOnDismissListener { onDismiss() } + } + return rawDialog as AlertDialog + } + + fun set(dialog: Dialog) { + rawDialog = dialog + dialog.setOnShowListener { onShow() } + dialog.setOnDismissListener { onDismiss() } + } + + private fun onShow() { + inShowingProcess = false + onShowCallback?.invoke() + } + + private fun onDismiss() { + inDismissingProcess = false + if (autoReleaseOnDismiss) release() + onDismissCallback?.invoke() + } + + fun show() { + val dialog = rawDialog ?: return + if (inShowingProcess) return + if (dialog.isShowing) return + + inShowingProcess = true + dialog.show() + } + + fun dismiss(autoRelease: Boolean = true) { + val dialog = rawDialog ?: return + if (inDismissingProcess) return + if (!dialog.isShowing) return + + autoReleaseOnDismiss = autoRelease + inDismissingProcess = true + dialog.dismiss() + } + + fun release() { + onDismissCallback = null + onShowCallback = null + + inShowingProcess = false + inDismissingProcess = false + autoReleaseOnDismiss = false + + view = null + rawDialog = null + } +} \ No newline at end of file diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/extensions/CardConfig.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/extensions/CardConfig.kt index c8f68bec9c..ab8c84334b 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/extensions/CardConfig.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/extensions/CardConfig.kt @@ -11,24 +11,38 @@ fun CardConfig.Companion.create(application: Application): CardConfig { val preferences = application.getSharedPreferences("prefs", Context.MODE_PRIVATE) - val signingMethod = SigningMethod.build( - signHash = preferences.getBoolean("personalization_SigningMethod_0", false), - signRaw = preferences.getBoolean("personalization_SigningMethod_1", false), - signHashValidatedByIssuer = preferences.getBoolean("personalization_SigningMethod_2", false), - signRawValidatedByIssuer = preferences.getBoolean("personalization_SigningMethod_3", false), - signHashValidatedByIssuerAndWriteIssuerData = preferences.getBoolean("personalization_SigningMethod_4", false), - signRawValidatedByIssuerAndWriteIssuerData = preferences.getBoolean("personalization_SigningMethod_5", false), - signPos = preferences.getBoolean("personalization_SigningMethod_6", false) - ) + val signingMethodMaskBuilder = SigningMethodMaskBuilder() + if (preferences.getBoolean("personalization_SigningMethod_0", false)) { + signingMethodMaskBuilder.add(SigningMethod.SignHash) + } + if (preferences.getBoolean("personalization_SigningMethod_1", false)) { + signingMethodMaskBuilder.add(SigningMethod.SignRaw) + } + if (preferences.getBoolean("personalization_SigningMethod_2", false)) { + signingMethodMaskBuilder.add(SigningMethod.SignHashValidateByIssuer) + } + if (preferences.getBoolean("personalization_SigningMethod_3", false)) { + signingMethodMaskBuilder.add(SigningMethod.SignRawValidateByIssuer) + } + if (preferences.getBoolean("personalization_SigningMethod_4", false)) { + signingMethodMaskBuilder.add(SigningMethod.SignHashValidateByIssuerWriteIssuerData) + } + if (preferences.getBoolean("personalization_SigningMethod_5", false)) { + signingMethodMaskBuilder.add(SigningMethod.SignRawValidateByIssuerWriteIssuerData) + } + if (preferences.getBoolean("personalization_SigningMethod_6", false)) { + signingMethodMaskBuilder.add(SigningMethod.SignHash) + } + val signingMethod = signingMethodMaskBuilder.build() val isNote = preferences.getBoolean("personalization_ProductMask_IsNote", true) val isTag = preferences.getBoolean("personalization_ProductMask_IsTag", false) val isIdCard = preferences.getBoolean("personalization_ProductMask_IsIDCard", false) val productMaskBuilder = ProductMaskBuilder() - if (isNote) productMaskBuilder.add(ProductMask.note) - if (isTag) productMaskBuilder.add(ProductMask.tag) - if (isIdCard) productMaskBuilder.add(ProductMask.idCard) + if (isNote) productMaskBuilder.add(Product.Note) + if (isTag) productMaskBuilder.add(Product.Tag) + if (isIdCard) productMaskBuilder.add(Product.IdCard) val productMask = productMaskBuilder.build() var tokenSymbol: String? = null @@ -80,7 +94,7 @@ fun CardConfig.Companion.create(application: Application): CardConfig { cardData = cardData, curveID = EllipticCurve.byName(preferences.getString("personalization_CurveId", "secp256k1")!!) ?: EllipticCurve.Secp256k1, - signingMethod = signingMethod, + signingMethods = signingMethod, createWallet = preferences.getBoolean("personalization_CreateWallet", true), maxSignatures = preferences.getString("personalization_MaxSignatures", "1000")!!.toInt(), isReusable = preferences.getBoolean("personalization_SettingsMask_IsReusable", true), diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/extensions/view/Transitions.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/extensions/view/Transitions.kt new file mode 100644 index 0000000000..d58f12aa97 --- /dev/null +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/extensions/view/Transitions.kt @@ -0,0 +1,13 @@ +package com.tangem.tangemtest.extensions.view + +import android.view.ViewGroup +import androidx.transition.AutoTransition +import androidx.transition.Transition +import androidx.transition.TransitionManager + +/** +[REDACTED_AUTHOR] + */ +fun ViewGroup.beginDelayedTransition(transition: Transition = AutoTransition()) { + TransitionManager.beginDelayedTransition(this, transition) +} \ No newline at end of file diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/actions/PersonalizeAction.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/actions/PersonalizeAction.kt index 612b728c24..457b4baac3 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/actions/PersonalizeAction.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/actions/PersonalizeAction.kt @@ -37,7 +37,7 @@ class PersonalizeAction : BaseAction() { val acquirer = DefaultPersonalizationParams.acquirer() val manufacturer = DefaultPersonalizationParams.manufacturer() - val personalizeConfig = PersonalizationConfigConverter().convert(itemList, PersonalizationConfig()) + val personalizeConfig = PersonalizationConfigConverter().convert(itemList, PersonalizationConfig.default()) val cardConfig = PersonalizationConfigToCardConfig().convert(personalizeConfig) attrs.tangemSdk.personalize(cardConfig, issuer, manufacturer, acquirer) { diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/actions/SignAction.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/actions/SignAction.kt index 375c2ce9a7..f44f36d1ee 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/actions/SignAction.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/actions/SignAction.kt @@ -13,10 +13,10 @@ import ru.dev.gbixahue.eu4d.lib.kotlin.stringOf class SignAction : BaseAction() { override fun executeMainAction(payload: PayloadHolder, attrs: AttrForAction, callback: ActionCallback) { val dataForHashing = attrs.itemList.findItem(TlvId.TransactionOutHash) ?: return - val hash = dataForHashing.getData() as? ByteArray ?: return + val hash = dataForHashing.getData() as? String ?: return val cardId = attrs.itemList.findItem(TlvId.CardId)?.viewModel?.data ?: return - attrs.tangemSdk.sign(arrayOf(hash), stringOf(cardId)) { handleResult(payload, it, null, attrs, callback) } + attrs.tangemSdk.sign(arrayOf(hash.toByteArray()), stringOf(cardId)) { handleResult(payload, it, null, attrs, callback) } } override fun getActionByTag(payload: PayloadHolder, id: Id, attrs: AttrForAction): ((ActionCallback) -> Unit)? { diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/ItemsManager.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/ItemsManager.kt index a43809684b..20d2ed3eb8 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/ItemsManager.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/ItemsManager.kt @@ -25,6 +25,7 @@ interface ItemsManager : PayloadHolder { fun invokeMainAction(tangemSdk: TangemSdk, callback: ActionCallback) fun getActionByTag(id: Id, tangemSdk: TangemSdk): ((ActionCallback) -> Unit)? fun attachPayload(payload: Payload) + fun updateByItemList(list: List) } interface PayloadKey { diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/managers/BaseItemsManager.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/managers/BaseItemsManager.kt index 6ddbc6a916..a3308d623d 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/managers/BaseItemsManager.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/managers/BaseItemsManager.kt @@ -6,6 +6,7 @@ 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.findItem +import com.tangem.tangemtest._arch.structure.abstraction.iterate import com.tangem.tangemtest.ucase.domain.actions.Action import com.tangem.tangemtest.ucase.domain.actions.AttrForAction import com.tangem.tangemtest.ucase.domain.paramsManager.ActionCallback @@ -47,6 +48,10 @@ open class BaseItemsManager(protected val action: Action) : ItemsManager, Lifecy this.changeConsequence = consequence } + override fun updateByItemList(list: List) { + list.iterate { itemList.findItem(it.id)?.update(it) } + } + override fun invokeMainAction(tangemSdk: TangemSdk, callback: ActionCallback) { action.executeMainAction(this, getAttrsForAction(tangemSdk), callback) } diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/managers/PersonalizationItemsManager.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/managers/PersonalizationItemsManager.kt index 1652cd2833..4a9c0b0572 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/managers/PersonalizationItemsManager.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/managers/PersonalizationItemsManager.kt @@ -29,7 +29,7 @@ class PersonalizationItemsManager( @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY) fun onDestroy() { - val config = converter.convert(itemList, PersonalizationConfig()) + val config = converter.convert(itemList, PersonalizationConfig.default()) store.save(config) } } \ No newline at end of file diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/triggers/changeConsequence/ParamChangeConsequence.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/triggers/changeConsequence/ParamChangeConsequence.kt index 730c570be6..bb771e86f6 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/triggers/changeConsequence/ParamChangeConsequence.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/paramsManager/triggers/changeConsequence/ParamChangeConsequence.kt @@ -4,6 +4,7 @@ import com.tangem.commands.Card import com.tangem.commands.EllipticCurve import com.tangem.common.extensions.calculateSha256 import com.tangem.common.extensions.calculateSha512 +import com.tangem.common.extensions.toHexString import com.tangem.tangemtest._arch.structure.PayloadHolder import com.tangem.tangemtest._arch.structure.abstraction.Item import com.tangem.tangemtest._arch.structure.abstraction.findItem @@ -40,7 +41,7 @@ class SignScanConsequence : ItemsChangeConsequence { EllipticCurve.Ed25519 -> dataForHashing.calculateSha512() else -> return null } - hashItem.setData(hashedData) + hashItem.setData(hashedData.toHexString()) } return affectedItems.toList() } diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/responses/ResponseJsonConverter.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/responses/ResponseJsonConverter.kt index d54bc08a5d..5533c3682d 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/responses/ResponseJsonConverter.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/domain/responses/ResponseJsonConverter.kt @@ -3,6 +3,7 @@ package com.tangem.tangemtest.ucase.domain.responses import com.google.gson.* import com.tangem.commands.* import com.tangem.common.extensions.toHexString +import com.tangem.tangemtest.extensions.print import java.lang.reflect.Type import java.text.DateFormat import java.util.* @@ -11,14 +12,17 @@ import java.util.* [REDACTED_AUTHOR] */ class ResponseJsonConverter { + val gson: Gson by lazy { init() } + private val fieldConverter = ResponseFieldConverter() + private fun init(): Gson { val builder = GsonBuilder().apply { - registerTypeAdapter(ByteArray::class.java, ByteTypeAdapter()) - registerTypeAdapter(SigningMethod::class.java, SigningMethodTypeAdapter()) - registerTypeAdapter(SettingsMask::class.java, SettingsMaskTypeAdapter()) - registerTypeAdapter(ProductMask::class.java, ProductMaskTypeAdapter()) + registerTypeAdapter(ByteArray::class.java, ByteTypeAdapter(fieldConverter)) + registerTypeAdapter(SigningMethodMask::class.java, SigningMethodTypeAdapter(fieldConverter)) + registerTypeAdapter(SettingsMask::class.java, SettingsMaskTypeAdapter(fieldConverter)) + registerTypeAdapter(ProductMask::class.java, ProductMaskTypeAdapter(fieldConverter)) registerTypeAdapter(Date::class.java, DateTypeAdapter()) } builder.setPrettyPrinting() @@ -28,31 +32,41 @@ class ResponseJsonConverter { fun convertResponse(response: CommandResponse?): String = gson.toJson(response) } -class ByteTypeAdapter : JsonSerializer { +class ByteTypeAdapter( + private val fieldConverter: ResponseFieldConverter +) : JsonSerializer { override fun serialize(src: ByteArray, typeOfSrc: Type, context: JsonSerializationContext): JsonElement { - return JsonPrimitive(src.toHexString()) + return JsonPrimitive(fieldConverter.byteArray(src)) } } -class SettingsMaskTypeAdapter : JsonSerializer { +class SettingsMaskTypeAdapter( + private val fieldConverter: ResponseFieldConverter +) : JsonSerializer { override fun serialize(src: SettingsMask, typeOfSrc: Type, context: JsonSerializationContext): JsonElement { - val arrayElement = JsonArray() - Settings.values() - .filter { src.contains(it) } - .forEach { arrayElement.add(it.name) } - return arrayElement + return JsonArray().apply { + fieldConverter.settingsMaskList(src).forEach { add(it) } + } } } -class ProductMaskTypeAdapter : JsonSerializer { +class ProductMaskTypeAdapter( + private val fieldConverter: ResponseFieldConverter +) : JsonSerializer { override fun serialize(src: ProductMask, typeOfSrc: Type, context: JsonSerializationContext): JsonElement { - return JsonPrimitive(src.rawValue.toString()) + return JsonArray().apply { + fieldConverter.productMaskList(src).forEach { add(it) } + } } } -class SigningMethodTypeAdapter : JsonSerializer { - override fun serialize(src: SigningMethod, typeOfSrc: Type, context: JsonSerializationContext): JsonElement { - return JsonPrimitive(src.rawValue.toString()) +class SigningMethodTypeAdapter( + private val fieldConverter: ResponseFieldConverter +) : JsonSerializer { + override fun serialize(src: SigningMethodMask, typeOfSrc: Type, context: JsonSerializationContext): JsonElement { + return JsonArray().apply { + fieldConverter.signingMethodList(src).forEach { add(it) } + } } } @@ -61,4 +75,41 @@ class DateTypeAdapter : JsonSerializer { val formatter = DateFormat.getDateInstance(DateFormat.DEFAULT, Locale("en_US")) return JsonPrimitive(formatter.format(src).toString()) } +} + +class ResponseFieldConverter { + + fun productMask(productMask: ProductMask?): String { + return productMaskList(productMask).print(wrap = false) + } + + fun productMaskList(productMask: ProductMask?): List { + val mask = productMask ?: return emptyList() + + return Product.values().filter { mask.contains(it) }.map { it.name } + } + + fun signingMethod(signingMask: SigningMethodMask?): String { + return signingMethodList(signingMask).print(wrap = false) + } + + fun signingMethodList(signingMask: SigningMethodMask?): List { + val mask = signingMask ?: return emptyList() + + return SigningMethod.values().filter { mask.contains(it) }.map { it.name } + } + + fun settingsMask(settingsMask: SettingsMask?): String { + return settingsMaskList(settingsMask).print(wrap = false) + } + + fun settingsMaskList(settingsMask: SettingsMask?): List { + val masks = settingsMask ?: return emptyList() + + return Settings.values().filter { masks.contains(it) }.map { it.name } + } + + fun byteArray(byteArray: ByteArray?): String? { + return byteArray?.toHexString() + } } \ No newline at end of file diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/resources/initializers/ResponseResources.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/resources/initializers/ResponseResources.kt index a9ad0a3d69..a199a04a26 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/resources/initializers/ResponseResources.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/resources/initializers/ResponseResources.kt @@ -5,6 +5,8 @@ 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 +import com.tangem.tangemtest.ucase.variants.responses.DepersonalizeId +import com.tangem.tangemtest.ucase.variants.responses.SignId /** [REDACTED_AUTHOR] @@ -13,6 +15,8 @@ class ResponseResources { fun init(holder: MainResourceHolder) { initCard(holder) initCardData(holder) + initSignResponse(holder) + initDepersonalizeResponse(holder) } private fun initCard(holder: MainResourceHolder) { @@ -26,7 +30,7 @@ class ResponseResources { holder.register(CardId.issuerPublicKey, Resources(R.string.response_card_issuer_data_public_key, R.string.info_response_card_issuer_data_public_key)) holder.register(CardId.curve, Resources(R.string.response_card_curve, R.string.info_response_card_curve)) holder.register(CardId.maxSignatures, Resources(R.string.response_card_max_signatures, R.string.info_response_card_max_signatures)) - holder.register(CardId.signingMethod, Resources(R.string.response_card_signing_method, R.string.response_card_signing_method)) + holder.register(CardId.signingMethod, Resources(R.string.response_card_signing_method, R.string.info_response_card_signing_method)) holder.register(CardId.pauseBeforePin2, Resources(R.string.response_card_pause_before_pin2, R.string.info_response_card_allow_pin2)) holder.register(CardId.walletPublicKey, Resources(R.string.response_card_wallet_public_key, R.string.info_response_card_wallet_public_key)) holder.register(CardId.walletRemainingSignatures, Resources(R.string.response_card_wallet_remaining_signatures, R.string.info_response_card_wallet_remaining_signatures)) @@ -50,4 +54,15 @@ class ResponseResources { 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)) } + + private fun initSignResponse(holder: MainResourceHolder) { + holder.register(SignId.cid, Resources(R.string.response_sign_cid, R.string.info_response_sign_cid)) + holder.register(SignId.walletSignedHashes, Resources(R.string.response_sign_wallet_signed_hashes, R.string.info_response_sign_wallet_signed_hashes)) + holder.register(SignId.walletRemainingSignatures, Resources(R.string.response_sign_wallet_remaining_signatures, R.string.info_response_sign_wallet_remaining_signatures)) + holder.register(SignId.signature, Resources(R.string.response_sign_signature, R.string.info_response_sign_signature)) + } + + private fun initDepersonalizeResponse(holder: MainResourceHolder) { + holder.register(DepersonalizeId.isSuccess, Resources(R.string.response_depersonalize_is_success, R.string.info_response_depersonalize_is_success)) + } } \ No newline at end of file diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/BaseCardActionFragment.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/BaseCardActionFragment.kt index e9916b33f0..32c0746431 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/BaseCardActionFragment.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/BaseCardActionFragment.kt @@ -8,9 +8,11 @@ import androidx.core.view.plusAssign import androidx.fragment.app.activityViewModels import androidx.fragment.app.viewModels import androidx.lifecycle.Observer +import androidx.swiperefreshlayout.widget.SwipeRefreshLayout import com.google.android.material.floatingactionbutton.FloatingActionButton import com.tangem.TangemSdk import com.tangem.commands.Card +import com.tangem.commands.CommandResponse import com.tangem.tangem_sdk_new.extensions.init import com.tangem.tangemtest.R import com.tangem.tangemtest._arch.structure.Id @@ -28,7 +30,8 @@ import ru.dev.gbixahue.eu4d.lib.android.global.log.Log */ abstract class BaseCardActionFragment : BaseFragment(), ActionView { - protected lateinit var itemContainer: ViewGroup + protected lateinit var swrLayout: SwipeRefreshLayout + protected lateinit var contentContainer: ViewGroup protected lateinit var actionFab: FloatingActionButton protected abstract val itemsManager: ItemsManager @@ -38,6 +41,8 @@ abstract class BaseCardActionFragment : BaseFragment(), ActionView { private val paramsWidgetList = mutableListOf() + override fun getLayoutId(): Int = R.layout.fg_base_action_layout + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) Log.d(this, "onViewCreated") @@ -47,7 +52,7 @@ abstract class BaseCardActionFragment : BaseFragment(), ActionView { actionVM.setCardManager(TangemSdk.init(requireActivity())) actionVM.attachToPayload(mutableMapOf(PayloadKey.actionView to this as ActionView)) - initFab() + initViews() createWidgets { widgetsWasCreated() subscribeToViewModelChanges() @@ -57,11 +62,13 @@ abstract class BaseCardActionFragment : BaseFragment(), ActionView { protected open fun widgetsWasCreated() {} protected open fun bindViews() { - itemContainer = mainView.findViewById(R.id.ll_container) + swrLayout = mainView.findViewById(R.id.swr_layout) + contentContainer = mainView.findViewById(R.id.ll_content_container) actionFab = mainView.findViewById(R.id.fab_action) } - protected open fun initFab() { + protected open fun initViews() { + swrLayout.isEnabled = false enableActionFab(false) actionFab.setOnClickListener { actionVM.invokeMainAction() } } @@ -70,7 +77,7 @@ abstract class BaseCardActionFragment : BaseFragment(), ActionView { Log.d(this, "createWidgets") actionVM.ldItemList.observe(viewLifecycleOwner, Observer { itemList -> itemList.forEach { param -> - val widget = ParameterWidget(inflateParamView(itemContainer), param) + val widget = ParameterWidget(inflateParamView(contentContainer), param) widget.onValueChanged = { id, value -> actionVM.userChangedItem(id, value) } widget.onActionBtnClickListener = actionVM.getItemAction(param.id) paramsWidgetList.add(widget) @@ -81,39 +88,46 @@ abstract class BaseCardActionFragment : BaseFragment(), ActionView { protected open fun subscribeToViewModelChanges() { Log.d(this, "subscribeToViewModelChanges") - listenResponse() - listenResponseData() - listenResponseCardData() - listenError() - listenChangedItems() - listenDescriptionSwitchChanges() - } - - private fun listenResponse() { actionVM.seResponse.observe(viewLifecycleOwner, Observer { - Log.d(this, "listen response: $it") - mainActivityVM.changeResponseEvent(it) + Log.d(this, "handle response: $it") + handleResponse(it) }) - } - - protected open fun listenResponseData() { actionVM.seResponseData.observe(viewLifecycleOwner, Observer { - Log.d(this, "listen responseData: $it") - navigateTo(R.id.action_nav_card_action_to_response_screen) + Log.d(this, "handle responseData: $it") + handleResponseData(it) }) - } - - protected open fun listenResponseCardData() { actionVM.seResponseCardData.observe(viewLifecycleOwner, Observer { - Log.d(this, "listen responseCardData: $it") - responseCardDataHandled(it) + Log.d(this, "handle responseCardData: $it") + handleResponseCardData(it) }) + actionVM.seError.observe(viewLifecycleOwner, Observer { + Log.d(this, "handle error: $it") + handleError(it) + }) + mainActivityVM.ldDescriptionSwitch.observe(viewLifecycleOwner, Observer { + Log.d(this, "handle descriptionVisibilityState: $it") + handleDescriptionSwitchChanges(it) + }) + listenChangedItems() } - protected open fun responseCardDataHandled(card: Card?) {} + protected open fun handleResponse(response: CommandResponse) { + mainActivityVM.changeResponseEvent(response) + } - protected open fun listenError() { - actionVM.seError.observe(viewLifecycleOwner, Observer { showSnackbar(it) }) + protected open fun handleResponseData(response: CommandResponse) { + navigateTo(R.id.action_nav_card_action_to_response_screen, options = null) + } + + protected open fun handleResponseCardData(card: Card) {} + + protected open fun handleError(error: String) { + showSnackbar(error) + } + + protected open fun handleDescriptionSwitchChanges(descriptionVisibilityState: Boolean) { + actionVM.toggleDescriptionVisibility(descriptionVisibilityState) + paramsWidgetList.forEach { it.toggleDescriptionVisibility(descriptionVisibilityState) } } @Deprecated("Start to use itemViewModel") @@ -126,12 +140,6 @@ abstract class BaseCardActionFragment : BaseFragment(), ActionView { }) } - protected open fun listenDescriptionSwitchChanges() { - 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) diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/widgets/ParameterWidget.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/widgets/ParameterWidget.kt index 96037984ea..ab01e1a692 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/widgets/ParameterWidget.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/ui/widgets/ParameterWidget.kt @@ -6,14 +6,13 @@ import android.view.View import android.view.ViewGroup import android.widget.Button import android.widget.TextView -import androidx.transition.AutoTransition -import androidx.transition.TransitionManager 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.Item import com.tangem.tangemtest._arch.structure.impl.EditTextItem +import com.tangem.tangemtest.extensions.view.beginDelayedTransition import com.tangem.tangemtest.ucase.resources.ActionType import com.tangem.tangemtest.ucase.resources.MainResourceHolder import com.tangem.tangemtest.ucase.resources.Resources @@ -73,7 +72,7 @@ class ParameterWidget( } fun toggleDescriptionVisibility(state: Boolean) { - TransitionManager.beginDelayedTransition(parent.parent as ViewGroup, AutoTransition()) + (parent.parent as ViewGroup).beginDelayedTransition() descriptionContainer.visibility = if (state) View.VISIBLE else View.GONE } @@ -95,7 +94,7 @@ class ParameterWidget( private fun toggleActionBtnVisibility() { fun switchVisibilityState(newState: Int) { actionBtnVisibilityState = newState - TransitionManager.beginDelayedTransition(parent, AutoTransition()) + parent.beginDelayedTransition() btnAction.visibility = actionBtnVisibilityState } when { diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/depersonalize/ui/DepersonalizeActionFragment.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/depersonalize/ui/DepersonalizeActionFragment.kt index e2ae9cdbdd..107ab9b3b3 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/depersonalize/ui/DepersonalizeActionFragment.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/depersonalize/ui/DepersonalizeActionFragment.kt @@ -1,6 +1,5 @@ package com.tangem.tangemtest.ucase.variants.depersonalize.ui -import com.tangem.tangemtest.R import com.tangem.tangemtest.ucase.domain.paramsManager.ItemsManager import com.tangem.tangemtest.ucase.domain.paramsManager.managers.DepersonalizeItemsManager import com.tangem.tangemtest.ucase.ui.BaseCardActionFragment @@ -11,6 +10,4 @@ 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 } \ No newline at end of file diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/PersonalizationConfigStore.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/PersonalizationConfigStore.kt index 77a68278f8..9c774a8071 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/PersonalizationConfigStore.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/PersonalizationConfigStore.kt @@ -5,22 +5,49 @@ import android.content.SharedPreferences import androidx.core.content.edit import com.google.gson.Gson import com.tangem.tangemtest.AppTangemDemo +import com.tangem.tangemtest.commons.KeyedStore import com.tangem.tangemtest.commons.Store import com.tangem.tangemtest.ucase.variants.personalize.dto.PersonalizationConfig -class PersonalizationConfigStore(context: Context) : Store { +class PersonalizationConfigStore(context: Context) : Store, KeyedStore { - private val key = "personalization_config" + companion object { + val defaultKey = "default" + } - private val sp: SharedPreferences = (context.applicationContext as AppTangemDemo).sharedPreferences() + private val sharedPreferencesKey = "personalization_presets" + + private val sp: SharedPreferences = (context.applicationContext as AppTangemDemo).sharedPreferences(sharedPreferencesKey) private val gson: Gson = Gson() - override fun save(config: PersonalizationConfig) { - sp.edit(true) { putString(key, gson.toJson(config)) } + override fun save(value: PersonalizationConfig) { + save(defaultKey, value) } - override fun restore(): PersonalizationConfig { - val json = sp.getString(key, gson.toJson(PersonalizationConfig())) - return gson.fromJson(json, PersonalizationConfig::class.java) + override fun restore(): PersonalizationConfig = restore(defaultKey) + + override fun save(key: String, value: PersonalizationConfig) { + sp.edit(true) { putString(key, toJson(value)) } } + + override fun restore(key: String): PersonalizationConfig { + val json = sp.getString(key, toJson(getDefaultConfig())) + return fromJson(json!!) + } + + override fun restoreAll(): MutableMap { + val map = mutableMapOf() + sp.all.forEach { map[it.key] = fromJson(it.value as String) } + return map.toSortedMap() + } + + override fun delete(key: String) { + sp.edit(true) { remove(key) } + } + + private fun getDefaultConfig(): PersonalizationConfig = PersonalizationConfig.default() + + private fun toJson(value: PersonalizationConfig): String = gson.toJson(value) + + private fun fromJson(json: String): PersonalizationConfig = gson.fromJson(json, PersonalizationConfig::class.java) } \ No newline at end of file diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/converter/DefaultValues.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/converter/DefaultValues.kt index 37364a0f8a..2f406d98c3 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/converter/DefaultValues.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/converter/DefaultValues.kt @@ -19,7 +19,7 @@ class ConfigValuesHolder : BaseTypedHolder() { register(CardNumber.BatchId, Value(default.batchId)) register(Common.Curve, Value(default.curveID, Helper.listOfCurves())) register(Common.Blockchain, Value(default.blockchain, Helper.listOfBlockchain())) - register(Common.BlockchainCustom, Value("")) + register(Common.BlockchainCustom, Value(default.blockchainCustom)) register(Common.MaxSignatures, Value(default.MaxSignatures)) register(Common.CreateWallet, Value(default.createWallet)) register(SigningMethod.SignTx, Value(default.SigningMethod0)) @@ -66,7 +66,7 @@ class ConfigValuesHolder : BaseTypedHolder() { register(SettingsMaskNdef.DynamicNdef, Value(default.useDynamicNDEF)) register(SettingsMaskNdef.DisablePrecomputedNdef, Value(default.disablePrecomputedNDEF)) register(SettingsMaskNdef.Aar, Value(default.aar, Helper.aarList())) - register(SettingsMaskNdef.AarCustom, Value(default.aar)) + register(SettingsMaskNdef.AarCustom, Value(default.aarCustom)) register(SettingsMaskNdef.Uri, Value(default.uri)) register(Pins.Pin, Value(default.PIN)) register(Pins.Pin2, Value(default.PIN2)) diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/converter/PersonalizationConfigConverter.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/converter/PersonalizationConfigConverter.kt index d450ab54d9..3d7fb1e3d1 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/converter/PersonalizationConfigConverter.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/converter/PersonalizationConfigConverter.kt @@ -52,6 +52,7 @@ class ItemsToPersonalizationConfig : ItemsToModel { val export = PersonalizationConfig() export.series = getTyped(CardNumber.Series) export.startNumber = getTyped(CardNumber.Number) + export.batchId = getTyped(CardNumber.BatchId) export.curveID = getTyped(Common.Curve) export.blockchain = getTyped(Common.Blockchain) export.blockchainCustom = getTyped(Common.BlockchainCustom) diff --git a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/converter/PersonalizationConfigToCardConfig.kt b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/converter/PersonalizationConfigToCardConfig.kt index fa79df96e0..a1c710527b 100644 --- a/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/converter/PersonalizationConfigToCardConfig.kt +++ b/tangem-demo/src/main/java/com/tangem/tangemtest/ucase/variants/personalize/converter/PersonalizationConfigToCardConfig.kt @@ -1,8 +1,6 @@ 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.* import com.tangem.commands.personalization.entities.CardConfig import com.tangem.commands.personalization.entities.NdefRecord import com.tangem.tangemtest.ucase.variants.personalize.dto.PersonalizationConfig @@ -12,15 +10,29 @@ import java.util.* class PersonalizationConfigToCardConfig : Converter { override fun convert(from: PersonalizationConfig): CardConfig { - val signingMethod = com.tangem.commands.SigningMethod.build( - signHash = from.SigningMethod0, - signRaw = from.SigningMethod1, - signHashValidatedByIssuer = from.SigningMethod2, - signRawValidatedByIssuer = from.SigningMethod3, - signHashValidatedByIssuerAndWriteIssuerData = from.SigningMethod4, - signRawValidatedByIssuerAndWriteIssuerData = from.SigningMethod5, - signPos = from.SigningMethod6 - ) + val signingMethodMaskBuilder = SigningMethodMaskBuilder() + if (from.SigningMethod0) { + signingMethodMaskBuilder.add(SigningMethod.SignHash) + } + if (from.SigningMethod1) { + signingMethodMaskBuilder.add(SigningMethod.SignRaw) + } + if (from.SigningMethod2) { + signingMethodMaskBuilder.add(SigningMethod.SignHashValidateByIssuer) + } + if (from.SigningMethod3) { + signingMethodMaskBuilder.add(SigningMethod.SignRawValidateByIssuer) + } + if (from.SigningMethod4) { + signingMethodMaskBuilder.add(SigningMethod.SignHashValidateByIssuerWriteIssuerData) + } + if (from.SigningMethod5) { + signingMethodMaskBuilder.add(SigningMethod.SignRawValidateByIssuerWriteIssuerData) + } + if (from.SigningMethod6) { + signingMethodMaskBuilder.add(SigningMethod.SignHash) + } + val signingMethod = signingMethodMaskBuilder.build() val isNote = from.cardData.product_note val isTag = from.cardData.product_tag @@ -28,10 +40,10 @@ class PersonalizationConfigToCardConfig : Converter Unit) { + Log.d(this, "createWidgets") + val itemList = mutableListOf() + + val maxDelay = 500 + val timeStart = System.currentTimeMillis() + actionVM.ldItemList.observe(viewLifecycleOwner, Observer { list -> + Log.d(this, "ldBlockList size: ${list.size}") + itemList.addAll(list) + val llContainer = LinearLayout(requireContext()).apply { orientation = LinearLayout.VERTICAL } + postWork { + val builder = WidgetBuilder(PersonalizationItemBuilder()) + itemList.forEach { builder.build(it, llContainer) } + actionVM.attachToPayload(mutableMapOf( + PayloadKey.actionView to this as ActionView, + PayloadKey.itemList to itemList + )) + val timeEnd = System.currentTimeMillis() + val diff = timeEnd - timeStart + postUI(maxDelay - diff) { + contentContainer.beginDelayedTransition(Fade()) + contentContainer.addView(llContainer) + widgetCreatedCallback() + swrLayout.isRefreshing = false + swrLayout.isEnabled = false + } + } + }) + } + override fun widgetsWasCreated() { super.widgetsWasCreated() - val btnContainer = itemContainer.inflate(R.layout.view_simple_button) + val btnContainer = contentContainer.inflate(R.layout.view_simple_button) val btn = btnContainer.findViewById