Updated on 2026-08-14
This commit is contained in:
commit
94a78fb08c
142 changed files with 5224 additions and 1101 deletions
541
.idea/navEditor.xml
generated
541
.idea/navEditor.xml
generated
|
|
@ -1,541 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="navEditor-manualLayoutAlgorithm2">
|
||||
<option name="myPositions">
|
||||
<map>
|
||||
<entry key="main_graph.xml">
|
||||
<value>
|
||||
<LayoutPositions>
|
||||
<option name="myPositions">
|
||||
<map>
|
||||
<entry key="cameraFragment">
|
||||
<value>
|
||||
<LayoutPositions>
|
||||
<option name="myPosition">
|
||||
<Point>
|
||||
<option name="x" value="-397" />
|
||||
<option name="y" value="645" />
|
||||
</Point>
|
||||
</option>
|
||||
</LayoutPositions>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="confirmTransactionFragment">
|
||||
<value>
|
||||
<LayoutPositions>
|
||||
<option name="myPosition">
|
||||
<Point>
|
||||
<option name="x" value="744" />
|
||||
<option name="y" value="2390" />
|
||||
</Point>
|
||||
</option>
|
||||
<option name="myPositions">
|
||||
<map>
|
||||
<entry key="action_confirmTransactionFragment_to_pinRequestFragment">
|
||||
<value>
|
||||
<LayoutPositions />
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="action_confirmTransactionFragment_to_signTransactionFragment">
|
||||
<value>
|
||||
<LayoutPositions />
|
||||
</value>
|
||||
</entry>
|
||||
</map>
|
||||
</option>
|
||||
</LayoutPositions>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="createNewWalletFragment">
|
||||
<value>
|
||||
<LayoutPositions>
|
||||
<option name="myPosition">
|
||||
<Point>
|
||||
<option name="x" value="500" />
|
||||
<option name="y" value="105" />
|
||||
</Point>
|
||||
</option>
|
||||
</LayoutPositions>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="emptyWalletFragment">
|
||||
<value>
|
||||
<LayoutPositions>
|
||||
<option name="myPosition">
|
||||
<Point>
|
||||
<option name="x" value="256" />
|
||||
<option name="y" value="105" />
|
||||
</Point>
|
||||
</option>
|
||||
<option name="myPositions">
|
||||
<map>
|
||||
<entry key="action_emptyWalletFragment_to_createNewWalletFragment">
|
||||
<value>
|
||||
<LayoutPositions />
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="action_emptyWalletFragment_to_pinRequestFragment">
|
||||
<value>
|
||||
<LayoutPositions />
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="action_emptyWalletFragment_to_verifyCard">
|
||||
<value>
|
||||
<LayoutPositions />
|
||||
</value>
|
||||
</entry>
|
||||
</map>
|
||||
</option>
|
||||
</LayoutPositions>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="idFragment">
|
||||
<value>
|
||||
<LayoutPositions>
|
||||
<option name="myPosition">
|
||||
<Point>
|
||||
<option name="x" value="-272" />
|
||||
<option name="y" value="1404" />
|
||||
</Point>
|
||||
</option>
|
||||
<option name="myPositions">
|
||||
<map>
|
||||
<entry key="action_idFragment_to_issueNewIdFragment">
|
||||
<value>
|
||||
<LayoutPositions />
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="action_idFragment_to_verifyCard">
|
||||
<value>
|
||||
<LayoutPositions />
|
||||
</value>
|
||||
</entry>
|
||||
</map>
|
||||
</option>
|
||||
</LayoutPositions>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="issueNewIdFragment">
|
||||
<value>
|
||||
<LayoutPositions>
|
||||
<option name="myPosition">
|
||||
<Point>
|
||||
<option name="x" value="-394" />
|
||||
<option name="y" value="1004" />
|
||||
</Point>
|
||||
</option>
|
||||
<option name="myPositions">
|
||||
<map>
|
||||
<entry key="action_issueNewIdFragment_to_cameraFragment">
|
||||
<value>
|
||||
<LayoutPositions />
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="action_issueNewIdFragment_to_validateIdFragment">
|
||||
<value>
|
||||
<LayoutPositions />
|
||||
</value>
|
||||
</entry>
|
||||
</map>
|
||||
</option>
|
||||
</LayoutPositions>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="loadedWalletFragment">
|
||||
<value>
|
||||
<LayoutPositions>
|
||||
<option name="myPosition">
|
||||
<Point>
|
||||
<option name="x" value="256" />
|
||||
<option name="y" value="1279" />
|
||||
</Point>
|
||||
</option>
|
||||
<option name="myPositions">
|
||||
<map>
|
||||
<entry key="action_loadedWalletFragment_to_pinRequestFragment">
|
||||
<value>
|
||||
<LayoutPositions />
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="action_loadedWalletFragment_to_pinSwapFragment">
|
||||
<value>
|
||||
<LayoutPositions />
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="action_loadedWalletFragment_to_prepareCryptonitWithdrawalFragment">
|
||||
<value>
|
||||
<LayoutPositions />
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="action_loadedWalletFragment_to_prepareKrakenWithdrawalFragment">
|
||||
<value>
|
||||
<LayoutPositions />
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="action_loadedWalletFragment_to_prepareTransactionFragment">
|
||||
<value>
|
||||
<LayoutPositions />
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="action_loadedWalletFragment_to_verifyCard">
|
||||
<value>
|
||||
<LayoutPositions />
|
||||
</value>
|
||||
</entry>
|
||||
</map>
|
||||
</option>
|
||||
</LayoutPositions>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="logoFragment">
|
||||
<value>
|
||||
<LayoutPositions>
|
||||
<option name="myPosition">
|
||||
<Point>
|
||||
<option name="x" value="256" />
|
||||
<option name="y" value="2390" />
|
||||
</Point>
|
||||
</option>
|
||||
<option name="myPositions">
|
||||
<map>
|
||||
<entry key="action_logoFragment_to_main">
|
||||
<value>
|
||||
<LayoutPositions />
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="action_logoFragment_to_prepareTransactionFragment">
|
||||
<value>
|
||||
<LayoutPositions />
|
||||
</value>
|
||||
</entry>
|
||||
</map>
|
||||
</option>
|
||||
</LayoutPositions>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="main">
|
||||
<value>
|
||||
<LayoutPositions>
|
||||
<option name="myPosition">
|
||||
<Point>
|
||||
<option name="x" value="12" />
|
||||
<option name="y" value="1279" />
|
||||
</Point>
|
||||
</option>
|
||||
<option name="myPositions">
|
||||
<map>
|
||||
<entry key="action_main_to_emptyWalletFragment">
|
||||
<value>
|
||||
<LayoutPositions />
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="action_main_to_idFragment">
|
||||
<value>
|
||||
<LayoutPositions />
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="action_main_to_loadedWalletFragment">
|
||||
<value>
|
||||
<LayoutPositions />
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="action_main_to_logoFragment">
|
||||
<value>
|
||||
<LayoutPositions />
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="action_main_to_pinRequestFragment">
|
||||
<value>
|
||||
<LayoutPositions />
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="action_main_to_pinSaveFragment">
|
||||
<value>
|
||||
<LayoutPositions />
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="action_main_to_settingsFragment">
|
||||
<value>
|
||||
<LayoutPositions />
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="action_main_to_tagFragment">
|
||||
<value>
|
||||
<LayoutPositions />
|
||||
</value>
|
||||
</entry>
|
||||
</map>
|
||||
</option>
|
||||
</LayoutPositions>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="pinRequestFragment">
|
||||
<value>
|
||||
<LayoutPositions>
|
||||
<option name="myPosition">
|
||||
<Point>
|
||||
<option name="x" value="988" />
|
||||
<option name="y" value="12" />
|
||||
</Point>
|
||||
</option>
|
||||
</LayoutPositions>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="pinSaveFragment">
|
||||
<value>
|
||||
<LayoutPositions>
|
||||
<option name="myPosition">
|
||||
<Point>
|
||||
<option name="x" value="256" />
|
||||
<option name="y" value="461" />
|
||||
</Point>
|
||||
</option>
|
||||
</LayoutPositions>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="pinSwapFragment">
|
||||
<value>
|
||||
<LayoutPositions>
|
||||
<option name="myPosition">
|
||||
<Point>
|
||||
<option name="x" value="744" />
|
||||
<option name="y" value="1039" />
|
||||
</Point>
|
||||
</option>
|
||||
</LayoutPositions>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="prepareCryptonitOtherApiWithdrawalFragment">
|
||||
<value>
|
||||
<LayoutPositions>
|
||||
<option name="myPosition">
|
||||
<Point>
|
||||
<option name="x" value="500" />
|
||||
<option name="y" value="1992" />
|
||||
</Point>
|
||||
</option>
|
||||
<option name="myPositions">
|
||||
<map>
|
||||
<entry key="action_prepareCryptonitOtherApiWithdrawalFragment_to_qrScanFragment">
|
||||
<value>
|
||||
<LayoutPositions />
|
||||
</value>
|
||||
</entry>
|
||||
</map>
|
||||
</option>
|
||||
</LayoutPositions>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="prepareCryptonitWithdrawalFragment">
|
||||
<value>
|
||||
<LayoutPositions>
|
||||
<option name="myPosition">
|
||||
<Point>
|
||||
<option name="x" value="500" />
|
||||
<option name="y" value="1618" />
|
||||
</Point>
|
||||
</option>
|
||||
</LayoutPositions>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="prepareKrakenWithdrawalFragment">
|
||||
<value>
|
||||
<LayoutPositions>
|
||||
<option name="myPosition">
|
||||
<Point>
|
||||
<option name="x" value="500" />
|
||||
<option name="y" value="1261" />
|
||||
</Point>
|
||||
</option>
|
||||
</LayoutPositions>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="prepareTransactionFragment">
|
||||
<value>
|
||||
<LayoutPositions>
|
||||
<option name="myPosition">
|
||||
<Point>
|
||||
<option name="x" value="500" />
|
||||
<option name="y" value="2348" />
|
||||
</Point>
|
||||
</option>
|
||||
<option name="myPositions">
|
||||
<map>
|
||||
<entry key="action_prepareTransactionFragment_to_confirmTransactionFragment">
|
||||
<value>
|
||||
<LayoutPositions />
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="action_prepareTransactionFragment_to_qrScanFragment">
|
||||
<value>
|
||||
<LayoutPositions />
|
||||
</value>
|
||||
</entry>
|
||||
</map>
|
||||
</option>
|
||||
</LayoutPositions>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="purgeFragment">
|
||||
<value>
|
||||
<LayoutPositions>
|
||||
<option name="myPosition">
|
||||
<Point>
|
||||
<option name="x" value="744" />
|
||||
<option name="y" value="682" />
|
||||
</Point>
|
||||
</option>
|
||||
<option name="myPositions">
|
||||
<map>
|
||||
<entry key="action_purgeFragment_to_mainFragment">
|
||||
<value>
|
||||
<LayoutPositions />
|
||||
</value>
|
||||
</entry>
|
||||
</map>
|
||||
</option>
|
||||
</LayoutPositions>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="qrScanFragment">
|
||||
<value>
|
||||
<LayoutPositions>
|
||||
<option name="myPosition">
|
||||
<Point>
|
||||
<option name="x" value="744" />
|
||||
<option name="y" value="2034" />
|
||||
</Point>
|
||||
</option>
|
||||
</LayoutPositions>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="sendTransactionFragment">
|
||||
<value>
|
||||
<LayoutPositions>
|
||||
<option name="myPosition">
|
||||
<Point>
|
||||
<option name="x" value="1232" />
|
||||
<option name="y" value="2433" />
|
||||
</Point>
|
||||
</option>
|
||||
</LayoutPositions>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="settingsFragment">
|
||||
<value>
|
||||
<LayoutPositions>
|
||||
<option name="myPosition">
|
||||
<Point>
|
||||
<option name="x" value="256" />
|
||||
<option name="y" value="923" />
|
||||
</Point>
|
||||
</option>
|
||||
</LayoutPositions>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="signTransactionFragment">
|
||||
<value>
|
||||
<LayoutPositions>
|
||||
<option name="myPosition">
|
||||
<Point>
|
||||
<option name="x" value="988" />
|
||||
<option name="y" value="2433" />
|
||||
</Point>
|
||||
</option>
|
||||
<option name="myPositions">
|
||||
<map>
|
||||
<entry key="action_signTransactionFragment_to_sendTransactionFragment">
|
||||
<value>
|
||||
<LayoutPositions />
|
||||
</value>
|
||||
</entry>
|
||||
</map>
|
||||
</option>
|
||||
</LayoutPositions>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="tagFragment">
|
||||
<value>
|
||||
<LayoutPositions>
|
||||
<option name="myPosition">
|
||||
<Point>
|
||||
<option name="x" value="256" />
|
||||
<option name="y" value="1635" />
|
||||
</Point>
|
||||
</option>
|
||||
</LayoutPositions>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="validateIdFragment">
|
||||
<value>
|
||||
<LayoutPositions>
|
||||
<option name="myPosition">
|
||||
<Point>
|
||||
<option name="x" value="-628" />
|
||||
<option name="y" value="1001" />
|
||||
</Point>
|
||||
</option>
|
||||
<option name="myPositions">
|
||||
<map>
|
||||
<entry key="action_validateIdFragment_to_writeIdFragment">
|
||||
<value>
|
||||
<LayoutPositions />
|
||||
</value>
|
||||
</entry>
|
||||
</map>
|
||||
</option>
|
||||
</LayoutPositions>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="verifyCard">
|
||||
<value>
|
||||
<LayoutPositions>
|
||||
<option name="myPosition">
|
||||
<Point>
|
||||
<option name="x" value="500" />
|
||||
<option name="y" value="860" />
|
||||
</Point>
|
||||
</option>
|
||||
<option name="myPositions">
|
||||
<map>
|
||||
<entry key="action_verifyCard_to_pinRequestFragment">
|
||||
<value>
|
||||
<LayoutPositions />
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="action_verifyCard_to_pinSwapFragment">
|
||||
<value>
|
||||
<LayoutPositions />
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="action_verifyCard_to_purgeFragment">
|
||||
<value>
|
||||
<LayoutPositions />
|
||||
</value>
|
||||
</entry>
|
||||
</map>
|
||||
</option>
|
||||
</LayoutPositions>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="writeIdFragment">
|
||||
<value>
|
||||
<LayoutPositions>
|
||||
<option name="myPosition">
|
||||
<Point>
|
||||
<option name="x" value="-661" />
|
||||
<option name="y" value="701" />
|
||||
</Point>
|
||||
</option>
|
||||
</LayoutPositions>
|
||||
</value>
|
||||
</entry>
|
||||
</map>
|
||||
</option>
|
||||
</LayoutPositions>
|
||||
</value>
|
||||
</entry>
|
||||
</map>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
||||
|
|
@ -11,18 +11,18 @@ public enum Blockchain {
|
|||
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"),
|
||||
Ethereum("ETH", "ETH", 1.0, R.drawable.ic_logo_ethereum, "Ethereum"),
|
||||
EthereumId("ETH/ID", "ETH", 1.0, R.drawable.ic_logo_ethereum, "Ethereum ID"),
|
||||
EthereumId("ETH/ID", "ETH", 1.0, R.drawable.ic_logo_ethereum, "Ethereum"),
|
||||
EthereumTestNet("ETH/test", "ETH", 1.0, R.drawable.ic_logo_ethereum_testnet, "Ethereum Testnet"),
|
||||
Token("Token", "ETH", 1.0, R.drawable.ic_logo_bat_token, "Ethereum"),
|
||||
NftToken("NftToken", "", 1.0, R.drawable.tangem2, "Ethereum"),
|
||||
Token("Token", "ETH", 1.0, R.drawable.ic_logo_ethereum, "Ethereum"),
|
||||
NftToken("NftToken", "", 1.0, R.drawable.ic_logo_ethereum, "Ethereum"),
|
||||
BitcoinCash("BCH", "BCH", 100000000.0, R.drawable.ic_logo_bitcoin_cash, "Bitcoin Cash"),
|
||||
Litecoin("LTC", "LTC", 100000000.0, R.drawable.tangem2, "Litecoin"),
|
||||
Litecoin("LTC", "LTC", 100000000.0, R.drawable.ic_logo_litecoin, "Litecoin"),
|
||||
Rootstock("RSK", "RBTC", 1.0, R.drawable.tangem2, "RSK"),
|
||||
RootstockToken("RskToken", "RBTC", 1.0, R.drawable.tangem2, "RSK"),
|
||||
Cardano("CARDANO", "ADA", 1000000.0, R.drawable.tangem2, "Cardano"),
|
||||
Ripple ("XRP", "XRP", 1000000.0, R.drawable.tangem2, "XRP"),
|
||||
Binance("BINANCE", "BNB", 100000000.0, R.drawable.tangem2, "Binance"),
|
||||
BinanceTestNet("BINANCE/test", "BNB", 100000000.0, R.drawable.tangem2, "Binance Testnet"),
|
||||
Ripple ("XRP", "XRP", 1000000.0, R.drawable.ic_logo_xrp, "XRP"),
|
||||
Binance("BINANCE", "BNB", 100000000.0, R.drawable.ic_logo_binance, "Binance"),
|
||||
BinanceTestNet("BINANCE/test", "BNB", 100000000.0, R.drawable.ic_logo_binance, "Binance Testnet"),
|
||||
Matic("MATIC", "MTX", 1.0, R.drawable.tangem2, "Matic"),
|
||||
MaticTestNet("MATIC/test", "MTX", 1.0, R.drawable.tangem2, "Matic Testnet"),
|
||||
Stellar("XLM", "XLM", 10000000.0, R.drawable.ic_logo_stellar, "Stellar"),
|
||||
|
|
@ -31,7 +31,7 @@ public enum Blockchain {
|
|||
StellarTag("XLM-Tag", "XLM", 1000000.0, R.drawable.ic_logo_stellar, "Stellar"),
|
||||
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.tangem2, "Tezos");
|
||||
Tezos("TEZOS", "XTZ", 10000000.0, R.drawable.ic_logo_tezos, "Tezos");
|
||||
|
||||
Blockchain(String ID, String currency, double multiplier, int imageResource, String officialName) {
|
||||
mID = ID;
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ class MainActivity : AppCompatActivity(), NfcAdapter.ReaderCallback {
|
|||
|
||||
private fun navigateSafelyToMainFragment() {
|
||||
try {
|
||||
findNavController(R.id.nav_host_fragment).navigate(R.id.main)
|
||||
findNavController(R.id.nav_host_fragment).popBackStack()
|
||||
} catch (e: IllegalArgumentException) {
|
||||
Log.w(this::class.java.simpleName, e.message)
|
||||
} catch (e: IllegalStateException) {
|
||||
|
|
|
|||
|
|
@ -481,7 +481,6 @@ public class EthEngine extends CoinEngine {
|
|||
BigInteger l = new BigInteger(balanceCap, 16);
|
||||
coinData.setBalanceReceived(true);
|
||||
coinData.setBalanceInInternalUnits(new CoinEngine.InternalAmount(l, "wei"));
|
||||
|
||||
// Log.i("$TAG eth_get_balance", balanceCap)
|
||||
}
|
||||
break;
|
||||
|
|
@ -491,8 +490,9 @@ public class EthEngine extends CoinEngine {
|
|||
nonce = nonce.substring(2);
|
||||
BigInteger count = new BigInteger(nonce, 16);
|
||||
coinData.setConfirmedTXCount(count);
|
||||
|
||||
|
||||
if (serverApiInfura.isRequestsSequenceCompleted()) { //getting balances after checking for pending to avoid showing old balance as verified
|
||||
serverApiInfura.requestData(ServerApiInfura.INFURA_ETH_GET_BALANCE, 67, coinData.getWallet(), "", "");
|
||||
}
|
||||
// Log.i("$TAG eth_getTransCount", nonce)
|
||||
}
|
||||
break;
|
||||
|
|
@ -502,7 +502,9 @@ public class EthEngine extends CoinEngine {
|
|||
pending = pending.substring(2);
|
||||
BigInteger count = new BigInteger(pending, 16);
|
||||
coinData.setUnconfirmedTXCount(count);
|
||||
|
||||
if (serverApiInfura.isRequestsSequenceCompleted()) { //getting balances after checking for pending to avoid showing old balance as verified
|
||||
serverApiInfura.requestData(ServerApiInfura.INFURA_ETH_GET_BALANCE, 67, coinData.getWallet(), "", "");
|
||||
}
|
||||
// Log.i("$TAG eth_getPendingTxCount", pending)
|
||||
}
|
||||
break;
|
||||
|
|
@ -582,7 +584,6 @@ public class EthEngine extends CoinEngine {
|
|||
};
|
||||
serverApiBlockcypher.setResponseListener(blockcypherListener);
|
||||
|
||||
serverApiInfura.requestData(ServerApiInfura.INFURA_ETH_GET_BALANCE, 67, coinData.getWallet(), "", "");
|
||||
serverApiInfura.requestData(ServerApiInfura.INFURA_ETH_GET_TRANSACTION_COUNT, 67, coinData.getWallet(), "", "");
|
||||
serverApiInfura.requestData(ServerApiInfura.INFURA_ETH_GET_PENDING_COUNT, 67, coinData.getWallet(), "", "");
|
||||
|
||||
|
|
|
|||
|
|
@ -109,7 +109,6 @@ public class MaticTokenEngine extends TokenEngine {
|
|||
BigInteger l = new BigInteger(balanceCap, 16);
|
||||
coinData.setBalanceReceived(true);
|
||||
coinData.setBalanceAlterInInternalUnits(new CoinEngine.InternalAmount(l, "wei"));
|
||||
|
||||
// Log.i("$TAG eth_get_balance", balanceCap)
|
||||
}
|
||||
break;
|
||||
|
|
@ -119,8 +118,10 @@ public class MaticTokenEngine extends TokenEngine {
|
|||
nonce = nonce.substring(2);
|
||||
BigInteger count = new BigInteger(nonce, 16);
|
||||
coinData.setConfirmedTXCount(count);
|
||||
|
||||
|
||||
if (serverApiMatic.isRequestsSequenceCompleted()) { //getting balances after checking for pending to avoid showing old balance as verified
|
||||
serverApiMatic.requestData(ServerApiMatic.MATIC_ETH_CALL, 67, coinData.getWallet(), getContractAddress(ctx.getCard()), "");
|
||||
serverApiMatic.requestData(ServerApiMatic.MATIC_ETH_GET_BALANCE, 67, coinData.getWallet(), "", "");
|
||||
}
|
||||
// Log.i("$TAG eth_getTransCount", nonce)
|
||||
}
|
||||
break;
|
||||
|
|
@ -130,7 +131,10 @@ public class MaticTokenEngine extends TokenEngine {
|
|||
pending = pending.substring(2);
|
||||
BigInteger count = new BigInteger(pending, 16);
|
||||
coinData.setUnconfirmedTXCount(count);
|
||||
|
||||
if (serverApiMatic.isRequestsSequenceCompleted()) { //getting balances after checking for pending to avoid showing old balance as verified
|
||||
serverApiMatic.requestData(ServerApiMatic.MATIC_ETH_CALL, 67, coinData.getWallet(), getContractAddress(ctx.getCard()), "");
|
||||
serverApiMatic.requestData(ServerApiMatic.MATIC_ETH_GET_BALANCE, 67, coinData.getWallet(), "", "");
|
||||
}
|
||||
// Log.i("$TAG eth_getPendingTxCount", pending)
|
||||
}
|
||||
break;
|
||||
|
|
@ -143,16 +147,6 @@ public class MaticTokenEngine extends TokenEngine {
|
|||
BigInteger l = new BigInteger(balanceCap, 16);
|
||||
coinData.setBalanceInInternalUnits(new CoinEngine.InternalAmount(l, ctx.getCard().tokenSymbol));
|
||||
// Log.i("$TAG eth_call", balanceCap)
|
||||
|
||||
|
||||
if (blockchainRequestsCallbacks.allowAdvance()) {
|
||||
serverApiMatic.requestData(ServerApiMatic.MATIC_ETH_GET_BALANCE, 67, coinData.getWallet(), "", "");
|
||||
serverApiMatic.requestData(ServerApiMatic.MATIC_ETH_GET_TRANSACTION_COUNT, 67, coinData.getWallet(), "", "");
|
||||
serverApiMatic.requestData(ServerApiMatic.MATIC_ETH_GET_PENDING_COUNT, 67, coinData.getWallet(), "", "");
|
||||
} else {
|
||||
ctx.setError("Terminated by user");
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
|
@ -181,7 +175,8 @@ public class MaticTokenEngine extends TokenEngine {
|
|||
serverApiMatic.setResponseListener(responseListener);
|
||||
|
||||
if (validateAddress(getContractAddress(ctx.getCard()))) {
|
||||
serverApiMatic.requestData(ServerApiMatic.MATIC_ETH_CALL, 67, coinData.getWallet(), getContractAddress(ctx.getCard()), "");
|
||||
serverApiMatic.requestData(ServerApiMatic.MATIC_ETH_GET_TRANSACTION_COUNT, 67, coinData.getWallet(), "", "");
|
||||
serverApiMatic.requestData(ServerApiMatic.MATIC_ETH_GET_PENDING_COUNT, 67, coinData.getWallet(), "", "");
|
||||
} else {
|
||||
ctx.setError("Smart contract address not defined");
|
||||
blockchainRequestsCallbacks.onComplete(false);
|
||||
|
|
|
|||
|
|
@ -85,8 +85,9 @@ public class RskEngine extends EthEngine {
|
|||
nonce = nonce.substring(2);
|
||||
BigInteger count = new BigInteger(nonce, 16);
|
||||
coinData.setConfirmedTXCount(count);
|
||||
|
||||
|
||||
if (serverApiRootstock.isRequestsSequenceCompleted()) {
|
||||
serverApiRootstock.requestData(ServerApiRootstock.ROOTSTOCK_ETH_GET_BALANCE, 67, coinData.getWallet(), "", "");
|
||||
}
|
||||
// Log.i("$TAG eth_getTransCount", nonce)
|
||||
}
|
||||
break;
|
||||
|
|
@ -96,7 +97,9 @@ public class RskEngine extends EthEngine {
|
|||
pending = pending.substring(2);
|
||||
BigInteger count = new BigInteger(pending, 16);
|
||||
coinData.setUnconfirmedTXCount(count);
|
||||
|
||||
if (serverApiRootstock.isRequestsSequenceCompleted()) {
|
||||
serverApiRootstock.requestData(ServerApiRootstock.ROOTSTOCK_ETH_GET_BALANCE, 67, coinData.getWallet(), "", "");
|
||||
}
|
||||
// Log.i("$TAG eth_getPendingTxCount", pending)
|
||||
}
|
||||
break;
|
||||
|
|
@ -122,7 +125,6 @@ public class RskEngine extends EthEngine {
|
|||
};
|
||||
serverApiRootstock.setResponseListener(responseListener);
|
||||
|
||||
serverApiRootstock.requestData(ServerApiRootstock.ROOTSTOCK_ETH_GET_BALANCE, 67, coinData.getWallet(), "", "");
|
||||
serverApiRootstock.requestData(ServerApiRootstock.ROOTSTOCK_ETH_GET_TRANSACTION_COUNT, 67, coinData.getWallet(), "", "");
|
||||
serverApiRootstock.requestData(ServerApiRootstock.ROOTSTOCK_ETH_GET_PENDING_COUNT, 67, coinData.getWallet(), "", "");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -82,7 +82,6 @@ public class RskTokenEngine extends TokenEngine {
|
|||
BigInteger l = new BigInteger(balanceCap, 16);
|
||||
coinData.setBalanceReceived(true);
|
||||
coinData.setBalanceAlterInInternalUnits(new CoinEngine.InternalAmount(l, "wei"));
|
||||
|
||||
// Log.i("$TAG eth_get_balance", balanceCap)
|
||||
}
|
||||
break;
|
||||
|
|
@ -92,8 +91,10 @@ public class RskTokenEngine extends TokenEngine {
|
|||
nonce = nonce.substring(2);
|
||||
BigInteger count = new BigInteger(nonce, 16);
|
||||
coinData.setConfirmedTXCount(count);
|
||||
|
||||
|
||||
if (serverApiRootstock.isRequestsSequenceCompleted()) { //getting balances after checking for pending to avoid showing old balance as verified
|
||||
serverApiRootstock.requestData(ServerApiRootstock.ROOTSTOCK_ETH_CALL, 67, coinData.getWallet(), getContractAddress(ctx.getCard()), "");
|
||||
serverApiRootstock.requestData(ServerApiRootstock.ROOTSTOCK_ETH_GET_BALANCE, 67, coinData.getWallet(), "", "");
|
||||
}
|
||||
// Log.i("$TAG eth_getTransCount", nonce)
|
||||
}
|
||||
break;
|
||||
|
|
@ -103,7 +104,10 @@ public class RskTokenEngine extends TokenEngine {
|
|||
pending = pending.substring(2);
|
||||
BigInteger count = new BigInteger(pending, 16);
|
||||
coinData.setUnconfirmedTXCount(count);
|
||||
|
||||
if (serverApiRootstock.isRequestsSequenceCompleted()) { //getting balances after checking for pending to avoid showing old balance as verified
|
||||
serverApiRootstock.requestData(ServerApiRootstock.ROOTSTOCK_ETH_CALL, 67, coinData.getWallet(), getContractAddress(ctx.getCard()), "");
|
||||
serverApiRootstock.requestData(ServerApiRootstock.ROOTSTOCK_ETH_GET_BALANCE, 67, coinData.getWallet(), "", "");
|
||||
}
|
||||
// Log.i("$TAG eth_getPendingTxCount", pending)
|
||||
}
|
||||
break;
|
||||
|
|
@ -115,15 +119,6 @@ public class RskTokenEngine extends TokenEngine {
|
|||
BigInteger l = new BigInteger(balanceCap, 16);
|
||||
coinData.setBalanceInInternalUnits(new CoinEngine.InternalAmount(l, ctx.getCard().tokenSymbol));
|
||||
// Log.i("$TAG eth_call", balanceCap)
|
||||
|
||||
if (blockchainRequestsCallbacks.allowAdvance()) {
|
||||
serverApiRootstock.requestData(ServerApiRootstock.ROOTSTOCK_ETH_GET_BALANCE, 67, coinData.getWallet(), "", "");
|
||||
serverApiRootstock.requestData(ServerApiRootstock.ROOTSTOCK_ETH_GET_TRANSACTION_COUNT, 67, coinData.getWallet(), "", "");
|
||||
serverApiRootstock.requestData(ServerApiRootstock.ROOTSTOCK_ETH_GET_PENDING_COUNT, 67, coinData.getWallet(), "", "");
|
||||
} else {
|
||||
ctx.setError("Terminated by user");
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
|
@ -152,7 +147,8 @@ public class RskTokenEngine extends TokenEngine {
|
|||
serverApiRootstock.setResponseListener(responseListener);
|
||||
|
||||
if (validateAddress(getContractAddress(ctx.getCard()))) {
|
||||
serverApiRootstock.requestData(ServerApiRootstock.ROOTSTOCK_ETH_CALL, 67, coinData.getWallet(), getContractAddress(ctx.getCard()), "");
|
||||
serverApiRootstock.requestData(ServerApiRootstock.ROOTSTOCK_ETH_GET_TRANSACTION_COUNT, 67, coinData.getWallet(), "", "");
|
||||
serverApiRootstock.requestData(ServerApiRootstock.ROOTSTOCK_ETH_GET_PENDING_COUNT, 67, coinData.getWallet(), "", "");
|
||||
} else {
|
||||
ctx.setError("Smart contract address not defined");
|
||||
blockchainRequestsCallbacks.onComplete(false);
|
||||
|
|
|
|||
|
|
@ -655,7 +655,6 @@ public class TokenEngine extends CoinEngine {
|
|||
BigInteger l = new BigInteger(balanceCap, 16);
|
||||
coinData.setBalanceReceived(true);
|
||||
coinData.setBalanceAlterInInternalUnits(new CoinEngine.InternalAmount(l, "wei"));
|
||||
|
||||
// Log.i("$TAG eth_get_balance", balanceCap)
|
||||
}
|
||||
break;
|
||||
|
|
@ -665,8 +664,10 @@ public class TokenEngine extends CoinEngine {
|
|||
nonce = nonce.substring(2);
|
||||
BigInteger count = new BigInteger(nonce, 16);
|
||||
coinData.setConfirmedTXCount(count);
|
||||
|
||||
|
||||
if (serverApiInfura.isRequestsSequenceCompleted()) { //getting balances after checking for pending to avoid showing old balance as verified
|
||||
serverApiInfura.requestData(ServerApiInfura.INFURA_ETH_CALL, 67, coinData.getWallet(), getContractAddress(ctx.getCard()), "");
|
||||
serverApiInfura.requestData(ServerApiInfura.INFURA_ETH_GET_BALANCE, 67, coinData.getWallet(), "", "");
|
||||
}
|
||||
// Log.i("$TAG eth_getTransCount", nonce)
|
||||
}
|
||||
break;
|
||||
|
|
@ -676,29 +677,21 @@ public class TokenEngine extends CoinEngine {
|
|||
pending = pending.substring(2);
|
||||
BigInteger count = new BigInteger(pending, 16);
|
||||
coinData.setUnconfirmedTXCount(count);
|
||||
|
||||
if (serverApiInfura.isRequestsSequenceCompleted()) { //getting balances after checking for pending to avoid showing old balance as verified
|
||||
serverApiInfura.requestData(ServerApiInfura.INFURA_ETH_CALL, 67, coinData.getWallet(), getContractAddress(ctx.getCard()), "");
|
||||
serverApiInfura.requestData(ServerApiInfura.INFURA_ETH_GET_BALANCE, 67, coinData.getWallet(), "", "");
|
||||
}
|
||||
// Log.i("$TAG eth_getPendingTxCount", pending)
|
||||
}
|
||||
break;
|
||||
//
|
||||
case ServerApiInfura.INFURA_ETH_CALL: {
|
||||
try {
|
||||
|
||||
String balanceCap = infuraResponse.getResult();
|
||||
balanceCap = balanceCap.substring(2);
|
||||
BigInteger l = new BigInteger(balanceCap, 16);
|
||||
coinData.setBalanceInInternalUnits(new CoinEngine.InternalAmount(l, ctx.getCard().tokenSymbol));
|
||||
// Log.i("$TAG eth_call", balanceCap)
|
||||
|
||||
|
||||
if (blockchainRequestsCallbacks.allowAdvance()) {
|
||||
serverApiInfura.requestData(ServerApiInfura.INFURA_ETH_GET_BALANCE, 67, coinData.getWallet(), "", "");
|
||||
serverApiInfura.requestData(ServerApiInfura.INFURA_ETH_GET_TRANSACTION_COUNT, 67, coinData.getWallet(), "", "");
|
||||
serverApiInfura.requestData(ServerApiInfura.INFURA_ETH_GET_PENDING_COUNT, 67, coinData.getWallet(), "", "");
|
||||
} else {
|
||||
ctx.setError("Terminated by user");
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
|
@ -706,7 +699,7 @@ public class TokenEngine extends CoinEngine {
|
|||
break;
|
||||
|
||||
}
|
||||
if (serverApiInfura.isRequestsSequenceCompleted()&& serverApiBlockcypher.isRequestsSequenceCompleted()) {
|
||||
if (serverApiInfura.isRequestsSequenceCompleted() && serverApiBlockcypher.isRequestsSequenceCompleted()) {
|
||||
blockchainRequestsCallbacks.onComplete(!ctx.hasError());
|
||||
} else {
|
||||
blockchainRequestsCallbacks.onProgress();
|
||||
|
|
@ -771,13 +764,13 @@ public class TokenEngine extends CoinEngine {
|
|||
serverApiBlockcypher.setResponseListener(blockcypherListener);
|
||||
|
||||
if (validateAddress(getContractAddress(ctx.getCard()))) {
|
||||
serverApiInfura.requestData(ServerApiInfura.INFURA_ETH_CALL, 67, coinData.getWallet(), getContractAddress(ctx.getCard()), "");
|
||||
serverApiInfura.requestData(ServerApiInfura.INFURA_ETH_GET_TRANSACTION_COUNT, 67, coinData.getWallet(), "", "");
|
||||
serverApiInfura.requestData(ServerApiInfura.INFURA_ETH_GET_PENDING_COUNT, 67, coinData.getWallet(), "", "");
|
||||
serverApiBlockcypher.requestData(ctx.getBlockchain().getID(), ServerApiBlockcypher.BLOCKCYPHER_ADDRESS, ctx.getCoinData().getWallet(), "");
|
||||
} else {
|
||||
ctx.setError("Smart contract address not defined");
|
||||
blockchainRequestsCallbacks.onComplete(false);
|
||||
}
|
||||
|
||||
serverApiBlockcypher.requestData(ctx.getBlockchain().getID(), ServerApiBlockcypher.BLOCKCYPHER_ADDRESS, ctx.getCoinData().getWallet(), "");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -109,14 +109,14 @@ public class XlmAssetEngine extends CoinEngine {
|
|||
@Override
|
||||
public boolean isBalanceNotZero() {
|
||||
if (coinData == null) return false;
|
||||
if (coinData.getXlmBalance() == null) return false;
|
||||
return coinData.getXlmBalance().notZero();
|
||||
return (coinData.getXlmBalance() != null && coinData.getXlmBalance().notZero() &&
|
||||
coinData.getAssetBalance() != null && coinData.getAssetBalance().notZero());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasBalanceInfo() {
|
||||
if (coinData == null) return false;
|
||||
return coinData.getXlmBalance() != null;
|
||||
return (coinData.getXlmBalance() != null && coinData.getAssetBalance() != null) || (coinData.isError404());
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -122,6 +122,6 @@ public class XrpData extends CoinData {
|
|||
}
|
||||
|
||||
public boolean hasUnconfirmed() {
|
||||
return !balanceConfirmed.equals(balanceUnconfirmed);
|
||||
return hasBalanceInfo() && !balanceConfirmed.equals(balanceUnconfirmed);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,12 @@ fun String.calculateSha256(): ByteArray {
|
|||
return sha256.digest(data)
|
||||
}
|
||||
|
||||
fun String.calculateSha512(): ByteArray {
|
||||
val sha = MessageDigest.getInstance("SHA-512")
|
||||
val data = this.toByteArray(Charset.forName("UTF-8"))
|
||||
return sha.digest(data)
|
||||
}
|
||||
|
||||
fun String.hexToBytes(): ByteArray {
|
||||
return ByteArray(this.length / 2)
|
||||
{ i ->
|
||||
|
|
|
|||
|
|
@ -3,8 +3,7 @@ apply plugin: 'kotlin-android'
|
|||
apply plugin: 'kotlin-android-extensions'
|
||||
android {
|
||||
compileSdkVersion 29
|
||||
buildToolsVersion "29.0.2"
|
||||
|
||||
buildToolsVersion "29.0.3"
|
||||
|
||||
defaultConfig {
|
||||
applicationId "com.tangem.tangemtest"
|
||||
|
|
@ -34,13 +33,23 @@ android {
|
|||
dependencies {
|
||||
implementation project(':tangem-core')
|
||||
implementation project(':tangem-sdk')
|
||||
|
||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$versions.kotlin"
|
||||
implementation 'androidx.appcompat:appcompat:1.1.0'
|
||||
implementation 'androidx.core:core-ktx:1.2.0'
|
||||
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
|
||||
|
||||
testImplementation 'junit:junit:4.12'
|
||||
androidTestImplementation 'androidx.test:runner:1.2.0'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
|
||||
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$versions.kotlin"
|
||||
|
||||
implementation 'androidx.appcompat:appcompat:1.1.0'
|
||||
implementation 'androidx.core:core-ktx:1.2.0'
|
||||
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 "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'
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,8 +8,8 @@
|
|||
<uses-permission android:name="android.permission.NFC" />
|
||||
|
||||
|
||||
|
||||
<application
|
||||
android:name=".AppTangemDemo"
|
||||
android:allowBackup="true"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
|
|
@ -17,15 +17,15 @@
|
|||
android:supportsRtl="true"
|
||||
android:theme="@style/AppTheme">
|
||||
|
||||
<activity android:name=".MainActivity">
|
||||
<activity android:name="._main.MainActivity">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.nfc.action.NDEF_DISCOVERED" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
|
||||
<data
|
||||
android:host="www.tangem.com"
|
||||
android:scheme="http" />
|
||||
|
|
|
|||
|
|
@ -0,0 +1,29 @@
|
|||
package com.tangem.tangemtest
|
||||
|
||||
import android.app.Application
|
||||
import com.tangem.tangemtest._arch.structure.ILog
|
||||
import com.tangem.tangemtest._arch.structure.ItemLogger
|
||||
import com.tangem.tangemtest.commons.TangemLogger
|
||||
import ru.dev.gbixahue.eu4d.lib.android.global.log.Log
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
class AppTangemDemo : Application() {
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
|
||||
AppTangemDemo.appInstance = this
|
||||
setupLoggers()
|
||||
}
|
||||
|
||||
private fun setupLoggers() {
|
||||
Log.setLogger(TangemLogger())
|
||||
ILog.setLogger(ItemLogger())
|
||||
}
|
||||
|
||||
companion object {
|
||||
lateinit var appInstance: AppTangemDemo
|
||||
}
|
||||
}
|
||||
|
|
@ -5,12 +5,13 @@ import android.os.Bundle
|
|||
import androidx.appcompat.app.AppCompatActivity
|
||||
import com.tangem.CardManager
|
||||
import com.tangem.tangem_sdk_new.extensions.init
|
||||
import com.tangem.tangemtest.extensions.create
|
||||
import com.tangem.tasks.ScanEvent
|
||||
import com.tangem.tasks.TaskError
|
||||
import com.tangem.tasks.TaskEvent
|
||||
import kotlinx.android.synthetic.main.activity_main.*
|
||||
import kotlinx.android.synthetic.main.old_activity_main.*
|
||||
|
||||
class MainActivity : AppCompatActivity() {
|
||||
class Old_MainActivity : AppCompatActivity() {
|
||||
|
||||
private lateinit var cardManager: CardManager
|
||||
private lateinit var cardId: String
|
||||
|
|
@ -20,7 +21,7 @@ class MainActivity : AppCompatActivity() {
|
|||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_main)
|
||||
setContentView(R.layout.old_activity_main)
|
||||
|
||||
cardManager = CardManager.init(this)
|
||||
|
||||
|
|
@ -19,162 +19,162 @@ import java.nio.charset.StandardCharsets
|
|||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
class TestUserDataActivity: AppCompatActivity() {
|
||||
class TestUserDataActivity : AppCompatActivity() {
|
||||
|
||||
private lateinit var cardManager: CardManager
|
||||
private lateinit var writeOptions: WriteOptions
|
||||
private lateinit var cardManager: CardManager
|
||||
private lateinit var writeOptions: WriteOptions
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_test_user_data)
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_test_user_data)
|
||||
|
||||
init()
|
||||
initWriteOptions()
|
||||
}
|
||||
|
||||
private fun init() {
|
||||
cardManager = CardManager.init(this)
|
||||
|
||||
btn_scan?.setOnClickListener { _ ->
|
||||
cardManager.scanCard { taskEvent ->
|
||||
when (taskEvent) {
|
||||
is TaskEvent.Event -> {
|
||||
when (taskEvent.data) {
|
||||
is ScanEvent.OnReadEvent -> {
|
||||
// Handle returned card data
|
||||
writeOptions.cardId = (taskEvent.data as ScanEvent.OnReadEvent).card.cardId
|
||||
runOnUiThread { showReadWriteSection(true) }
|
||||
}
|
||||
is ScanEvent.OnVerifyEvent -> {
|
||||
//Handle card verification
|
||||
}
|
||||
}
|
||||
}
|
||||
is TaskEvent.Completion -> {
|
||||
if (taskEvent.error != null) {
|
||||
if (taskEvent.error is TaskError.UserCancelled) {
|
||||
// Handle case when user cancelled manually
|
||||
}
|
||||
// Handle other errors
|
||||
}
|
||||
// Handle completion
|
||||
}
|
||||
}
|
||||
}
|
||||
init()
|
||||
initWriteOptions()
|
||||
}
|
||||
|
||||
btn_write.setOnClickListener {
|
||||
if (writeOptions.cardId == null) return@setOnClickListener
|
||||
private fun init() {
|
||||
cardManager = CardManager.init(this)
|
||||
|
||||
cardManager.writeUserData(
|
||||
writeOptions.cardId !!,
|
||||
writeOptions.userData,
|
||||
writeOptions.userProtectedData,
|
||||
writeOptions.userCounter,
|
||||
writeOptions.userProtectedCounter
|
||||
) {
|
||||
when (it) {
|
||||
is TaskEvent.Completion -> handleError(tv_write_result, it.error)
|
||||
is TaskEvent.Event -> {
|
||||
runOnUiThread {
|
||||
val data = it.data as? WriteUserDataResponse
|
||||
if (data == null) {
|
||||
tv_write_result.text = "Response doesn't match"
|
||||
return@runOnUiThread
|
||||
}
|
||||
tv_write_result?.text = "Success"
|
||||
btn_scan?.setOnClickListener { _ ->
|
||||
cardManager.scanCard { taskEvent ->
|
||||
when (taskEvent) {
|
||||
is TaskEvent.Event -> {
|
||||
when (taskEvent.data) {
|
||||
is ScanEvent.OnReadEvent -> {
|
||||
// Handle returned card data
|
||||
writeOptions.cardId = (taskEvent.data as ScanEvent.OnReadEvent).card.cardId
|
||||
runOnUiThread { showReadWriteSection(true) }
|
||||
}
|
||||
is ScanEvent.OnVerifyEvent -> {
|
||||
//Handle card verification
|
||||
}
|
||||
}
|
||||
}
|
||||
is TaskEvent.Completion -> {
|
||||
if (taskEvent.error != null) {
|
||||
if (taskEvent.error is TaskError.UserCancelled) {
|
||||
// Handle case when user cancelled manually
|
||||
}
|
||||
// Handle other errors
|
||||
}
|
||||
// Handle completion
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
btn_write.setOnClickListener {
|
||||
if (writeOptions.cardId == null) return@setOnClickListener
|
||||
|
||||
cardManager.writeUserData(
|
||||
writeOptions.cardId!!,
|
||||
writeOptions.userData,
|
||||
writeOptions.userProtectedData,
|
||||
writeOptions.userCounter,
|
||||
writeOptions.userProtectedCounter
|
||||
) {
|
||||
when (it) {
|
||||
is TaskEvent.Completion -> handleError(tv_write_result, it.error)
|
||||
is TaskEvent.Event -> {
|
||||
runOnUiThread {
|
||||
val data = it.data as? WriteUserDataResponse
|
||||
if (data == null) {
|
||||
tv_write_result.text = "Response doesn't match"
|
||||
return@runOnUiThread
|
||||
}
|
||||
tv_write_result?.text = "Success"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
btn_read.setOnClickListener {
|
||||
if (writeOptions.cardId == null) return@setOnClickListener
|
||||
|
||||
cardManager.readUserData(writeOptions.cardId!!) {
|
||||
when (it) {
|
||||
is TaskEvent.Completion -> handleError(tv_read_result, it.error)
|
||||
is TaskEvent.Event -> {
|
||||
runOnUiThread {
|
||||
val data = it.data as? ReadUserDataResponse
|
||||
if (data == null) {
|
||||
tv_read_result.text = "Response doesn't match"
|
||||
return@runOnUiThread
|
||||
}
|
||||
tv_read_result?.text = "Success"
|
||||
|
||||
writeOptions.userData = data.userData
|
||||
writeOptions.userProtectedData = data.userProtectedData
|
||||
writeOptions.userCounter = data.userCounter
|
||||
writeOptions.userProtectedCounter = data.userProtectedCounter
|
||||
|
||||
tv_card_cid.text = data.cardId
|
||||
tv_data.text = String(data.userData, StandardCharsets.US_ASCII)
|
||||
tv_protected_data.text = String(data.userProtectedData, StandardCharsets.US_ASCII)
|
||||
tv_counter.text = data.userCounter.toString()
|
||||
tv_protected_counter.text = data.userProtectedCounter.toString()
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
btn_read.setOnClickListener {
|
||||
if (writeOptions.cardId == null) return@setOnClickListener
|
||||
private fun handleError(tv: TextView, error: TaskError?) {
|
||||
val er = error ?: return
|
||||
if (er is TaskError.UserCancelled) return
|
||||
|
||||
cardManager.readUserData(writeOptions.cardId !!) {
|
||||
when (it) {
|
||||
is TaskEvent.Completion -> handleError(tv_read_result, it.error)
|
||||
is TaskEvent.Event -> {
|
||||
runOnUiThread {
|
||||
val data = it.data as? ReadUserDataResponse
|
||||
if (data == null) {
|
||||
tv_read_result.text = "Response doesn't match"
|
||||
return@runOnUiThread
|
||||
}
|
||||
tv_read_result?.text = "Success"
|
||||
|
||||
writeOptions.userData = data.userData
|
||||
writeOptions.userProtectedData = data.userProtectedData
|
||||
writeOptions.userCounter = data.userCounter
|
||||
writeOptions.userProtectedCounter = data.userProtectedCounter
|
||||
|
||||
tv_card_cid.text = data.cardId
|
||||
tv_data.text = String(data.userData, StandardCharsets.US_ASCII)
|
||||
tv_protected_data.text = String(data.userProtectedData, StandardCharsets.US_ASCII)
|
||||
tv_counter.text = data.userCounter.toString()
|
||||
tv_protected_counter.text = data.userProtectedCounter.toString()
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
runOnUiThread { tv.text = er::class.simpleName }
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleError(tv: TextView, error: TaskError?) {
|
||||
val er = error ?: return
|
||||
if (er is TaskError.UserCancelled) return
|
||||
private fun initWriteOptions() {
|
||||
writeOptions = WriteOptions()
|
||||
|
||||
runOnUiThread { tv.text = er::class.simpleName }
|
||||
}
|
||||
chb_with_ud.setOnCheckedChangeListener { buttonView, isChecked -> writeOptions.updateData(buttonView) }
|
||||
chb_with_ud_protected.setOnCheckedChangeListener { buttonView, isChecked -> writeOptions.updateProtectedData(buttonView) }
|
||||
chb_with_counter.setOnCheckedChangeListener { buttonView, isChecked -> writeOptions.updateCounter(buttonView) }
|
||||
chb_with_protected_counter.setOnCheckedChangeListener { buttonView, isChecked -> writeOptions.updateProtectedCounter(buttonView) }
|
||||
chb_with_pin2.setOnCheckedChangeListener { buttonView, isChecked -> writeOptions.updatePin2(buttonView) }
|
||||
}
|
||||
|
||||
private fun initWriteOptions() {
|
||||
writeOptions = WriteOptions()
|
||||
|
||||
chb_with_ud.setOnCheckedChangeListener { buttonView, isChecked -> writeOptions.updateData(buttonView) }
|
||||
chb_with_ud_protected.setOnCheckedChangeListener { buttonView, isChecked -> writeOptions.updateProtectedData(buttonView) }
|
||||
chb_with_counter.setOnCheckedChangeListener { buttonView, isChecked -> writeOptions.updateCounter(buttonView) }
|
||||
chb_with_protected_counter.setOnCheckedChangeListener { buttonView, isChecked -> writeOptions.updateProtectedCounter(buttonView) }
|
||||
chb_with_pin2.setOnCheckedChangeListener { buttonView, isChecked -> writeOptions.updatePin2(buttonView) }
|
||||
}
|
||||
|
||||
private fun showReadWriteSection(show: Boolean) {
|
||||
val state = if (show) View.VISIBLE else View.GONE
|
||||
cl_read_write.visibility = state
|
||||
}
|
||||
private fun showReadWriteSection(show: Boolean) {
|
||||
val state = if (show) View.VISIBLE else View.GONE
|
||||
cl_read_write.visibility = state
|
||||
}
|
||||
}
|
||||
|
||||
class WriteOptions {
|
||||
var cardId: String? = null
|
||||
var userData: ByteArray? = null
|
||||
var userProtectedData: ByteArray? = null
|
||||
var userCounter: Int? = null
|
||||
var userProtectedCounter: Int? = null
|
||||
var pin2: String? = null
|
||||
var cardId: String? = null
|
||||
var userData: ByteArray? = null
|
||||
var userProtectedData: ByteArray? = null
|
||||
var userCounter: Int? = null
|
||||
var userProtectedCounter: Int? = null
|
||||
var pin2: String? = null
|
||||
|
||||
fun updateData(chbx: CompoundButton) {
|
||||
val value = "simple user data".toByteArray()
|
||||
userData = if (chbx.isChecked) value else null
|
||||
}
|
||||
fun updateData(chbx: CompoundButton) {
|
||||
val value = "simple user data".toByteArray()
|
||||
userData = if (chbx.isChecked) value else null
|
||||
}
|
||||
|
||||
fun updateProtectedData(chbx: CompoundButton) {
|
||||
val value = "protected user data".toByteArray()
|
||||
userProtectedData = if (chbx.isChecked) value else null
|
||||
}
|
||||
fun updateProtectedData(chbx: CompoundButton) {
|
||||
val value = "protected user data".toByteArray()
|
||||
userProtectedData = if (chbx.isChecked) value else null
|
||||
}
|
||||
|
||||
fun updateCounter(chbx: CompoundButton) {
|
||||
val value = if (userCounter == null) 0 else userCounter !! + 1
|
||||
userCounter = if (chbx.isChecked) value else null
|
||||
}
|
||||
fun updateCounter(chbx: CompoundButton) {
|
||||
val value = if (userCounter == null) 0 else userCounter!! + 1
|
||||
userCounter = if (chbx.isChecked) value else null
|
||||
}
|
||||
|
||||
fun updateProtectedCounter(chbx: CompoundButton) {
|
||||
val value = if (userProtectedCounter == null) 0 else userProtectedCounter !! + 1
|
||||
userProtectedCounter = if (chbx.isChecked) value else null
|
||||
}
|
||||
fun updateProtectedCounter(chbx: CompoundButton) {
|
||||
val value = if (userProtectedCounter == null) 0 else userProtectedCounter!! + 1
|
||||
userProtectedCounter = if (chbx.isChecked) value else null
|
||||
}
|
||||
|
||||
fun updatePin2(chbx: CompoundButton) {
|
||||
val value = CardEnvironment.DEFAULT_PIN2
|
||||
pin2 = if (chbx.isChecked) value else null
|
||||
}
|
||||
fun updatePin2(chbx: CompoundButton) {
|
||||
val value = CardEnvironment.DEFAULT_PIN2
|
||||
pin2 = if (chbx.isChecked) value else null
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
package com.tangem.tangemtest._arch
|
||||
|
||||
import android.util.Log
|
||||
import androidx.annotation.MainThread
|
||||
import androidx.annotation.Nullable
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.Observer
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
class SingleLiveEvent<T> : MutableLiveData<T>() {
|
||||
private val mPending: AtomicBoolean = AtomicBoolean(false)
|
||||
|
||||
override fun observe(owner: LifecycleOwner, observer: Observer<in T>) {
|
||||
if (hasActiveObservers()) {
|
||||
Log.w(TAG, "Multiple observers registered but only one will be notified of changes.")
|
||||
}
|
||||
|
||||
// Observe the internal MutableLiveData
|
||||
super.observe(owner, Observer {
|
||||
if (mPending.compareAndSet(true, false)) {
|
||||
observer.onChanged(it)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@MainThread
|
||||
override fun setValue(@Nullable t: T?) {
|
||||
mPending.set(true)
|
||||
super.setValue(t)
|
||||
}
|
||||
|
||||
@MainThread
|
||||
fun call() {
|
||||
setValue(null)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TAG = "SingleLiveEvent"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
package com.tangem.tangemtest._arch.structure
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
interface DataHolder<D> {
|
||||
var viewModel: D
|
||||
}
|
||||
|
||||
typealias Payload = MutableMap<String, Any?>
|
||||
|
||||
interface PayloadHolder {
|
||||
val payload: Payload
|
||||
|
||||
fun get(key: String): Any? = payload[key]
|
||||
fun remove(key: String): Any? = payload.remove(key)
|
||||
fun set(key: String, value: Any?) {
|
||||
payload[key] = value
|
||||
}
|
||||
}
|
||||
|
||||
interface ItemListHolder<I> {
|
||||
fun setItems(list: MutableList<I>)
|
||||
fun getItems(): MutableList<I>
|
||||
fun addItem(item: I)
|
||||
fun removeItem(item: I)
|
||||
fun clear()
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
package com.tangem.tangemtest._arch.structure
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
interface Id
|
||||
|
||||
enum class Additional : Id {
|
||||
UNDEFINED,
|
||||
JSON_INCOMING,
|
||||
JSON_OUTGOING,
|
||||
JSON_TAILS,
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
package com.tangem.tangemtest._arch.structure
|
||||
|
||||
import ru.dev.gbixahue.eu4d.lib.android.global.log.Logger
|
||||
import ru.dev.gbixahue.eu4d.lib.android.global.log.TagLogger
|
||||
import ru.dev.gbixahue.eu4d.lib.android.global.log.profiling.SimpleLogProfiler
|
||||
import ru.dev.gbixahue.eu4d.lib.kotlin.stringOf
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
object ILog {
|
||||
|
||||
private var logger: Logger? = null
|
||||
|
||||
fun setLogger(logger: Logger) {
|
||||
ILog.logger = logger
|
||||
}
|
||||
|
||||
fun d(from: Any, msg: Any?, value: Any? = null) {
|
||||
logger?.d(from, stringOf(msg), value)
|
||||
}
|
||||
|
||||
fun w(from: Any, msg: Any?, value: Any? = null) {
|
||||
logger?.w(from, stringOf(msg), value)
|
||||
}
|
||||
|
||||
fun e(from: Any, msg: Any?, value: Any? = null) {
|
||||
logger?.e(from, stringOf(msg), value)
|
||||
}
|
||||
}
|
||||
|
||||
class ItemLogger : TagLogger("ITEM") {
|
||||
init {
|
||||
msProfiler = SimpleLogProfiler()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
package com.tangem.tangemtest._arch.structure.abstraction
|
||||
|
||||
import com.tangem.tangemtest._arch.structure.ILog
|
||||
import com.tangem.tangemtest._arch.structure.Id
|
||||
import com.tangem.tangemtest._arch.structure.ItemListHolder
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
interface Block : Item {
|
||||
val itemList: MutableList<Item>
|
||||
}
|
||||
|
||||
abstract class BaseBlock : Block {
|
||||
override var parent: Item? = null
|
||||
override val itemList: MutableList<Item> = mutableListOf()
|
||||
override val payload: MutableMap<String, Any?> = mutableMapOf()
|
||||
}
|
||||
|
||||
open class ListItemBlock(
|
||||
override val id: Id
|
||||
) : BaseBlock(), ItemListHolder<Item> {
|
||||
|
||||
override fun setItems(list: MutableList<Item>) {
|
||||
ILog.d(this, "setItems into: $id, count: ${list.size}")
|
||||
itemList.clear()
|
||||
itemList.addAll(list)
|
||||
itemList.forEach { it.parent = this }
|
||||
}
|
||||
|
||||
override fun getItems(): MutableList<Item> {
|
||||
return itemList
|
||||
}
|
||||
|
||||
override fun addItem(item: Item) {
|
||||
ILog.d(this, "addItem into: $id, who: ${item.id}")
|
||||
item.parent = this
|
||||
itemList.add(item)
|
||||
}
|
||||
|
||||
override fun removeItem(item: Item) {
|
||||
ILog.d(this, "removeItem from: $id, which: ${item.id}")
|
||||
itemList.remove(item)
|
||||
}
|
||||
|
||||
override fun clear() {
|
||||
ILog.d(this, "clear $id")
|
||||
itemList.clear()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
package com.tangem.tangemtest._arch.structure.abstraction
|
||||
|
||||
import com.tangem.tangemtest._arch.structure.Id
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
fun List<Item>.findDataItem(id: Id): BaseItem<Any?>? {
|
||||
var foundItem: BaseItem<Any?>? = null
|
||||
iterate {
|
||||
if (it.id == id) {
|
||||
foundItem = it as? BaseItem<Any?>
|
||||
return@iterate
|
||||
}
|
||||
}
|
||||
return foundItem
|
||||
}
|
||||
|
||||
fun List<Item>.iterate(func: (Item) -> Unit) {
|
||||
forEach {
|
||||
when (it) {
|
||||
is BaseItem<*> -> func(it)
|
||||
is Block -> it.itemList.iterate(func)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
package com.tangem.tangemtest._arch.structure.abstraction
|
||||
|
||||
import com.tangem.tangemtest._arch.structure.DataHolder
|
||||
import com.tangem.tangemtest._arch.structure.Id
|
||||
import com.tangem.tangemtest._arch.structure.Payload
|
||||
import com.tangem.tangemtest._arch.structure.PayloadHolder
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
interface Item : PayloadHolder {
|
||||
val id: Id
|
||||
var parent: Item?
|
||||
}
|
||||
|
||||
abstract class BaseItem<D>(
|
||||
override var viewModel: ItemViewModel<D>
|
||||
) : Item, DataHolder<ItemViewModel<D>> {
|
||||
override var parent: Item? = null
|
||||
override val payload: Payload = mutableMapOf()
|
||||
|
||||
fun getData(): D? = viewModel.data
|
||||
|
||||
fun setData(value: D?) {
|
||||
viewModel.data = value
|
||||
}
|
||||
|
||||
fun restoreDefaultData(){
|
||||
setData(viewModel.defaultData)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
package com.tangem.tangemtest._arch.structure.abstraction
|
||||
|
||||
import ru.dev.gbixahue.eu4d.lib.kotlin.common.Converter
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
interface DefaultConverter<A, B> {
|
||||
fun convert(from: A, default: B): B
|
||||
}
|
||||
|
||||
interface ItemsToModel<M> : DefaultConverter<List<Item>, M>
|
||||
interface ModelToItems<M> : Converter<M, List<Item>>
|
||||
|
||||
interface ModelConverter<M> : DefaultConverter<List<Item>, M>, Converter<M, List<Item>>
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
package com.tangem.tangemtest._arch.structure.abstraction
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
typealias PluginCallBackResult = (Any?) -> Item
|
||||
|
||||
interface Plugin : Item {
|
||||
fun invoke(data: Any?, asyncCallback: PluginCallBackResult? = null): Any?
|
||||
}
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
package com.tangem.tangemtest._arch.structure.abstraction
|
||||
|
||||
import com.tangem.tangemtest._arch.structure.ILog
|
||||
import com.tangem.tangemtest._arch.structure.Payload
|
||||
import com.tangem.tangemtest._arch.structure.PayloadHolder
|
||||
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
typealias ValueChange<V> = (V?) -> Unit
|
||||
typealias SafeValueChange<V> = (V) -> Unit
|
||||
|
||||
interface ItemViewModel<D : Any?> : PayloadHolder {
|
||||
val viewState: ViewState
|
||||
var data: D?
|
||||
var defaultData: D?
|
||||
var onDataUpdated: ValueChange<D>?
|
||||
|
||||
fun updateDataByView(data: D?)
|
||||
}
|
||||
|
||||
open class BaseItemViewModel<D> : ItemViewModel<D> {
|
||||
|
||||
override val viewState: ViewState = ViewState()
|
||||
override val payload: Payload = mutableMapOf()
|
||||
|
||||
// Don't update it directly from a View. Use for it updateDataByView()
|
||||
override var data: D? = null
|
||||
set(value) {
|
||||
if (handleDataUpdates(value)) field = value
|
||||
}
|
||||
|
||||
// Data for restoring initial value
|
||||
override var defaultData: D? = null
|
||||
set(value) {
|
||||
field = value
|
||||
data = value
|
||||
}
|
||||
|
||||
// Use it for handling data updates in View
|
||||
override var onDataUpdated: ValueChange<D>? = null
|
||||
|
||||
// When data updates directly it invokes onDataUpdated
|
||||
// return true = data will update
|
||||
// return false = data won't update
|
||||
protected open fun handleDataUpdates(value: D?): Boolean {
|
||||
ILog.d(this, "handleDateUpdates: $value")
|
||||
onDataUpdated?.invoke(value)
|
||||
return true
|
||||
}
|
||||
|
||||
// Use it to update the data from a View. It disables onDataUpdated to prevent a callback loop
|
||||
override fun updateDataByView(data: D?) {
|
||||
ILog.d(this, "data changed: $data")
|
||||
val callback = onDataUpdated
|
||||
onDataUpdated = null
|
||||
this.data = data
|
||||
onDataUpdated = callback
|
||||
}
|
||||
}
|
||||
|
||||
class ViewState {
|
||||
var isHiddenField = false
|
||||
|
||||
var descriptionVisibility: Int = 0x00000008
|
||||
set(value) {
|
||||
field = value
|
||||
onDescriptionVisibilityChanged?.invoke(value)
|
||||
}
|
||||
|
||||
var onDescriptionVisibilityChanged: SafeValueChange<Int>? = null
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
package com.tangem.tangemtest._arch.structure.impl
|
||||
|
||||
import com.tangem.tangemtest._arch.structure.Id
|
||||
import com.tangem.tangemtest._arch.structure.abstraction.BaseItem
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
class AnyItem(override val id: Id, value: Any? = null) : BaseItem<Any>(AnyViewModel(value))
|
||||
class TextItem(override val id: Id, value: String? = null) : BaseItem<String>(StringViewModel(value))
|
||||
class EditTextItem(override val id: Id, value: String? = null) : BaseItem<String>(StringViewModel(value))
|
||||
class NumberItem(override val id: Id, value: Number? = null) : BaseItem<Number>(NumberViewModel(value))
|
||||
class BoolItem(override val id: Id, value: Boolean? = null) : BaseItem<Boolean>(BoolViewModel(value))
|
||||
|
||||
class ListItem(override val id: Id, value: List<KeyValue>, selectedValue: Any?)
|
||||
: BaseItem<ListValueWrapper>(ListViewModel(ListValueWrapper(selectedValue, value))
|
||||
)
|
||||
|
||||
inline fun<reified T> BaseItem<T>.getData(): T? = viewModel.data
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
package com.tangem.tangemtest._arch.structure.impl
|
||||
|
||||
import com.tangem.tangemtest._arch.structure.abstraction.BaseItemViewModel
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
open class TransitiveViewModel<D>(value: D?) : BaseItemViewModel<D>() {
|
||||
init {
|
||||
defaultData = value
|
||||
}
|
||||
}
|
||||
|
||||
class AnyViewModel(value: Any? = null) : TransitiveViewModel<Any>(value)
|
||||
class StringViewModel(value: String? = null) : TransitiveViewModel<String>(value)
|
||||
class NumberViewModel(value: Number? = null) : TransitiveViewModel<Number>(value)
|
||||
class BoolViewModel(value: Boolean? = null) : TransitiveViewModel<Boolean>(value)
|
||||
class ListViewModel(value: ListValueWrapper? = null) : TransitiveViewModel<ListValueWrapper>(value) {
|
||||
|
||||
override fun handleDataUpdates(value: ListValueWrapper?): Boolean {
|
||||
val newValue = value ?: return false
|
||||
if (newValue.selectedItem == data?.selectedItem) return false
|
||||
|
||||
return super.handleDataUpdates(value)
|
||||
}
|
||||
}
|
||||
|
||||
class KeyValue(val key: String, val value: Any)
|
||||
class ListValueWrapper(var selectedItem: Any?, val itemList: List<KeyValue>)
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
package com.tangem.tangemtest._main
|
||||
|
||||
import android.content.res.Resources
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.Menu
|
||||
import androidx.activity.viewModels
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.appcompat.widget.SwitchCompat
|
||||
import androidx.appcompat.widget.Toolbar
|
||||
import androidx.navigation.findNavController
|
||||
import androidx.navigation.fragment.NavHostFragment
|
||||
import androidx.navigation.ui.AppBarConfiguration
|
||||
import androidx.navigation.ui.navigateUp
|
||||
import androidx.navigation.ui.setupActionBarWithNavController
|
||||
import com.tangem.tangemtest.R
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
class MainActivity : AppCompatActivity() {
|
||||
|
||||
private val vm: MainViewModel by viewModels<MainViewModel>()
|
||||
private lateinit var appBarConfiguration: AppBarConfiguration
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.a_main)
|
||||
|
||||
val toolbar = findViewById<Toolbar>(R.id.toolbar)
|
||||
setSupportActionBar(toolbar)
|
||||
|
||||
val host: NavHostFragment = supportFragmentManager
|
||||
.findFragmentById(R.id.nav_host_fragment) as NavHostFragment? ?: return
|
||||
|
||||
val navController = host.navController
|
||||
appBarConfiguration = AppBarConfiguration(setOf(R.id.nav_entry_point))
|
||||
setupActionBarWithNavController(navController, appBarConfiguration)
|
||||
|
||||
navController.addOnDestinationChangedListener { _, destination, _ ->
|
||||
val dest: String = try {
|
||||
resources.getResourceName(destination.id)
|
||||
} catch (e: Resources.NotFoundException) {
|
||||
destination.id.toString()
|
||||
}
|
||||
Log.d("NavigationActivity", "Navigated to $dest")
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSupportNavigateUp(): Boolean {
|
||||
return findNavController(R.id.nav_host_fragment).navigateUp(appBarConfiguration)
|
||||
}
|
||||
|
||||
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) }
|
||||
}
|
||||
return super.onCreateOptionsMenu(menu)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
package com.tangem.tangemtest._main
|
||||
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
class MainViewModel : ViewModel() {
|
||||
val ldDescriptionSwitch = MutableLiveData<Boolean>(false)
|
||||
|
||||
fun switchToggled(state: Boolean) {
|
||||
ldDescriptionSwitch.postValue(state)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,87 @@
|
|||
package com.tangem.tangemtest._main.entryPoint
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.navigation.NavController
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.recyclerview.widget.DividerItemDecoration
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.transition.AutoTransition
|
||||
import androidx.transition.TransitionManager
|
||||
import com.tangem.tangemtest.R
|
||||
import com.tangem.tangemtest._main.MainViewModel
|
||||
import com.tangem.tangemtest.ucase.getDefaultNavigationOptions
|
||||
import com.tangem.tangemtest.ucase.resources.ActionType
|
||||
import com.tangem.tangemtest.ucase.resources.MainResourceHolder
|
||||
import kotlinx.android.synthetic.main.fg_entry_point.*
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
class ActionListFragment : Fragment() {
|
||||
|
||||
private val navController: NavController by lazy { findNavController() }
|
||||
private lateinit var rvActions: RecyclerView
|
||||
|
||||
private val mainActivityVM: MainViewModel by activityViewModels()
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
return inflater.inflate(R.layout.fg_entry_point, container, false)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
initRecyclerView()
|
||||
}
|
||||
|
||||
private fun initRecyclerView() {
|
||||
val layoutManager = LinearLayoutManager(activity)
|
||||
rvActions = rv_actions
|
||||
rvActions.layoutManager = layoutManager
|
||||
rvActions.addItemDecoration(DividerItemDecoration(activity, layoutManager.orientation))
|
||||
|
||||
val vhDataWrapper = VhExDataWrapper(MainResourceHolder, false)
|
||||
|
||||
rvActions.adapter = RvActionsAdapter(vhDataWrapper) { type, position, data -> navigate(data) }
|
||||
mainActivityVM.ldDescriptionSwitch.observe(viewLifecycleOwner, Observer {
|
||||
vhDataWrapper.descriptionIsVisible = it
|
||||
TransitionManager.beginDelayedTransition(rvActions as ViewGroup, AutoTransition())
|
||||
rvActions.adapter?.notifyDataSetChanged()
|
||||
})
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
val adapter: RvActionsAdapter = rvActions.adapter as? RvActionsAdapter ?: return
|
||||
|
||||
adapter.setItemList(getNavigateOptions())
|
||||
adapter.notifyDataSetChanged()
|
||||
}
|
||||
|
||||
private fun navigate(destinationId: Int) {
|
||||
navController.navigate(destinationId, null, getDefaultNavigationOptions())
|
||||
}
|
||||
|
||||
private fun getNavigateOptions(): MutableList<ActionType> {
|
||||
return mutableListOf(
|
||||
ActionType.Scan,
|
||||
ActionType.Sign,
|
||||
ActionType.Personalize,
|
||||
ActionType.Depersonalize
|
||||
// ActionType.CreateWallet,
|
||||
// ActionType.PurgeWallet,
|
||||
// ActionType.ReadIssuerData,
|
||||
// ActionType.WriteIssuerData,
|
||||
// ActionType.ReadIssuerExData,
|
||||
// ActionType.WriteIssuerExData,
|
||||
// ActionType.ReadUserData,
|
||||
// ActionType.WriteUserData
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
package com.tangem.tangemtest._main.entryPoint
|
||||
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.TextView
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.tangem.tangemtest.R
|
||||
import com.tangem.tangemtest.ucase.resources.ActionRes
|
||||
import com.tangem.tangemtest.ucase.resources.ActionType
|
||||
import com.tangem.tangemtest.ucase.resources.MainResourceHolder
|
||||
import ru.dev.gbixahue.eu4d.lib.android._android.views.inflate
|
||||
import ru.dev.gbixahue.eu4d.lib.android._android.views.recycler_view.RvBaseAdapter
|
||||
import ru.dev.gbixahue.eu4d.lib.android._android.views.recycler_view.RvBaseVH
|
||||
import ru.dev.gbixahue.eu4d.lib.android._android.views.recycler_view.RvCallback
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
class RvActionsAdapter(
|
||||
private val wrapper: VhExDataWrapper,
|
||||
private val callback: RvCallback<Int>
|
||||
) : RvBaseAdapter<RvActionsVH, ActionType>() {
|
||||
|
||||
override fun onBindViewHolder(holder: RvActionsVH, position: Int) {
|
||||
holder.bindData(itemList[position])
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RvActionsVH {
|
||||
val view = parent.inflate<View>(R.layout.vh_actions, false)
|
||||
return RvActionsVH(view, wrapper, callback)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class VhExDataWrapper(
|
||||
val resHolder: MainResourceHolder,
|
||||
var descriptionIsVisible: Boolean
|
||||
)
|
||||
|
||||
class RvActionsVH(
|
||||
itemView: View,
|
||||
private val wrapper: VhExDataWrapper,
|
||||
private val callback: RvCallback<Int>?
|
||||
) : RvBaseVH<ActionType>(itemView) {
|
||||
|
||||
private val tvAction = itemView.findViewById<TextView>(R.id.tv_title)
|
||||
private val containerDescription = itemView.findViewById<ViewGroup>(R.id.container_description)
|
||||
private val tvDescription = itemView.findViewById<TextView>(R.id.tv_description)
|
||||
|
||||
override fun bindData(data: ActionType) {
|
||||
val res: ActionRes = wrapper.resHolder.safeGet(data)
|
||||
tvAction.text = getString(res.resName)
|
||||
tvDescription.text = getString(res.resDescription)
|
||||
|
||||
itemView.setOnClickListener {
|
||||
res.resNavigation?.let { callback?.invoke(itemViewType, adapterPosition, it) }
|
||||
}
|
||||
|
||||
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 {
|
||||
val reqId = id ?: return ifNull
|
||||
return itemView.context.getString(reqId)
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
package com.tangem.tangemtest.commons
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
fun <A, B> performAction(a: A?, b: B?, action: (A, B) -> Unit) {
|
||||
if (a != null && b != null) action(a, b)
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
package com.tangem.tangemtest.commons
|
||||
|
||||
import ru.dev.gbixahue.eu4d.lib.android.global.log.TagLogger
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
class TangemLogger : TagLogger("TangemDemo")
|
||||
|
|
@ -7,7 +7,7 @@ import com.tangem.commands.personalization.entities.CardConfig
|
|||
import com.tangem.commands.personalization.entities.NdefRecord
|
||||
import java.util.*
|
||||
|
||||
fun CardConfig.Companion.init(application: Application): CardConfig {
|
||||
fun CardConfig.Companion.create(application: Application): CardConfig {
|
||||
|
||||
val preferences = application.getSharedPreferences("prefs", Context.MODE_PRIVATE)
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,16 @@
|
|||
package com.tangem.tangemtest.extensions
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
fun <T> List<T>.print(delimiter: String = ", ", wrap: Boolean = true): String {
|
||||
val builder = StringBuilder()
|
||||
forEach { builder.append(it).append(delimiter) }
|
||||
val length = builder.length
|
||||
if (length > delimiter.length) {
|
||||
builder.delete(length - delimiter.length, length)
|
||||
}
|
||||
val result = builder.toString()
|
||||
|
||||
return if (wrap) "[$result]" else result
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
package com.tangem.tangemtest.ucase
|
||||
|
||||
import androidx.navigation.NavOptions
|
||||
import androidx.navigation.navOptions
|
||||
import com.tangem.tangemtest.R
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
fun getDefaultNavigationOptions(): NavOptions {
|
||||
return navOptions {
|
||||
anim {
|
||||
enter = R.anim.slide_in_right
|
||||
exit = R.anim.slide_out_left
|
||||
popEnter = R.anim.slide_in_left
|
||||
popExit = R.anim.slide_out_right
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
package com.tangem.tangemtest.ucase.domain.actions
|
||||
|
||||
import com.tangem.CardManager
|
||||
import com.tangem.tangemtest._arch.structure.Id
|
||||
import com.tangem.tangemtest._arch.structure.Payload
|
||||
import com.tangem.tangemtest._arch.structure.PayloadHolder
|
||||
import com.tangem.tangemtest._arch.structure.abstraction.Item
|
||||
import com.tangem.tangemtest.ucase.domain.paramsManager.ActionCallback
|
||||
import com.tangem.tangemtest.ucase.domain.paramsManager.triggers.afterAction.AfterActionModification
|
||||
import com.tangem.tangemtest.ucase.domain.paramsManager.triggers.changeConsequence.ItemsChangeConsequence
|
||||
import com.tangem.tasks.TaskEvent
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*
|
||||
* The Card Action class family is designed for calling Card Manager functions
|
||||
* and then processing the response. It also allows you to extract the main action as a lambda expression
|
||||
*/
|
||||
data class AttrForAction(
|
||||
val cardManager: CardManager,
|
||||
val itemList: List<Item>,
|
||||
val payload: Payload,
|
||||
val consequence: ItemsChangeConsequence?
|
||||
)
|
||||
|
||||
interface Action {
|
||||
fun executeMainAction(payload: PayloadHolder, attrs: AttrForAction, callback: ActionCallback)
|
||||
fun getActionByTag(payload: PayloadHolder, id: Id, attrs: AttrForAction): ((ActionCallback) -> Unit)? = null
|
||||
}
|
||||
|
||||
abstract class BaseAction : Action {
|
||||
protected open fun handleResult(
|
||||
payload: PayloadHolder,
|
||||
taskEvent: TaskEvent<*>,
|
||||
modifier: AfterActionModification?,
|
||||
attrs: AttrForAction,
|
||||
callback: ActionCallback
|
||||
) {
|
||||
val modifiedItems = mutableListOf<Item>()
|
||||
modifier?.modify(payload, taskEvent, attrs.itemList)?.forEach { item ->
|
||||
modifiedItems.add(item)
|
||||
attrs.consequence?.affectChanges(payload, item, attrs.itemList)?.let { modifiedItems.addAll(it) }
|
||||
}
|
||||
callback(taskEvent, modifiedItems)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
package com.tangem.tangemtest.ucase.domain.actions
|
||||
|
||||
import com.tangem.tangemtest._arch.structure.Id
|
||||
import com.tangem.tangemtest._arch.structure.PayloadHolder
|
||||
import com.tangem.tangemtest._arch.structure.abstraction.findDataItem
|
||||
import com.tangem.tangemtest.ucase.domain.paramsManager.ActionCallback
|
||||
import com.tangem.tangemtest.ucase.variants.TlvId
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
class DepersonalizeAction : BaseAction() {
|
||||
override fun executeMainAction(payload: PayloadHolder, attrs: AttrForAction, callback: ActionCallback) {
|
||||
val item = attrs.itemList.findDataItem(TlvId.CardId) ?: return
|
||||
val cardId = item.viewModel.data as? String ?: return
|
||||
|
||||
attrs.cardManager.depersonalize(cardId) { handleResult(payload, it, null, attrs, callback) }
|
||||
}
|
||||
|
||||
override fun getActionByTag(payload: PayloadHolder, id: Id, attrs: AttrForAction): ((ActionCallback) -> Unit)? {
|
||||
return when (id) {
|
||||
TlvId.CardId -> { callback -> ScanAction().executeMainAction(payload, attrs, callback) }
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
package com.tangem.tangemtest.ucase.domain.actions
|
||||
|
||||
import com.tangem.tangemtest._arch.structure.PayloadHolder
|
||||
import com.tangem.tangemtest._arch.structure.abstraction.Item
|
||||
import com.tangem.tangemtest._arch.structure.abstraction.findDataItem
|
||||
import com.tangem.tangemtest._arch.structure.impl.EditTextItem
|
||||
import com.tangem.tangemtest._arch.structure.impl.NumberItem
|
||||
import com.tangem.tangemtest.ucase.domain.paramsManager.ActionCallback
|
||||
import com.tangem.tangemtest.ucase.domain.paramsManager.PayloadKey
|
||||
import com.tangem.tangemtest.ucase.tunnel.ActionView
|
||||
import com.tangem.tangemtest.ucase.tunnel.ItemError
|
||||
import com.tangem.tangemtest.ucase.variants.personalize.CardNumber
|
||||
import com.tangem.tangemtest.ucase.variants.personalize.converter.PersonalizeConfigConverter
|
||||
import com.tangem.tangemtest.ucase.variants.personalize.converter.fromTo.PersonalizeConfigToCardConfig
|
||||
import com.tangem.tangemtest.ucase.variants.personalize.dto.DefaultPersonalizeParams
|
||||
import com.tangem.tangemtest.ucase.variants.personalize.dto.PersonalizeConfig
|
||||
import ru.dev.gbixahue.eu4d.lib.kotlin.stringOf
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
class PersonalizeAction : BaseAction() {
|
||||
override fun executeMainAction(payload: PayloadHolder, attrs: AttrForAction, callback: ActionCallback) {
|
||||
val itemList = attrs.payload[PayloadKey.itemList] as? List<Item> ?: return
|
||||
val actionView = attrs.payload[PayloadKey.actionView] as? ActionView ?: return
|
||||
|
||||
if (!checkSeries(itemList)) {
|
||||
actionView.showSnackbar(ItemError.BadSeries)
|
||||
return
|
||||
}
|
||||
if (!checkNumber(itemList)) {
|
||||
actionView.showSnackbar(ItemError.BadCardNumber)
|
||||
return
|
||||
}
|
||||
|
||||
val issuer = DefaultPersonalizeParams.issuer()
|
||||
val acquirer = DefaultPersonalizeParams.acquirer()
|
||||
val manufacturer = DefaultPersonalizeParams.manufacturer()
|
||||
|
||||
val personalizeConfig = PersonalizeConfigConverter().convert(itemList, PersonalizeConfig())
|
||||
val cardConfig = PersonalizeConfigToCardConfig().convert(personalizeConfig)
|
||||
|
||||
attrs.cardManager.personalize(cardConfig, issuer, manufacturer, acquirer) {
|
||||
handleResult(payload, it, null, attrs, callback)
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkSeries(itemList: List<Item>): Boolean {
|
||||
val seriesItem = itemList.findDataItem(CardNumber.Series) as? EditTextItem ?: return false
|
||||
val data = seriesItem.getData() ?: return false
|
||||
|
||||
seriesItem.setData(data.toUpperCase())
|
||||
return data.length == 2 || data.length == 4
|
||||
}
|
||||
|
||||
private fun checkNumber(itemList: List<Item>): Boolean {
|
||||
val seriesItem = itemList.findDataItem(CardNumber.Series) as? EditTextItem ?: return false
|
||||
val numberItem = itemList.findDataItem(CardNumber.Number) as? NumberItem ?: return false
|
||||
val seriesData = seriesItem.getData() ?: return false
|
||||
val numberData = stringOf(numberItem.getData())
|
||||
|
||||
return if (seriesData.length == 2 && numberData.length > 13) false
|
||||
else !(seriesData.length == 4 && numberData.length > 11)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
package com.tangem.tangemtest.ucase.domain.actions
|
||||
|
||||
import com.tangem.tangemtest._arch.structure.PayloadHolder
|
||||
import com.tangem.tangemtest.ucase.domain.paramsManager.ActionCallback
|
||||
import com.tangem.tangemtest.ucase.domain.paramsManager.triggers.afterAction.AfterScanModifier
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
class ScanAction : BaseAction() {
|
||||
override fun executeMainAction(payload: PayloadHolder, attrs: AttrForAction, callback: ActionCallback) {
|
||||
attrs.cardManager.scanCard { handleResult(payload, it, AfterScanModifier(), attrs, callback) }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
package com.tangem.tangemtest.ucase.domain.actions
|
||||
|
||||
import com.tangem.tangemtest._arch.structure.Id
|
||||
import com.tangem.tangemtest._arch.structure.PayloadHolder
|
||||
import com.tangem.tangemtest._arch.structure.abstraction.findDataItem
|
||||
import com.tangem.tangemtest.ucase.domain.paramsManager.ActionCallback
|
||||
import com.tangem.tangemtest.ucase.variants.TlvId
|
||||
import ru.dev.gbixahue.eu4d.lib.kotlin.stringOf
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
class SignAction : BaseAction() {
|
||||
override fun executeMainAction(payload: PayloadHolder, attrs: AttrForAction, callback: ActionCallback) {
|
||||
val dataForHashing = attrs.itemList.findDataItem(TlvId.TransactionOutHash) ?: return
|
||||
val hash = dataForHashing.getData() as? ByteArray ?: return
|
||||
val cardId = attrs.itemList.findDataItem(TlvId.CardId)?.viewModel?.data ?: return
|
||||
|
||||
attrs.cardManager.sign(arrayOf(hash), stringOf(cardId)) { handleResult(payload, it, null, attrs, callback) }
|
||||
}
|
||||
|
||||
override fun getActionByTag(payload: PayloadHolder, id: Id, attrs: AttrForAction): ((ActionCallback) -> Unit)? {
|
||||
return when (id) {
|
||||
TlvId.CardId -> { callback -> ScanAction().executeMainAction(payload, attrs, callback) }
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
package com.tangem.tangemtest.ucase.domain.paramsManager
|
||||
|
||||
import com.tangem.tangemtest.ucase.domain.paramsManager.managers.DepersonalizeItemsManager
|
||||
import com.tangem.tangemtest.ucase.domain.paramsManager.managers.PersonalizeItemsManager
|
||||
import com.tangem.tangemtest.ucase.domain.paramsManager.managers.ScanItemsManager
|
||||
import com.tangem.tangemtest.ucase.domain.paramsManager.managers.SignItemsManager
|
||||
import com.tangem.tangemtest.ucase.resources.ActionType
|
||||
import ru.dev.gbixahue.eu4d.lib.kotlin.common.BaseTypedHolder
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
class ParamsManagerFactory : BaseTypedHolder<ActionType, ItemsManager>() {
|
||||
|
||||
companion object {
|
||||
fun createFactory(): ParamsManagerFactory {
|
||||
return ParamsManagerFactory().apply {
|
||||
register(ActionType.Scan, ScanItemsManager())
|
||||
register(ActionType.Sign, SignItemsManager())
|
||||
register(ActionType.Personalize, PersonalizeItemsManager())
|
||||
register(ActionType.Depersonalize, DepersonalizeItemsManager())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
package com.tangem.tangemtest.ucase.domain.paramsManager
|
||||
|
||||
import com.tangem.CardManager
|
||||
import com.tangem.tangemtest._arch.structure.Id
|
||||
import com.tangem.tangemtest._arch.structure.Payload
|
||||
import com.tangem.tangemtest._arch.structure.PayloadHolder
|
||||
import com.tangem.tangemtest._arch.structure.abstraction.Item
|
||||
import com.tangem.tangemtest.ucase.domain.paramsManager.triggers.changeConsequence.ItemsChangeConsequence
|
||||
import com.tangem.tasks.TaskEvent
|
||||
|
||||
typealias ActionResponse = TaskEvent<*>
|
||||
typealias AffectedList = List<Item>
|
||||
typealias AffectedItemsCallback = (AffectedList) -> Unit
|
||||
typealias ActionCallback = (ActionResponse, AffectedList) -> Unit
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
interface ItemsManager : PayloadHolder {
|
||||
|
||||
fun itemChanged(id: Id, value: Any?, callback: AffectedItemsCallback? = null)
|
||||
fun getItems(): List<Item>
|
||||
fun invokeMainAction(cardManager: CardManager, callback: ActionCallback)
|
||||
fun getActionByTag(id: Id, cardManager: CardManager): ((ActionCallback) -> Unit)?
|
||||
fun attachPayload(payload: Payload)
|
||||
|
||||
val consequence: ItemsChangeConsequence?
|
||||
}
|
||||
|
||||
interface PayloadKey {
|
||||
companion object {
|
||||
val cardConfig: String = "cardConfig"
|
||||
val incomingJson: String = "incomingJson"
|
||||
val card = "card"
|
||||
val actionView = "actionView"
|
||||
val itemList = "itemList"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
package com.tangem.tangemtest.ucase.domain.paramsManager.managers
|
||||
|
||||
import com.tangem.CardManager
|
||||
import com.tangem.tangemtest._arch.structure.Id
|
||||
import com.tangem.tangemtest._arch.structure.Payload
|
||||
import com.tangem.tangemtest._arch.structure.abstraction.Item
|
||||
import com.tangem.tangemtest._arch.structure.abstraction.findDataItem
|
||||
import com.tangem.tangemtest.ucase.domain.actions.Action
|
||||
import com.tangem.tangemtest.ucase.domain.actions.AttrForAction
|
||||
import com.tangem.tangemtest.ucase.domain.paramsManager.ActionCallback
|
||||
import com.tangem.tangemtest.ucase.domain.paramsManager.AffectedItemsCallback
|
||||
import com.tangem.tangemtest.ucase.domain.paramsManager.ItemsManager
|
||||
import com.tangem.tangemtest.ucase.domain.paramsManager.triggers.changeConsequence.ItemsChangeConsequence
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
abstract class BaseItemsManager(protected val action: Action) : ItemsManager {
|
||||
|
||||
protected val itemList: List<Item> by lazy { createItemsList() }
|
||||
|
||||
override val consequence: ItemsChangeConsequence? = null
|
||||
|
||||
override val payload: MutableMap<String, Any?> = mutableMapOf()
|
||||
|
||||
override fun itemChanged(id: Id, value: Any?, callback: AffectedItemsCallback?) {
|
||||
if (itemList.isEmpty()) return
|
||||
val foundItem = itemList.findDataItem(id) ?: return
|
||||
|
||||
foundItem.setData(value)
|
||||
applyChangesByAffectedItems(foundItem, callback)
|
||||
}
|
||||
|
||||
override fun getItems(): MutableList<Item> = itemList.toMutableList()
|
||||
|
||||
override fun getActionByTag(id: Id, cardManager: CardManager): ((ActionCallback) -> Unit)? = null
|
||||
|
||||
override fun attachPayload(payload: Payload) {
|
||||
payload.forEach { this.payload[it.key] = it.value }
|
||||
}
|
||||
|
||||
// Use it if current item needs to be affect any other items
|
||||
protected open fun applyChangesByAffectedItems(param: Item, callback: AffectedItemsCallback?) {
|
||||
consequence?.affectChanges(this, param, itemList)?.let { callback?.invoke(it) }
|
||||
}
|
||||
|
||||
protected fun getAttrsForAction(cardManager: CardManager)
|
||||
: AttrForAction = AttrForAction(cardManager, itemList, payload, consequence)
|
||||
|
||||
abstract fun createItemsList(): List<Item>
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
package com.tangem.tangemtest.ucase.domain.paramsManager.managers
|
||||
|
||||
import com.tangem.CardManager
|
||||
import com.tangem.tangemtest._arch.structure.Id
|
||||
import com.tangem.tangemtest._arch.structure.abstraction.Item
|
||||
import com.tangem.tangemtest._arch.structure.impl.EditTextItem
|
||||
import com.tangem.tangemtest.ucase.domain.actions.DepersonalizeAction
|
||||
import com.tangem.tangemtest.ucase.domain.paramsManager.ActionCallback
|
||||
import com.tangem.tangemtest.ucase.variants.TlvId
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
class DepersonalizeItemsManager : BaseItemsManager(DepersonalizeAction()) {
|
||||
override fun createItemsList(): List<Item> {
|
||||
return listOf(EditTextItem(TlvId.CardId, null))
|
||||
}
|
||||
|
||||
override fun invokeMainAction(cardManager: CardManager, callback: ActionCallback) {
|
||||
action.executeMainAction(this, getAttrsForAction(cardManager), callback)
|
||||
}
|
||||
|
||||
override fun getActionByTag(id: Id, cardManager: CardManager): ((ActionCallback) -> Unit)? {
|
||||
return action.getActionByTag(this, id, getAttrsForAction(cardManager))
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
package com.tangem.tangemtest.ucase.domain.paramsManager.managers
|
||||
|
||||
import com.tangem.CardManager
|
||||
import com.tangem.tangemtest._arch.structure.abstraction.Item
|
||||
import com.tangem.tangemtest.ucase.domain.actions.PersonalizeAction
|
||||
import com.tangem.tangemtest.ucase.domain.paramsManager.ActionCallback
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
class PersonalizeItemsManager : BaseItemsManager(PersonalizeAction()) {
|
||||
override fun createItemsList(): List<Item> = listOf()
|
||||
|
||||
override fun invokeMainAction(cardManager: CardManager, callback: ActionCallback) {
|
||||
action.executeMainAction(this, getAttrsForAction(cardManager), callback)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
package com.tangem.tangemtest.ucase.domain.paramsManager.managers
|
||||
|
||||
import com.tangem.CardManager
|
||||
import com.tangem.tangemtest._arch.structure.abstraction.Item
|
||||
import com.tangem.tangemtest.ucase.domain.actions.ScanAction
|
||||
import com.tangem.tangemtest.ucase.domain.paramsManager.ActionCallback
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
class ScanItemsManager : BaseItemsManager(ScanAction()) {
|
||||
override fun createItemsList(): List<Item> = listOf()
|
||||
|
||||
override fun invokeMainAction(cardManager: CardManager, callback: ActionCallback) {
|
||||
action.executeMainAction(this, getAttrsForAction(cardManager), callback)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
package com.tangem.tangemtest.ucase.domain.paramsManager.managers
|
||||
|
||||
import com.tangem.CardManager
|
||||
import com.tangem.tangemtest._arch.structure.Id
|
||||
import com.tangem.tangemtest._arch.structure.abstraction.Item
|
||||
import com.tangem.tangemtest._arch.structure.impl.EditTextItem
|
||||
import com.tangem.tangemtest.ucase.domain.actions.SignAction
|
||||
import com.tangem.tangemtest.ucase.domain.paramsManager.ActionCallback
|
||||
import com.tangem.tangemtest.ucase.domain.paramsManager.triggers.changeConsequence.ItemsChangeConsequence
|
||||
import com.tangem.tangemtest.ucase.domain.paramsManager.triggers.changeConsequence.SignScanConsequence
|
||||
import com.tangem.tangemtest.ucase.variants.TlvId
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
class SignItemsManager : BaseItemsManager(SignAction()) {
|
||||
|
||||
override val consequence: ItemsChangeConsequence? by lazy { SignScanConsequence() }
|
||||
|
||||
override fun createItemsList(): List<Item> {
|
||||
return listOf(
|
||||
EditTextItem(TlvId.CardId, null),
|
||||
EditTextItem(TlvId.TransactionOutHash, "Data used for hashing")
|
||||
)
|
||||
}
|
||||
|
||||
override fun invokeMainAction(cardManager: CardManager, callback: ActionCallback) {
|
||||
action.executeMainAction(this, getAttrsForAction(cardManager), callback)
|
||||
}
|
||||
|
||||
override fun getActionByTag(id: Id, cardManager: CardManager): ((ActionCallback) -> Unit)? {
|
||||
return action.getActionByTag(this, id, getAttrsForAction(cardManager))
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
package com.tangem.tangemtest.ucase.domain.paramsManager.triggers.afterAction
|
||||
|
||||
import com.tangem.tangemtest._arch.structure.PayloadHolder
|
||||
import com.tangem.tangemtest._arch.structure.abstraction.Item
|
||||
import com.tangem.tasks.TaskEvent
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*
|
||||
* The After Action Modification class family is intended for modifying items (if necessary)
|
||||
* after calling CardManager.anyAction.
|
||||
* Returns a list of items that have been modified
|
||||
*/
|
||||
interface AfterActionModification {
|
||||
fun modify(payload: PayloadHolder, taskEvent: TaskEvent<*>, itemList: List<Item>): List<Item>
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
package com.tangem.tangemtest.ucase.domain.paramsManager.triggers.afterAction
|
||||
|
||||
import com.tangem.commands.Card
|
||||
import com.tangem.commands.CardStatus
|
||||
import com.tangem.tangemtest._arch.structure.PayloadHolder
|
||||
import com.tangem.tangemtest._arch.structure.abstraction.Item
|
||||
import com.tangem.tangemtest._arch.structure.abstraction.findDataItem
|
||||
import com.tangem.tangemtest.ucase.domain.paramsManager.PayloadKey
|
||||
import com.tangem.tangemtest.ucase.tunnel.ActionView
|
||||
import com.tangem.tangemtest.ucase.tunnel.CardError
|
||||
import com.tangem.tangemtest.ucase.variants.TlvId
|
||||
import com.tangem.tasks.ScanEvent
|
||||
import com.tangem.tasks.TaskEvent
|
||||
import ru.dev.gbixahue.eu4d.lib.android.global.threading.postUI
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
class AfterScanModifier : AfterActionModification {
|
||||
override fun modify(payload: PayloadHolder, taskEvent: TaskEvent<*>, itemList: List<Item>): List<Item> {
|
||||
val foundItem = itemList.findDataItem(TlvId.CardId) ?: return listOf()
|
||||
val card = smartCast(taskEvent)?.card ?: return listOf()
|
||||
val actionView = payload.get(PayloadKey.actionView) as? ActionView ?: return listOf()
|
||||
|
||||
return if (isNotPersonalized(card)) {
|
||||
actionView.showSnackbar(CardError.NotPersonalized)
|
||||
postUI { actionView.showActionFab(false) }
|
||||
listOf()
|
||||
} else {
|
||||
payload.set(PayloadKey.card, card)
|
||||
foundItem.setData(card.cardId)
|
||||
postUI { actionView.showActionFab(true) }
|
||||
listOf(foundItem)
|
||||
}
|
||||
}
|
||||
|
||||
private fun smartCast(taskEvent: TaskEvent<*>): ScanEvent.OnReadEvent? {
|
||||
return (taskEvent as? TaskEvent.Event)?.data as? ScanEvent.OnReadEvent
|
||||
}
|
||||
|
||||
private fun isNotPersonalized(card: Card): Boolean = card.status == CardStatus.NotPersonalized
|
||||
}
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
package com.tangem.tangemtest.ucase.domain.paramsManager.triggers.changeConsequence
|
||||
|
||||
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.tangemtest._arch.structure.PayloadHolder
|
||||
import com.tangem.tangemtest._arch.structure.abstraction.Item
|
||||
import com.tangem.tangemtest._arch.structure.abstraction.findDataItem
|
||||
import com.tangem.tangemtest.ucase.domain.paramsManager.PayloadKey
|
||||
import com.tangem.tangemtest.ucase.tunnel.ActionView
|
||||
import com.tangem.tangemtest.ucase.variants.TlvId
|
||||
import ru.dev.gbixahue.eu4d.lib.android.global.threading.postUI
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*
|
||||
* The ParamsChangeConsequence class family modifies parameters depending on the state
|
||||
* of the incoming parameter
|
||||
*/
|
||||
interface ItemsChangeConsequence {
|
||||
fun affectChanges(payload: PayloadHolder, changedItem: Item, itemList: List<Item>): List<Item>?
|
||||
}
|
||||
|
||||
class SignScanConsequence: ItemsChangeConsequence {
|
||||
|
||||
override fun affectChanges(payload: PayloadHolder, changedItem: Item, itemList: List<Item>): List<Item>? {
|
||||
if (changedItem.id != TlvId.CardId) return null
|
||||
|
||||
val hashItem = itemList.findDataItem(TlvId.TransactionOutHash) ?: return null
|
||||
val affectedItems = mutableListOf(hashItem)
|
||||
val card = payload.remove(PayloadKey.card) as Card?
|
||||
if (card == null) {
|
||||
hashItem.restoreDefaultData()
|
||||
postUI { (payload.get(PayloadKey.actionView) as? ActionView)?.showActionFab(false) }
|
||||
} else {
|
||||
val dataForHashing = hashItem.getData() as? String ?: "Any data mother...s"
|
||||
val hashedData = when(card.curve) {
|
||||
EllipticCurve.Secp256k1 -> dataForHashing.calculateSha256()
|
||||
EllipticCurve.Ed25519 -> dataForHashing.calculateSha512()
|
||||
else -> return null
|
||||
}
|
||||
hashItem.setData(hashedData)
|
||||
}
|
||||
return affectedItems.toList()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
package com.tangem.tangemtest.ucase.domain.responses
|
||||
|
||||
import com.google.gson.*
|
||||
import com.tangem.commands.ProductMask
|
||||
import com.tangem.commands.Settings
|
||||
import com.tangem.commands.SettingsMask
|
||||
import com.tangem.commands.SigningMethod
|
||||
import com.tangem.common.extensions.toHexString
|
||||
import java.lang.reflect.Type
|
||||
import java.text.DateFormat
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
class GsonInitializer {
|
||||
val gson: Gson by lazy { init() }
|
||||
|
||||
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(Date::class.java, DateTypeAdapter())
|
||||
}
|
||||
builder.setPrettyPrinting()
|
||||
return builder.create()
|
||||
}
|
||||
}
|
||||
|
||||
class ByteTypeAdapter : JsonSerializer<ByteArray> {
|
||||
override fun serialize(src: ByteArray, typeOfSrc: Type, context: JsonSerializationContext): JsonElement {
|
||||
return JsonPrimitive(src.toHexString())
|
||||
}
|
||||
}
|
||||
|
||||
class SettingsMaskTypeAdapter : JsonSerializer<SettingsMask> {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
class ProductMaskTypeAdapter : JsonSerializer<ProductMask> {
|
||||
override fun serialize(src: ProductMask, typeOfSrc: Type, context: JsonSerializationContext): JsonElement {
|
||||
return JsonPrimitive(src.rawValue.toString())
|
||||
}
|
||||
}
|
||||
|
||||
class SigningMethodTypeAdapter : JsonSerializer<SigningMethod> {
|
||||
override fun serialize(src: SigningMethod, typeOfSrc: Type, context: JsonSerializationContext): JsonElement {
|
||||
return JsonPrimitive(src.rawValue.toString())
|
||||
}
|
||||
}
|
||||
|
||||
class DateTypeAdapter: JsonSerializer<Date> {
|
||||
override fun serialize(src: Date, typeOfSrc: Type, context: JsonSerializationContext): JsonElement {
|
||||
val formatter = DateFormat.getDateInstance(DateFormat.DEFAULT, Locale("en_US"))
|
||||
return JsonPrimitive(formatter.format(src).toString())
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
package com.tangem.tangemtest.ucase.resources
|
||||
|
||||
import com.tangem.tangemtest._arch.structure.Id
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
enum class ActionType : Id {
|
||||
Scan,
|
||||
Sign,
|
||||
CreateWallet,
|
||||
PurgeWallet,
|
||||
ReadIssuerData,
|
||||
WriteIssuerData,
|
||||
ReadIssuerExData,
|
||||
WriteIssuerExData,
|
||||
ReadUserData,
|
||||
WriteUserData,
|
||||
Personalize,
|
||||
Depersonalize,
|
||||
Unknown,
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
package com.tangem.tangemtest.ucase.resources
|
||||
|
||||
import com.tangem.tangemtest.R
|
||||
import com.tangem.tangemtest._arch.structure.Id
|
||||
import com.tangem.tangemtest.ucase.resources.initializers.ActionResources
|
||||
import com.tangem.tangemtest.ucase.resources.initializers.PersonalizeResources
|
||||
import com.tangem.tangemtest.ucase.resources.initializers.TlvResources
|
||||
import ru.dev.gbixahue.eu4d.lib.kotlin.common.BaseTypedHolder
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
open class ResourceHolder<T> : BaseTypedHolder<T, Resources>()
|
||||
|
||||
object MainResourceHolder : ResourceHolder<Id>() {
|
||||
init {
|
||||
PersonalizeResources().init(this)
|
||||
ActionResources().init(this)
|
||||
TlvResources().init(this)
|
||||
}
|
||||
|
||||
inline fun <reified Res : Resources> safeGet(id: Id): Res {
|
||||
val result = super.get(id) ?: Resources(R.string.unknown, R.string.unknown)
|
||||
return result as Res
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
package com.tangem.tangemtest.ucase.resources
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
open class Resources(
|
||||
val resName: Int,
|
||||
val resDescription: Int? = null
|
||||
)
|
||||
|
||||
class ActionRes(
|
||||
resName: Int,
|
||||
resDescription: Int? = null,
|
||||
val resNavigation: Int? = null
|
||||
) : Resources(resName, resDescription)
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
package com.tangem.tangemtest.ucase.resources.initializers
|
||||
|
||||
import com.tangem.tangemtest.R
|
||||
import com.tangem.tangemtest._arch.structure.Id
|
||||
import com.tangem.tangemtest.ucase.resources.ActionRes
|
||||
import com.tangem.tangemtest.ucase.resources.ActionType
|
||||
import com.tangem.tangemtest.ucase.resources.ResourceHolder
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
class ActionResources {
|
||||
|
||||
fun init(holder: ResourceHolder<Id>) {
|
||||
holder.register(ActionType.Scan, ActionRes(R.string.action_card_scan, R.string.info_action_card_scan, R.id.action_nav_entry_point_to_nav_scan))
|
||||
holder.register(ActionType.Sign, ActionRes(R.string.action_card_sign, R.string.info_action_card_sign, R.id.action_nav_entry_point_to_nav_sign))
|
||||
holder.register(ActionType.Personalize, ActionRes(R.string.action_personalize, R.string.info_action_personalize, R.id.action_nav_entry_point_to_nav_personalize))
|
||||
holder.register(ActionType.Depersonalize, ActionRes(R.string.action_depersonalize, R.string.info_action_depersonalize, R.id.action_nav_entry_point_to_nav_depersonalize))
|
||||
holder.register(ActionType.CreateWallet, ActionRes(R.string.action_wallet_create, R.string.info_action_wallet_create, R.id.action_nav_entry_point_to_nav_wallet_create))
|
||||
holder.register(ActionType.PurgeWallet, ActionRes(R.string.action_wallet_purge, R.string.info_action_wallet_purge, R.id.action_nav_entry_point_to_nav_wallet_purge))
|
||||
holder.register(ActionType.ReadIssuerData, ActionRes(R.string.action_issuer_read_data, R.string.info_action_issuer_read_data, R.id.action_nav_entry_point_to_nav_issuer_read_data))
|
||||
holder.register(ActionType.WriteIssuerData, ActionRes(R.string.action_issuer_write_data, R.string.info_action_issuer_write_data, R.id.action_nav_entry_point_to_nav_issuer_write_data))
|
||||
holder.register(ActionType.ReadIssuerExData, ActionRes(R.string.action_issuer_read_ex_data, R.string.info_action_issuer_read_ex_data, R.id.action_nav_entry_point_to_nav_issuer_read_ex_data))
|
||||
holder.register(ActionType.WriteIssuerExData, ActionRes(R.string.action_issuer_write_ex_data, R.string.info_action_issuer_write_ex_data, R.id.action_nav_entry_point_to_nav_issuer_write_ex_data))
|
||||
holder.register(ActionType.ReadUserData, ActionRes(R.string.action_user_read_data, R.string.info_action_user_read_data, R.id.action_nav_entry_point_to_nav_user_read_data))
|
||||
holder.register(ActionType.WriteUserData, ActionRes(R.string.action_user_write_data, R.string.info_action_user_write_data, R.id.action_nav_entry_point_to_nav_user_write_data))
|
||||
// holder.register(ActionType.Unknown, getIfNotContains())
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,131 @@
|
|||
package com.tangem.tangemtest.ucase.resources.initializers
|
||||
|
||||
import com.tangem.tangemtest.R
|
||||
import com.tangem.tangemtest._arch.structure.Id
|
||||
import com.tangem.tangemtest.ucase.resources.ResourceHolder
|
||||
import com.tangem.tangemtest.ucase.resources.Resources
|
||||
import com.tangem.tangemtest.ucase.variants.personalize.*
|
||||
import ru.dev.gbixahue.eu4d.lib.kotlin.common.TypedHolder
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
class PersonalizeResources() {
|
||||
fun init(holder: ResourceHolder<Id>) {
|
||||
initBlock(holder)
|
||||
initCardNumber(holder)
|
||||
initCommon(holder)
|
||||
initSigningMethod(holder)
|
||||
initSignHashExProp(holder)
|
||||
initDenomination(holder)
|
||||
initToken(holder)
|
||||
initProductMask(holder)
|
||||
initSettingsMask(holder)
|
||||
initSettingsMaskProtocolEnc(holder)
|
||||
initSettingsMaskNde(holder)
|
||||
initPins(holder)
|
||||
}
|
||||
|
||||
private fun initBlock(holder: TypedHolder<Id, Resources>) {
|
||||
holder.register(BlockId.CardNumber, Resources(R.string.pers_block_card_number, R.string.info_pers_block_card_number))
|
||||
holder.register(BlockId.Common, Resources(R.string.pers_block_common, R.string.info_pers_block_common))
|
||||
holder.register(BlockId.SigningMethod, Resources(R.string.pers_block_signing_method, R.string.info_pers_block_signing_method))
|
||||
holder.register(BlockId.SignHashExProp, Resources(R.string.pers_block_sign_hash_ex_prop, R.string.info_pers_block_sign_hash_ex_prop))
|
||||
holder.register(BlockId.Denomination, Resources(R.string.pers_block_denomination, R.string.info_pers_block_denomination))
|
||||
holder.register(BlockId.Token, Resources(R.string.pers_block_token, R.string.info_pers_block_token))
|
||||
holder.register(BlockId.ProdMask, Resources(R.string.pers_block_product_mask, R.string.info_pers_block_product_mask))
|
||||
holder.register(BlockId.SettingsMask, Resources(R.string.pers_block_settings_mask, R.string.info_pers_block_settings_mask))
|
||||
holder.register(BlockId.SettingsMaskProtocolEnc, Resources(R.string.pers_block_settings_mask_protocol_enc, R.string.info_pers_block_settings_mask_protocol_enc))
|
||||
holder.register(BlockId.SettingsMaskNdef, Resources(R.string.pers_block_settings_mask_ndef, R.string.info_pers_block_settings_mask_ndef))
|
||||
holder.register(BlockId.Pins, Resources(R.string.pers_block_pins, R.string.info_pers_block_pins))
|
||||
}
|
||||
|
||||
private fun initCardNumber(holder: TypedHolder<Id, Resources>) {
|
||||
holder.register(CardNumber.Series, Resources(R.string.pers_item_series, R.string.info_pers_item_series))
|
||||
holder.register(CardNumber.Number, Resources(R.string.pers_item_number, R.string.info_pers_item_number))
|
||||
holder.register(CardNumber.BatchId, Resources(R.string.pers_item_batch_id, R.string.info_pers_item_batch_id))
|
||||
}
|
||||
|
||||
private fun initCommon(holder: TypedHolder<Id, Resources>) {
|
||||
holder.register(Common.Curve, Resources(R.string.pers_item_curve, R.string.info_pers_item_curve))
|
||||
holder.register(Common.Blockchain, Resources(R.string.pers_item_blockchain, R.string.info_pers_item_blockchain))
|
||||
holder.register(Common.BlockchainCustom, Resources(R.string.pers_item_custom_blockchain, R.string.info_pers_item_custom_blockchain))
|
||||
holder.register(Common.MaxSignatures, Resources(R.string.pers_item_max_signatures, R.string.info_pers_item_max_signatures))
|
||||
holder.register(Common.CreateWallet, Resources(R.string.pers_item_create_wallet, R.string.info_pers_item_create_wallet))
|
||||
}
|
||||
|
||||
private fun initSigningMethod(holder: TypedHolder<Id, Resources>) {
|
||||
holder.register(SigningMethod.SignTx, Resources(R.string.pers_item_sign_tx_hashes, R.string.info_pers_item_sign_tx_hashes))
|
||||
holder.register(SigningMethod.SignTxRaw, Resources(R.string.pers_item_sign_raw_tx, R.string.info_pers_item_sign_raw_tx))
|
||||
holder.register(SigningMethod.SignValidatedTx, Resources(R.string.pers_item_sign_validated_tx_hashes, R.string.info_pers_item_sign_validated_tx_hashes))
|
||||
holder.register(SigningMethod.SignValidatedTxRaw, Resources(R.string.pers_item_sign_validated_raw_tx, R.string.info_pers_item_sign_validated_raw_tx))
|
||||
holder.register(SigningMethod.SignValidatedTxIssuer, Resources(R.string.pers_item_sign_validated_tx_hashes_with_iss_data, R.string.info_pers_item_sign_validated_tx_hashes_with_iss_data))
|
||||
holder.register(SigningMethod.SignValidatedTxRawIssuer, Resources(R.string.pers_item_sign_validated_raw_tx_with_iss_data, R.string.info_pers_item_sign_validated_raw_tx_with_iss_data))
|
||||
holder.register(SigningMethod.SignExternal, Resources(R.string.pers_item_sign_hash_ex, R.string.info_pers_item_sign_hash_ex))
|
||||
}
|
||||
|
||||
private fun initSignHashExProp(holder: TypedHolder<Id, Resources>) {
|
||||
holder.register(SignHashExProp.PinLessFloorLimit, Resources(R.string.pers_item_pin_less_floor_limit, R.string.info_pers_item_pin_less_floor_limit))
|
||||
holder.register(SignHashExProp.CryptoExKey, Resources(R.string.pers_item_cr_ex_key, R.string.info_pers_item_cr_ex_key))
|
||||
holder.register(SignHashExProp.RequireTerminalCertSig, Resources(R.string.pers_item_require_terminal_cert_sig, R.string.info_pers_item_require_terminal_cert_sig))
|
||||
holder.register(SignHashExProp.RequireTerminalTxSig, Resources(R.string.pers_item_require_terminal_tx_sig, R.string.info_pers_item_require_terminal_tx_sig))
|
||||
holder.register(SignHashExProp.CheckPin3, Resources(R.string.pers_item_check_pin3_on_card, R.string.info_pers_item_check_pin3_on_card))
|
||||
}
|
||||
|
||||
private fun initDenomination(holder: TypedHolder<Id, Resources>) {
|
||||
holder.register(Denomination.WriteOnPersonalize, Resources(R.string.pers_item_write_on_personalize, R.string.info_pers_item_write_on_personalize))
|
||||
holder.register(Denomination.Denomination, Resources(R.string.pers_item_denomination, R.string.info_pers_item_denomination))
|
||||
}
|
||||
|
||||
private fun initToken(holder: TypedHolder<Id, Resources>) {
|
||||
holder.register(Token.ItsToken, Resources(R.string.pers_item_its_token, R.string.info_pers_item_its_token))
|
||||
holder.register(Token.Symbol, Resources(R.string.pers_item_symbol, R.string.info_pers_item_symbol))
|
||||
holder.register(Token.ContractAddress, Resources(R.string.pers_item_contract_address, R.string.info_pers_item_contract_address))
|
||||
holder.register(Token.Decimal, Resources(R.string.pers_item_decimal, R.string.info_pers_item_decimal))
|
||||
}
|
||||
|
||||
private fun initProductMask(holder: TypedHolder<Id, Resources>) {
|
||||
holder.register(ProductMask.Note, Resources(R.string.pers_item_note, R.string.info_pers_item_note))
|
||||
holder.register(ProductMask.Tag, Resources(R.string.pers_item_tag, R.string.info_pers_item_tag))
|
||||
holder.register(ProductMask.CardId, Resources(R.string.pers_item_id_card, R.string.info_pers_item_id_card))
|
||||
}
|
||||
|
||||
private fun initSettingsMask(holder: TypedHolder<Id, Resources>) {
|
||||
holder.register(SettingsMask.IsReusable, Resources(R.string.pers_item_is_reusable, R.string.info_pers_item_is_reusable))
|
||||
holder.register(SettingsMask.NeedActivation, Resources(R.string.pers_item_need_activation, R.string.info_pers_item_need_activation))
|
||||
holder.register(SettingsMask.ForbidPurge, Resources(R.string.pers_item_forbid_purge, R.string.info_pers_item_forbid_purge))
|
||||
holder.register(SettingsMask.AllowSelectBlockchain, Resources(R.string.pers_item_allow_select_blockchain, R.string.info_pers_item_allow_select_blockchain))
|
||||
holder.register(SettingsMask.UseBlock, Resources(R.string.pers_item_use_block, R.string.info_pers_item_use_block))
|
||||
holder.register(SettingsMask.OneApdu, Resources(R.string.pers_item_one_apdu_at_once, R.string.info_pers_item_one_apdu_at_once))
|
||||
holder.register(SettingsMask.UseCvc, Resources(R.string.pers_item_use_cvc, R.string.info_pers_item_use_cvc))
|
||||
holder.register(SettingsMask.AllowSwapPin, Resources(R.string.pers_item_allow_swap_pin, R.string.info_pers_item_allow_swap_pin))
|
||||
holder.register(SettingsMask.AllowSwapPin2, Resources(R.string.pers_item_allow_swap_pin2, R.string.info_pers_item_allow_swap_pin2))
|
||||
holder.register(SettingsMask.ForbidDefaultPin, Resources(R.string.pers_item_forbid_default_pin, R.string.info_pers_item_forbid_default_pin))
|
||||
holder.register(SettingsMask.SmartSecurityDelay, Resources(R.string.pers_item_smart_security_delay, R.string.info_pers_item_smart_security_delay))
|
||||
holder.register(SettingsMask.ProtectIssuerDataAgainstReplay, Resources(R.string.pers_item_protect_issuer_data_against_replay, R.string.info_pers_item_protect_issuer_data_against_replay))
|
||||
holder.register(SettingsMask.SkipSecurityDelayIfValidated, Resources(R.string.pers_item_skip_security_delay_if_validated, R.string.info_pers_item_skip_security_delay_if_validated))
|
||||
holder.register(SettingsMask.SkipPin2CvcIfValidated, Resources(R.string.pers_item_skip_pin2_and_cvc_if_validated, R.string.info_pers_item_skip_pin2_and_cvc_if_validated))
|
||||
holder.register(SettingsMask.SkipSecurityDelayOnLinkedTerminal, Resources(R.string.pers_item_skip_security_delay_on_linked_terminal, R.string.info_pers_item_skip_security_delay_on_linked_terminal))
|
||||
holder.register(SettingsMask.RestrictOverwriteExtraIssuerData, Resources(R.string.pers_item_restrict_overwrite_ex_issuer_data, R.string.info_pers_item_restrict_overwrite_ex_issuer_data))
|
||||
}
|
||||
|
||||
private fun initSettingsMaskProtocolEnc(holder: TypedHolder<Id, Resources>) {
|
||||
holder.register(SettingsMaskProtocolEnc.AllowUnencrypted, Resources(R.string.pers_item_allow_unencrypted, R.string.info_pers_item_allow_unencrypted))
|
||||
holder.register(SettingsMaskProtocolEnc.AllowStaticEncryption, Resources(R.string.pers_item_allow_fast_encryption, R.string.info_pers_item_allow_fast_encryption))
|
||||
}
|
||||
|
||||
private fun initSettingsMaskNde(holder: TypedHolder<Id, Resources>) {
|
||||
holder.register(SettingsMaskNdef.UseNdef, Resources(R.string.pers_item_use_ndef, R.string.info_pers_item_use_ndef))
|
||||
holder.register(SettingsMaskNdef.DynamicNdef, Resources(R.string.pers_item_dynamic_ndef, R.string.info_pers_item_dynamic_ndef))
|
||||
holder.register(SettingsMaskNdef.DisablePrecomputedNdef, Resources(R.string.pers_item_disable_precomputed_ndef, R.string.info_pers_item_disable_precomputed_ndef))
|
||||
holder.register(SettingsMaskNdef.Aar, Resources(R.string.pers_item_aar, R.string.info_pers_item_aar))
|
||||
}
|
||||
|
||||
private fun initPins(holder: TypedHolder<Id, Resources>) {
|
||||
holder.register(Pins.Pin, Resources(R.string.pers_item_pin, R.string.info_pers_item_pin))
|
||||
holder.register(Pins.Pin2, Resources(R.string.pers_item_pin2, R.string.info_pers_item_pin2))
|
||||
holder.register(Pins.Pin3, Resources(R.string.pers_item_pin3, R.string.info_pers_item_pin3))
|
||||
holder.register(Pins.Cvc, Resources(R.string.pers_item_cvc, R.string.info_pers_item_cvc))
|
||||
holder.register(Pins.PauseBeforePin2, Resources(R.string.pers_item_pause_before_pin2, R.string.info_pers_item_pause_before_pin2))
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
package com.tangem.tangemtest.ucase.resources.initializers
|
||||
|
||||
import com.tangem.tangemtest.R
|
||||
import com.tangem.tangemtest.ucase.resources.MainResourceHolder
|
||||
import com.tangem.tangemtest.ucase.resources.Resources
|
||||
import com.tangem.tangemtest.ucase.variants.TlvId
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
class TlvResources {
|
||||
fun init(holder: MainResourceHolder) {
|
||||
initScan(holder)
|
||||
}
|
||||
|
||||
private fun initScan(holder: MainResourceHolder) {
|
||||
holder.register(TlvId.CardId, Resources(R.string.tlv_card_id, R.string.info_tlv_card_id))
|
||||
holder.register(TlvId.TransactionOutHash, Resources(R.string.tlv_transaction_out_hash, R.string.info_tlv_transaction_out_hash))
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
package com.tangem.tangemtest.ucase.tunnel
|
||||
|
||||
import com.tangem.tangemtest._arch.structure.Id
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
interface Errors: Id
|
||||
|
||||
enum class CardError : Errors {
|
||||
NotPersonalized,
|
||||
}
|
||||
|
||||
enum class ItemError: Errors {
|
||||
BadSeries,
|
||||
BadCardNumber,
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
package com.tangem.tangemtest.ucase.tunnel
|
||||
|
||||
import com.tangem.tangemtest._arch.structure.Id
|
||||
|
||||
interface ViewScreen
|
||||
|
||||
interface SnackbarHolder {
|
||||
fun showSnackbar(message: String)
|
||||
fun showSnackbar(id: Int)
|
||||
fun showSnackbar(id: Id)
|
||||
}
|
||||
|
||||
interface ActionView : ViewScreen, SnackbarHolder {
|
||||
fun showActionFab(show: Boolean)
|
||||
}
|
||||
|
|
@ -0,0 +1,150 @@
|
|||
package com.tangem.tangemtest.ucase.ui
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.TextView
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.lifecycle.Observer
|
||||
import com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
import com.google.android.material.snackbar.BaseTransientBottomBar
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import com.tangem.CardManager
|
||||
import com.tangem.tangem_sdk_new.extensions.init
|
||||
import com.tangem.tangemtest.R
|
||||
import com.tangem.tangemtest._arch.structure.Id
|
||||
import com.tangem.tangemtest._arch.structure.abstraction.BaseItem
|
||||
import com.tangem.tangemtest._main.MainViewModel
|
||||
import com.tangem.tangemtest.ucase.domain.paramsManager.ItemsManager
|
||||
import com.tangem.tangemtest.ucase.domain.paramsManager.ParamsManagerFactory
|
||||
import com.tangem.tangemtest.ucase.domain.paramsManager.PayloadKey
|
||||
import com.tangem.tangemtest.ucase.resources.ActionType
|
||||
import com.tangem.tangemtest.ucase.tunnel.ActionView
|
||||
import com.tangem.tangemtest.ucase.tunnel.CardError
|
||||
import com.tangem.tangemtest.ucase.ui.widgets.ParameterWidget
|
||||
import ru.dev.gbixahue.eu4d.lib.android.global.log.Log
|
||||
import ru.dev.gbixahue.eu4d.lib.kotlin.common.LayoutHolder
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
abstract class BaseCardActionFragment : Fragment(), LayoutHolder, ActionView {
|
||||
|
||||
protected val incomingParamsContainer: ViewGroup by lazy {
|
||||
mainView.findViewById<LinearLayout>(R.id.ll_incoming_params_container)
|
||||
}
|
||||
protected lateinit var mainView: View
|
||||
protected val actionFab: FloatingActionButton by lazy { mainView.findViewById<FloatingActionButton>(R.id.fab_action) }
|
||||
|
||||
protected val paramsVM: ParamsViewModel by viewModels { ActionViewModelFactory(itemsManager) }
|
||||
protected val widgetList = mutableListOf<ParameterWidget>()
|
||||
|
||||
private val itemsManager: ItemsManager by lazy { ParamsManagerFactory.createFactory().get(getAction())!! }
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
Log.d(this, "onCreateView")
|
||||
mainView = inflater.inflate(getLayoutId(), container, false)
|
||||
return mainView
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
Log.d(this, "onViewCreated")
|
||||
|
||||
viewLifecycleOwner.lifecycle.addObserver(paramsVM)
|
||||
paramsVM.setCardManager(CardManager.init(requireActivity()))
|
||||
paramsVM.attachToPayload(mutableMapOf(PayloadKey.actionView to this as ActionView))
|
||||
initFab()
|
||||
showActionFab(false)
|
||||
createWidgets { subscribeToViewModelChanges() }
|
||||
}
|
||||
|
||||
private fun initFab() {
|
||||
Log.d(this, "initFab")
|
||||
actionFab.setOnClickListener { paramsVM.invokeMainAction() }
|
||||
}
|
||||
|
||||
private fun createWidgets(widgetCreatedCallback: () -> Unit) {
|
||||
Log.d(this, "createWidgets")
|
||||
paramsVM.ldParams.observe(viewLifecycleOwner, Observer { itemList ->
|
||||
itemList.forEach { param ->
|
||||
val widget = ParameterWidget(inflateParamView(incomingParamsContainer), param)
|
||||
widget.onValueChanged = { id, value -> paramsVM.userChangedItem(id, value) }
|
||||
widget.onActionBtnClickListener = paramsVM.getItemAction(param.id)
|
||||
widgetList.add(widget)
|
||||
}
|
||||
widgetCreatedCallback()
|
||||
})
|
||||
}
|
||||
|
||||
private fun subscribeToViewModelChanges() {
|
||||
Log.d(this, "subscribeToViewModelChanges")
|
||||
listenResponse()
|
||||
listenError()
|
||||
listenChangedItems()
|
||||
listenDescriptionSwitchChanges()
|
||||
}
|
||||
|
||||
protected open fun listenResponse() {
|
||||
val tvResponse by lazy { mainView.findViewById<TextView>(R.id.tv_action_response_json) }
|
||||
paramsVM.ldResponse.observe(viewLifecycleOwner, Observer {
|
||||
Log.d(this, "action response: ${if (it.length > 50) it.substring(0..50) else it}")
|
||||
tvResponse.text = it
|
||||
})
|
||||
}
|
||||
|
||||
protected open fun listenError() {
|
||||
paramsVM.seError.observe(viewLifecycleOwner, Observer { showSnackbar(it) })
|
||||
}
|
||||
|
||||
protected open fun listenChangedItems() {
|
||||
paramsVM.seChangedItems.observe(viewLifecycleOwner, Observer { itemList ->
|
||||
itemList.forEach { item ->
|
||||
Log.d(this, "item changed from VM - name: ${item.id}")
|
||||
val dataItem = item as? BaseItem<Any?> ?: return@Observer
|
||||
Log.d(this, "item changed from VM - name: ${dataItem.id}, value:${dataItem.viewModel.data}")
|
||||
widgetList.firstOrNull { it.id == item.id }?.changeParamValue(item.viewModel.data)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
protected open fun listenDescriptionSwitchChanges() {
|
||||
val mainActViewModel by activityViewModels<MainViewModel>()
|
||||
mainActViewModel.ldDescriptionSwitch.observe(viewLifecycleOwner, Observer { isEnabled ->
|
||||
widgetList.forEach { it.toggleDescriptionVisibility(isEnabled) }
|
||||
})
|
||||
}
|
||||
|
||||
private fun inflateParamView(where: ViewGroup): ViewGroup {
|
||||
val inflater = LayoutInflater.from(where.context)
|
||||
val view = inflater.inflate(R.layout.w_card_incoming_param, where, false)
|
||||
where.addView(view)
|
||||
return view as ViewGroup
|
||||
}
|
||||
|
||||
override fun showActionFab(show: Boolean) {
|
||||
if (show) actionFab.show() else actionFab.hide()
|
||||
}
|
||||
|
||||
override fun showSnackbar(id: Id) {
|
||||
// MainResourceHolder.safeGet<>()
|
||||
when (id) {
|
||||
CardError.NotPersonalized -> showSnackbar(R.string.card_error_not_personalized)
|
||||
else -> showSnackbar(requireContext().getString(R.string.unknown))
|
||||
}
|
||||
}
|
||||
|
||||
override fun showSnackbar(id: Int) {
|
||||
showSnackbar(requireContext().getString(id))
|
||||
}
|
||||
|
||||
override fun showSnackbar(message: String) {
|
||||
Snackbar.make(mainView, message, BaseTransientBottomBar.LENGTH_SHORT).show()
|
||||
}
|
||||
|
||||
abstract fun getAction(): ActionType
|
||||
}
|
||||
|
|
@ -0,0 +1,144 @@
|
|||
package com.tangem.tangemtest.ucase.ui
|
||||
|
||||
import androidx.annotation.UiThread
|
||||
import androidx.lifecycle.*
|
||||
import com.google.gson.Gson
|
||||
import com.tangem.CardManager
|
||||
import com.tangem.commands.Card
|
||||
import com.tangem.tangemtest._arch.SingleLiveEvent
|
||||
import com.tangem.tangemtest._arch.structure.Id
|
||||
import com.tangem.tangemtest._arch.structure.Payload
|
||||
import com.tangem.tangemtest._arch.structure.abstraction.Item
|
||||
import com.tangem.tangemtest.commons.performAction
|
||||
import com.tangem.tangemtest.ucase.domain.paramsManager.ItemsManager
|
||||
import com.tangem.tangemtest.ucase.domain.responses.GsonInitializer
|
||||
import com.tangem.tangemtest.ucase.tunnel.ViewScreen
|
||||
import com.tangem.tasks.ScanEvent
|
||||
import com.tangem.tasks.TaskError
|
||||
import com.tangem.tasks.TaskEvent
|
||||
import ru.dev.gbixahue.eu4d.lib.android.global.log.Log
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
class ActionViewModelFactory(private val manager: ItemsManager) : ViewModelProvider.NewInstanceFactory() {
|
||||
override fun <T : ViewModel?> create(modelClass: Class<T>): T = ParamsViewModel(manager) as T
|
||||
}
|
||||
|
||||
class ParamsViewModel(private val itemsManager: ItemsManager) : ViewModel(), LifecycleObserver {
|
||||
|
||||
val ldCard = MutableLiveData<Card>()
|
||||
val ldIsVerified = MutableLiveData<Boolean>()
|
||||
val ldResponse = MutableLiveData<String>()
|
||||
val ldReadResponse = MutableLiveData<String>()
|
||||
val ldParams = MutableLiveData(itemsManager.getItems())
|
||||
|
||||
val seError: MutableLiveData<String> = SingleLiveEvent()
|
||||
val seChangedItems: MutableLiveData<List<Item>> = SingleLiveEvent()
|
||||
|
||||
private val notifier: Notifier = Notifier(this)
|
||||
private lateinit var cardManager: CardManager
|
||||
|
||||
fun setCardManager(cardManager: CardManager) {
|
||||
this.cardManager = cardManager
|
||||
}
|
||||
|
||||
fun userChangedItem(id: Id, value: Any?) {
|
||||
itemChanged(id, value)
|
||||
}
|
||||
|
||||
private fun itemChanged(id: Id, value: Any?) {
|
||||
itemsManager.itemChanged(id, value) { notifier.notifyItemsChanged(it) }
|
||||
}
|
||||
|
||||
//invokes Scan, Sign etc...
|
||||
fun invokeMainAction() {
|
||||
performAction(itemsManager, cardManager) { paramsManager, cardManager ->
|
||||
paramsManager.invokeMainAction(cardManager) { response, listOfChangedParams ->
|
||||
notifier.handleActionResult(response, listOfChangedParams)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getItemAction(id: Id): (() -> Unit)? {
|
||||
val itemFunction = itemsManager.getActionByTag(id, cardManager) ?: return null
|
||||
|
||||
return {
|
||||
itemFunction { response, listOfChangedParams ->
|
||||
notifier.handleActionResult(response, listOfChangedParams)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun attachToPayload(payload: Payload) {
|
||||
itemsManager.attachPayload(payload)
|
||||
}
|
||||
|
||||
@OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
|
||||
fun viewOnDestroy() {
|
||||
val keyList = mutableListOf<String>()
|
||||
itemsManager.payload.filterValues { it is ViewScreen }.forEach { keyList.add(it.key) }
|
||||
keyList.forEach { itemsManager.payload.remove(it) }
|
||||
}
|
||||
}
|
||||
|
||||
internal class Notifier(private val vm: ParamsViewModel) {
|
||||
|
||||
private var notShowedError: TaskError? = null
|
||||
private val gson: Gson = GsonInitializer().gson
|
||||
|
||||
fun handleActionResult(response: TaskEvent<*>, list: List<Item>) {
|
||||
if (list.isNotEmpty()) notifyItemsChanged(list)
|
||||
handleResponse(response)
|
||||
}
|
||||
|
||||
@UiThread
|
||||
fun notifyItemsChanged(list: List<Item>) {
|
||||
vm.seChangedItems.postValue(list)
|
||||
}
|
||||
|
||||
fun handleResponse(response: TaskEvent<*>) {
|
||||
val taskEvent = response as? TaskEvent<*> ?: return
|
||||
|
||||
when (taskEvent) {
|
||||
is TaskEvent.Completion -> handleCompletionEvent(taskEvent)
|
||||
is TaskEvent.Event -> handleDataEvent(taskEvent.data)
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleDataEvent(event: Any?) {
|
||||
when (event) {
|
||||
is ScanEvent.OnReadEvent -> {
|
||||
vm.ldCard.postValue(event.card)
|
||||
vm.ldReadResponse.postValue(gson.toJson(event))
|
||||
}
|
||||
is ScanEvent.OnVerifyEvent -> {
|
||||
vm.ldIsVerified.postValue(true)
|
||||
}
|
||||
else -> vm.ldResponse.postValue(gson.toJson(event))
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleCompletionEvent(taskEvent: TaskEvent.Completion<*>) {
|
||||
if (taskEvent.error == null) {
|
||||
Log.d(this, "error = null")
|
||||
if (notShowedError != null) {
|
||||
vm.seError.postValue("${notShowedError!!::class.simpleName}")
|
||||
notShowedError = null
|
||||
}
|
||||
} else {
|
||||
Log.d(this, "error = ${taskEvent.error}")
|
||||
when (taskEvent.error) {
|
||||
is TaskError.UserCancelled -> {
|
||||
if (notShowedError == null) {
|
||||
vm.seError.postValue("User canceled the action")
|
||||
} else {
|
||||
vm.seError.postValue("${notShowedError!!::class.simpleName}")
|
||||
notShowedError = null
|
||||
}
|
||||
}
|
||||
else -> notShowedError = taskEvent.error
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,119 @@
|
|||
package com.tangem.tangemtest.ucase.ui.widgets
|
||||
|
||||
import android.text.Editable
|
||||
import android.text.TextWatcher
|
||||
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.BaseItem
|
||||
import com.tangem.tangemtest._arch.structure.abstraction.Item
|
||||
import com.tangem.tangemtest.ucase.resources.ActionType
|
||||
import com.tangem.tangemtest.ucase.resources.MainResourceHolder
|
||||
import com.tangem.tangemtest.ucase.resources.Resources
|
||||
import ru.dev.gbixahue.eu4d.lib.android.global.log.Log
|
||||
import ru.dev.gbixahue.eu4d.lib.kotlin.stringOf
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
class ParameterWidget(
|
||||
private val parent: ViewGroup,
|
||||
item: Item
|
||||
) {
|
||||
|
||||
val id: Id = item.id
|
||||
val dataItem: BaseItem<Any> = item as BaseItem<Any>
|
||||
|
||||
var onValueChanged: ((Id, Any?) -> Unit)? = null
|
||||
var onActionBtnClickListener: (() -> Unit)? = null
|
||||
set(value) {
|
||||
field = value
|
||||
toggleActionBtnVisibility()
|
||||
}
|
||||
|
||||
private val tilValue: TextInputLayout = parent.findViewById(R.id.til_param)
|
||||
private val etValue: TextInputEditText = parent.findViewById(R.id.et_param)
|
||||
private val btnAction: Button = parent.findViewById(R.id.btn_action)
|
||||
private val valueWatcher: TextWatcher by lazy { getWatcher() }
|
||||
|
||||
private val descriptionContainer: ViewGroup by lazy { parent.findViewById<ViewGroup>(R.id.container_description) }
|
||||
private val tvDescription: TextView? by lazy { descriptionContainer.findViewById<TextView>(R.id.tv_description) }
|
||||
|
||||
private var actionBtnVisibilityState: Int = btnAction.visibility
|
||||
private var value: Any? = dataItem.viewModel.data
|
||||
|
||||
init {
|
||||
tilValue.hint = tilValue.context.getString(getResNameId())
|
||||
etValue.setText(stringOf(dataItem.viewModel.data))
|
||||
etValue.addTextChangedListener(valueWatcher)
|
||||
btnAction.setOnClickListener { onActionBtnClickListener?.invoke() }
|
||||
btnAction.setText(getResNameId(ActionType.Scan))
|
||||
toggleActionBtnVisibility()
|
||||
initDescriptionWidget()
|
||||
}
|
||||
|
||||
fun changeParamValue(data: Any?, silent: Boolean = true) {
|
||||
Log.d(this, "changeParamValue: tag: $id, value: $data")
|
||||
value = data
|
||||
toggleActionBtnVisibility()
|
||||
if (silent) {
|
||||
etValue.removeTextChangedListener(valueWatcher)
|
||||
etValue.setText(stringOf(value))
|
||||
etValue.addTextChangedListener(valueWatcher)
|
||||
} else {
|
||||
etValue.setText(stringOf(value))
|
||||
}
|
||||
}
|
||||
|
||||
fun toggleDescriptionVisibility(state: Boolean) {
|
||||
TransitionManager.beginDelayedTransition(parent.parent as ViewGroup, AutoTransition())
|
||||
descriptionContainer.visibility = if (state) View.VISIBLE else View.GONE
|
||||
}
|
||||
|
||||
private fun getWatcher(): TextWatcher {
|
||||
return object : TextWatcher {
|
||||
override fun afterTextChanged(editable: Editable?) {
|
||||
Log.d(this, "afterTextChanged $editable")
|
||||
value = if (editable.isNullOrEmpty()) null else editable.toString()
|
||||
toggleActionBtnVisibility()
|
||||
onValueChanged?.invoke(id, value)
|
||||
}
|
||||
|
||||
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
|
||||
|
||||
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {}
|
||||
}
|
||||
}
|
||||
|
||||
private fun toggleActionBtnVisibility() {
|
||||
fun switchVisibilityState(newState: Int) {
|
||||
actionBtnVisibilityState = newState
|
||||
TransitionManager.beginDelayedTransition(parent, AutoTransition())
|
||||
btnAction.visibility = actionBtnVisibilityState
|
||||
}
|
||||
when {
|
||||
onActionBtnClickListener == null -> {
|
||||
if (actionBtnVisibilityState == View.GONE) return
|
||||
switchVisibilityState(View.GONE)
|
||||
}
|
||||
value == null && actionBtnVisibilityState != View.VISIBLE -> switchVisibilityState(View.VISIBLE)
|
||||
value != null && actionBtnVisibilityState != View.GONE -> switchVisibilityState(View.GONE)
|
||||
}
|
||||
}
|
||||
|
||||
private fun initDescriptionWidget() {
|
||||
getResDescription()?.let { tvDescription?.setText(it) }
|
||||
}
|
||||
}
|
||||
|
||||
fun ParameterWidget.getResNameId(id: Id? = null): Int = MainResourceHolder.safeGet<Resources>(id
|
||||
?: this.id).resName
|
||||
|
||||
fun ParameterWidget.getResDescription(): Int? = MainResourceHolder.safeGet<Resources>(id).resDescription
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
package com.tangem.tangemtest.ucase.variants
|
||||
|
||||
import com.tangem.tangemtest._arch.structure.Id
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
enum class TlvId : Id {
|
||||
CardId,
|
||||
TransactionOutHash,
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
package com.tangem.tangemtest.ucase.variants.depersonalize.ui
|
||||
|
||||
import com.tangem.tangemtest.R
|
||||
import com.tangem.tangemtest.ucase.resources.ActionType
|
||||
import com.tangem.tangemtest.ucase.ui.BaseCardActionFragment
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
class DepersonalizeActionFragment : BaseCardActionFragment() {
|
||||
|
||||
override fun getLayoutId(): Int = R.layout.fg_depersonalize
|
||||
|
||||
override fun getAction(): ActionType = ActionType.Depersonalize
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,115 @@
|
|||
package com.tangem.tangemtest.ucase.variants.personalize
|
||||
|
||||
import com.tangem.tangemtest._arch.structure.Id
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
|
||||
interface PersonalizeId : Id
|
||||
|
||||
interface BlockItem : PersonalizeId
|
||||
|
||||
enum class BlockId : PersonalizeId {
|
||||
CardNumber,
|
||||
Common,
|
||||
SigningMethod,
|
||||
SignHashExProp,
|
||||
Denomination,
|
||||
Token,
|
||||
ProdMask,
|
||||
SettingsMask,
|
||||
SettingsMaskProtocolEnc,
|
||||
SettingsMaskNdef,
|
||||
Pins,
|
||||
}
|
||||
|
||||
enum class CardNumber : BlockItem {
|
||||
Series,
|
||||
Number,
|
||||
BatchId,
|
||||
}
|
||||
|
||||
enum class Common : BlockItem {
|
||||
Curve,
|
||||
Blockchain,
|
||||
BlockchainCustom,
|
||||
MaxSignatures,
|
||||
CreateWallet,
|
||||
}
|
||||
|
||||
enum class SigningMethod : BlockItem {
|
||||
SignTx,
|
||||
SignTxRaw,
|
||||
SignValidatedTx,
|
||||
SignValidatedTxRaw,
|
||||
SignValidatedTxIssuer,
|
||||
SignValidatedTxRawIssuer,
|
||||
SignExternal,
|
||||
}
|
||||
|
||||
enum class SignHashExProp : BlockItem {
|
||||
PinLessFloorLimit,
|
||||
CryptoExKey,
|
||||
RequireTerminalCertSig,
|
||||
RequireTerminalTxSig,
|
||||
CheckPin3,
|
||||
}
|
||||
|
||||
enum class Denomination : BlockItem {
|
||||
WriteOnPersonalize,
|
||||
Denomination,
|
||||
}
|
||||
|
||||
enum class Token : BlockItem {
|
||||
ItsToken,
|
||||
Symbol,
|
||||
ContractAddress,
|
||||
Decimal
|
||||
}
|
||||
|
||||
enum class ProductMask : BlockItem {
|
||||
Note,
|
||||
Tag,
|
||||
CardId
|
||||
}
|
||||
|
||||
enum class SettingsMask : BlockItem {
|
||||
IsReusable,
|
||||
NeedActivation,
|
||||
ForbidPurge,
|
||||
AllowSelectBlockchain,
|
||||
UseBlock,
|
||||
OneApdu,
|
||||
UseCvc,
|
||||
AllowSwapPin,
|
||||
AllowSwapPin2,
|
||||
ForbidDefaultPin,
|
||||
SmartSecurityDelay,
|
||||
ProtectIssuerDataAgainstReplay,
|
||||
SkipSecurityDelayIfValidated,
|
||||
SkipPin2CvcIfValidated,
|
||||
SkipSecurityDelayOnLinkedTerminal,
|
||||
RestrictOverwriteExtraIssuerData,
|
||||
}
|
||||
|
||||
enum class SettingsMaskProtocolEnc : BlockItem {
|
||||
AllowUnencrypted,
|
||||
AllowStaticEncryption
|
||||
}
|
||||
|
||||
enum class SettingsMaskNdef : BlockItem {
|
||||
UseNdef,
|
||||
DynamicNdef,
|
||||
DisablePrecomputedNdef,
|
||||
Aar,
|
||||
AarCustom
|
||||
}
|
||||
|
||||
enum class Pins : BlockItem {
|
||||
Pin,
|
||||
Pin2,
|
||||
Pin3,
|
||||
Cvc,
|
||||
PauseBeforePin2
|
||||
}
|
||||
|
|
@ -0,0 +1,135 @@
|
|||
package com.tangem.tangemtest.ucase.variants.personalize.converter
|
||||
|
||||
import com.tangem.commands.EllipticCurve
|
||||
import com.tangem.tangemtest._arch.structure.Id
|
||||
import com.tangem.tangemtest._arch.structure.impl.KeyValue
|
||||
import com.tangem.tangemtest.ucase.variants.personalize.*
|
||||
import com.tangem.tangemtest.ucase.variants.personalize.dto.PersonalizeConfig
|
||||
import ru.dev.gbixahue.eu4d.lib.kotlin.common.BaseTypedHolder
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
class PersonalizeConfigValuesHolder : BaseTypedHolder<Id, Value>() {
|
||||
|
||||
// All registered association values can't be objects
|
||||
fun init(default: PersonalizeConfig) {
|
||||
register(CardNumber.Series, Value(default.series))
|
||||
register(CardNumber.Number, Value(default.startNumber))
|
||||
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.MaxSignatures, Value(default.MaxSignatures))
|
||||
register(Common.CreateWallet, Value(default.createWallet))
|
||||
register(SigningMethod.SignTx, Value(default.SigningMethod0))
|
||||
register(SigningMethod.SignTxRaw, Value(default.SigningMethod1))
|
||||
register(SigningMethod.SignValidatedTx, Value(default.SigningMethod2))
|
||||
register(SigningMethod.SignValidatedTxRaw, Value(default.SigningMethod3))
|
||||
register(SigningMethod.SignValidatedTxIssuer, Value(default.SigningMethod4))
|
||||
register(SigningMethod.SignValidatedTxRawIssuer, Value(default.SigningMethod5))
|
||||
register(SigningMethod.SignExternal, Value(default.SigningMethod6))
|
||||
register(SignHashExProp.PinLessFloorLimit, Value(default.pinLessFloorLimit))
|
||||
register(SignHashExProp.CryptoExKey, Value(default.hexCrExKey))
|
||||
register(SignHashExProp.RequireTerminalCertSig, Value(default.requireTerminalCertSignature))
|
||||
register(SignHashExProp.RequireTerminalTxSig, Value(default.requireTerminalTxSignature))
|
||||
register(SignHashExProp.CheckPin3, Value(default.checkPIN3onCard))
|
||||
register(Denomination.WriteOnPersonalize, Value(default.writeOnPersonalization))
|
||||
register(Denomination.Denomination, Value(default.denomination))
|
||||
register(Token.ItsToken, Value(default.itsToken))
|
||||
register(Token.Symbol, Value(default.symbol))
|
||||
register(Token.ContractAddress, Value(default.contractAddress))
|
||||
register(Token.Decimal, Value(default.decimal))
|
||||
register(ProductMask.Note, Value(default.cardData.product_note))
|
||||
register(ProductMask.Tag, Value(default.cardData.product_tag))
|
||||
register(ProductMask.CardId, Value(default.cardData.product_id_card))
|
||||
register(SettingsMask.IsReusable, Value(default.isReusable))
|
||||
register(SettingsMask.NeedActivation, Value(default.useActivation))
|
||||
register(SettingsMask.ForbidPurge, Value(default.forbidPurgeWallet))
|
||||
register(SettingsMask.AllowSelectBlockchain, Value(default.allowSelectBlockchain))
|
||||
register(SettingsMask.UseBlock, Value(default.useBlock))
|
||||
register(SettingsMask.OneApdu, Value(default.oneApdu))
|
||||
register(SettingsMask.UseCvc, Value(default.useCVC))
|
||||
register(SettingsMask.AllowSwapPin, Value(default.allowSwapPIN))
|
||||
register(SettingsMask.AllowSwapPin2, Value(default.allowSwapPIN2))
|
||||
register(SettingsMask.ForbidDefaultPin, Value(default.forbidDefaultPIN))
|
||||
register(SettingsMask.SmartSecurityDelay, Value(default.smartSecurityDelay))
|
||||
register(SettingsMask.ProtectIssuerDataAgainstReplay, Value(default.protectIssuerDataAgainstReplay))
|
||||
register(SettingsMask.SkipSecurityDelayIfValidated, Value(default.skipSecurityDelayIfValidatedByIssuer))
|
||||
register(SettingsMask.SkipPin2CvcIfValidated, Value(default.skipCheckPIN2andCVCIfValidatedByIssuer))
|
||||
register(SettingsMask.SkipSecurityDelayOnLinkedTerminal, Value(default.skipSecurityDelayIfValidatedByLinkedTerminal))
|
||||
register(SettingsMask.RestrictOverwriteExtraIssuerData, Value(default.restrictOverwriteIssuerDataEx))
|
||||
register(SettingsMaskProtocolEnc.AllowUnencrypted, Value(default.protocolAllowUnencrypted))
|
||||
register(SettingsMaskProtocolEnc.AllowStaticEncryption, Value(default.protocolAllowStaticEncryption))
|
||||
register(SettingsMaskNdef.UseNdef, Value(default.useNDEF))
|
||||
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(Pins.Pin, Value(default.PIN))
|
||||
register(Pins.Pin2, Value(default.PIN2))
|
||||
register(Pins.Pin3, Value(default.PIN3))
|
||||
register(Pins.Cvc, Value(default.CVC))
|
||||
register(Pins.PauseBeforePin2, Value(default.pauseBeforePIN2, Helper.pauseBeforePin()))
|
||||
}
|
||||
}
|
||||
|
||||
class Value(
|
||||
private var value: Any? = null,
|
||||
val list: List<KeyValue>? = mutableListOf()
|
||||
) {
|
||||
fun get(): Any? = value
|
||||
fun set(newValue: Any?) {
|
||||
value = newValue
|
||||
}
|
||||
}
|
||||
|
||||
internal class Helper {
|
||||
companion object {
|
||||
fun listOfCurves(): List<KeyValue> {
|
||||
return EllipticCurve.values().map { KeyValue(it.name, it.curve) }
|
||||
}
|
||||
|
||||
fun listOfBlockchain(): List<KeyValue> {
|
||||
return mutableListOf(
|
||||
KeyValue("--- CUSTOM ---", ""),
|
||||
KeyValue("BTC", "BTC"),
|
||||
KeyValue("BTC/test", "BTC/test"),
|
||||
KeyValue("ETH", "ETH"),
|
||||
KeyValue("ETH/test", "ETH/test"),
|
||||
KeyValue("BCH", "BCH"),
|
||||
KeyValue("BCH/test", "BCH/test"),
|
||||
KeyValue("LTC", "LTC"),
|
||||
KeyValue("XLM", "XLM"),
|
||||
KeyValue("XLM/test", "XLM/test"),
|
||||
KeyValue("RSK", "RSK"),
|
||||
KeyValue("XPR", "XPR"),
|
||||
KeyValue("CARDANO", "CARDANO"),
|
||||
KeyValue("BNB", "BNB"),
|
||||
KeyValue("XTZ", "XTZ"),
|
||||
KeyValue("DUC", "DUC")
|
||||
)
|
||||
}
|
||||
|
||||
fun aarList(): List<KeyValue> {
|
||||
return mutableListOf(
|
||||
KeyValue("--- CUSTOM ---", ""),
|
||||
KeyValue("Release APP", "com.tangem.wallet"),
|
||||
KeyValue("Debug APP", "com.tangem.wallet.debug"),
|
||||
KeyValue("None", "")
|
||||
)
|
||||
}
|
||||
|
||||
fun pauseBeforePin(): List<KeyValue> {
|
||||
return mutableListOf(
|
||||
KeyValue("immediately", 0L),
|
||||
KeyValue("2 seconds", 2000L),
|
||||
KeyValue("5 seconds", 5000L),
|
||||
KeyValue("15 seconds", 15000L),
|
||||
KeyValue("30 seconds", 30000L),
|
||||
KeyValue("1 minute", 60000L),
|
||||
KeyValue("2 minute", 120000L)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
package com.tangem.tangemtest.ucase.variants.personalize.converter
|
||||
|
||||
import com.tangem.tangemtest._arch.structure.abstraction.Item
|
||||
import com.tangem.tangemtest._arch.structure.abstraction.ItemsToModel
|
||||
import com.tangem.tangemtest._arch.structure.abstraction.ModelConverter
|
||||
import com.tangem.tangemtest._arch.structure.abstraction.ModelToItems
|
||||
import com.tangem.tangemtest.ucase.variants.personalize.converter.fromTo.ItemsToPersonalizeConfig
|
||||
import com.tangem.tangemtest.ucase.variants.personalize.converter.fromTo.PersonalizeConfigToItem
|
||||
import com.tangem.tangemtest.ucase.variants.personalize.dto.PersonalizeConfig
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
class PersonalizeConfigConverter : ModelConverter<PersonalizeConfig> {
|
||||
|
||||
private val toModel: ItemsToModel<PersonalizeConfig> = ItemsToPersonalizeConfig()
|
||||
private val toItems: ModelToItems<PersonalizeConfig> = PersonalizeConfigToItem()
|
||||
|
||||
override fun convert(from: List<Item>, default: PersonalizeConfig): PersonalizeConfig {
|
||||
return toModel.convert(from, default)
|
||||
}
|
||||
|
||||
override fun convert(from: PersonalizeConfig): List<Item> {
|
||||
return toItems.convert(from)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
package com.tangem.tangemtest.ucase.variants.personalize.converter.fromTo
|
||||
|
||||
import com.tangem.tangemtest.ucase.variants.personalize.*
|
||||
|
||||
class ItemTypes {
|
||||
|
||||
val blockIdList = mutableListOf(
|
||||
BlockId.CardNumber, BlockId.Common, BlockId.SigningMethod, BlockId.SignHashExProp, BlockId.Denomination,
|
||||
BlockId.Token, BlockId.ProdMask, BlockId.SettingsMask, BlockId.SettingsMaskProtocolEnc,
|
||||
BlockId.SettingsMaskNdef, BlockId.Pins
|
||||
)
|
||||
|
||||
val listItemList = mutableListOf(Common.Curve, Common.Blockchain, SettingsMaskNdef.Aar, Pins.PauseBeforePin2)
|
||||
|
||||
val boolList = mutableListOf(
|
||||
Common.CreateWallet, SigningMethod.SignTx, SigningMethod.SignTxRaw, SigningMethod.SignValidatedTx,
|
||||
SigningMethod.SignValidatedTxRaw, SigningMethod.SignValidatedTxIssuer,
|
||||
SigningMethod.SignValidatedTxRawIssuer, SigningMethod.SignExternal, SignHashExProp.RequireTerminalCertSig,
|
||||
SignHashExProp.RequireTerminalTxSig, SignHashExProp.CheckPin3, Denomination.WriteOnPersonalize, Token.ItsToken,
|
||||
ProductMask.Note, ProductMask.Tag, ProductMask.CardId, SettingsMask.IsReusable, SettingsMask.NeedActivation,
|
||||
SettingsMask.ForbidPurge, SettingsMask.AllowSelectBlockchain, SettingsMask.UseBlock, SettingsMask.OneApdu,
|
||||
SettingsMask.UseCvc, SettingsMask.AllowSwapPin, SettingsMask.AllowSwapPin2, SettingsMask.ForbidDefaultPin,
|
||||
SettingsMask.SmartSecurityDelay, SettingsMask.ProtectIssuerDataAgainstReplay,
|
||||
SettingsMask.SkipSecurityDelayIfValidated, SettingsMask.SkipPin2CvcIfValidated,
|
||||
SettingsMask.SkipSecurityDelayOnLinkedTerminal, SettingsMask.RestrictOverwriteExtraIssuerData,
|
||||
SettingsMaskProtocolEnc.AllowUnencrypted, SettingsMaskProtocolEnc.AllowStaticEncryption,
|
||||
SettingsMaskNdef.UseNdef, SettingsMaskNdef.DynamicNdef, SettingsMaskNdef.DisablePrecomputedNdef
|
||||
)
|
||||
|
||||
val editTextList = mutableListOf(
|
||||
CardNumber.Series, CardNumber.BatchId, Common.BlockchainCustom, SignHashExProp.CryptoExKey, Token.Symbol,
|
||||
Token.ContractAddress, Pins.Pin, Pins.Pin2, Pins.Pin3, Pins.Cvc, SettingsMaskNdef.AarCustom
|
||||
)
|
||||
|
||||
val numberList = mutableListOf(
|
||||
CardNumber.Number, Common.MaxSignatures, SignHashExProp.PinLessFloorLimit, Denomination.Denomination, Token.Decimal
|
||||
)
|
||||
|
||||
val hiddenList = mutableListOf(
|
||||
CardNumber.Series, CardNumber.BatchId, Pins.Pin3, SigningMethod.SignExternal,
|
||||
SignHashExProp.CryptoExKey, SignHashExProp.CheckPin3, SettingsMask.OneApdu,
|
||||
SettingsMask.UseBlock, SettingsMask.ProtectIssuerDataAgainstReplay,
|
||||
SignHashExProp.RequireTerminalCertSig, SignHashExProp.RequireTerminalTxSig
|
||||
)
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,116 @@
|
|||
package com.tangem.tangemtest.ucase.variants.personalize.converter.fromTo
|
||||
|
||||
import com.tangem.tangemtest._arch.structure.Id
|
||||
import com.tangem.tangemtest._arch.structure.abstraction.BaseItem
|
||||
import com.tangem.tangemtest._arch.structure.abstraction.Block
|
||||
import com.tangem.tangemtest._arch.structure.abstraction.Item
|
||||
import com.tangem.tangemtest._arch.structure.abstraction.ItemsToModel
|
||||
import com.tangem.tangemtest._arch.structure.impl.KeyValue
|
||||
import com.tangem.tangemtest._arch.structure.impl.ListValueWrapper
|
||||
import com.tangem.tangemtest.ucase.variants.personalize.*
|
||||
import com.tangem.tangemtest.ucase.variants.personalize.converter.PersonalizeConfigValuesHolder
|
||||
import com.tangem.tangemtest.ucase.variants.personalize.dto.PersonalizeConfig
|
||||
import ru.dev.gbixahue.eu4d.lib.android.global.log.Log
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
class ItemsToPersonalizeConfig : ItemsToModel<PersonalizeConfig> {
|
||||
protected val valuesHolder = PersonalizeConfigValuesHolder()
|
||||
|
||||
override fun convert(from: List<Item>, default: PersonalizeConfig): PersonalizeConfig {
|
||||
valuesHolder.init(default)
|
||||
mapListItems(from)
|
||||
return createModel()
|
||||
}
|
||||
|
||||
private fun mapListItems(itemList: List<Item>) {
|
||||
itemList.forEach { item -> mapItemToHolder(item) }
|
||||
}
|
||||
|
||||
private fun mapItemToHolder(item: Item) {
|
||||
when (item) {
|
||||
is Block -> mapListItems(item.itemList)
|
||||
is BaseItem<*> -> {
|
||||
val defValue = valuesHolder.get(item.id) ?: return
|
||||
defValue.set(item.viewModel.data)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun createModel(): PersonalizeConfig {
|
||||
val export = PersonalizeConfig()
|
||||
export.series = getTyped(CardNumber.Series)
|
||||
export.startNumber = getTyped(CardNumber.Number)
|
||||
export.curveID = getTyped(Common.Curve)
|
||||
export.blockchain = getTyped(Common.Blockchain)
|
||||
export.blockchainCustom = getTyped(Common.BlockchainCustom)
|
||||
export.MaxSignatures = getTyped(Common.MaxSignatures)
|
||||
export.createWallet = getTyped(Common.CreateWallet)
|
||||
export.SigningMethod0 = getTyped(SigningMethod.SignTx)
|
||||
export.SigningMethod1 = getTyped(SigningMethod.SignTxRaw)
|
||||
export.SigningMethod2 = getTyped(SigningMethod.SignValidatedTx)
|
||||
export.SigningMethod3 = getTyped(SigningMethod.SignValidatedTxRaw)
|
||||
export.SigningMethod4 = getTyped(SigningMethod.SignValidatedTxIssuer)
|
||||
export.SigningMethod5 = getTyped(SigningMethod.SignValidatedTxRawIssuer)
|
||||
export.SigningMethod6 = getTyped(SigningMethod.SignExternal)
|
||||
export.pinLessFloorLimit = getTyped(SignHashExProp.PinLessFloorLimit)
|
||||
export.hexCrExKey = getTyped(SignHashExProp.CryptoExKey)
|
||||
export.requireTerminalCertSignature = getTyped(SignHashExProp.RequireTerminalCertSig)
|
||||
export.requireTerminalTxSignature = getTyped(SignHashExProp.RequireTerminalTxSig)
|
||||
export.checkPIN3onCard = getTyped(SignHashExProp.CheckPin3)
|
||||
export.writeOnPersonalization = getTyped(Denomination.WriteOnPersonalize)
|
||||
export.denomination = getTyped(Denomination.Denomination)
|
||||
export.itsToken = getTyped(Token.ItsToken)
|
||||
export.symbol = getTyped(Token.Symbol)
|
||||
export.contractAddress = getTyped(Token.ContractAddress)
|
||||
export.decimal = getTyped(Token.Decimal)
|
||||
export.cardData = export.cardData.apply { this.product_note = getTyped(ProductMask.Note) }
|
||||
export.cardData = export.cardData.apply { this.product_tag = getTyped(ProductMask.Tag) }
|
||||
export.cardData = export.cardData.apply { this.product_id_card = getTyped(ProductMask.CardId) }
|
||||
export.isReusable = getTyped(SettingsMask.IsReusable)
|
||||
export.useActivation = getTyped(SettingsMask.NeedActivation)
|
||||
export.forbidPurgeWallet = getTyped(SettingsMask.ForbidPurge)
|
||||
export.allowSelectBlockchain = getTyped(SettingsMask.AllowSelectBlockchain)
|
||||
export.useBlock = getTyped(SettingsMask.UseBlock)
|
||||
export.oneApdu = getTyped(SettingsMask.OneApdu)
|
||||
export.useCVC = getTyped(SettingsMask.UseCvc)
|
||||
export.allowSwapPIN = getTyped(SettingsMask.AllowSwapPin)
|
||||
export.allowSwapPIN2 = getTyped(SettingsMask.AllowSwapPin2)
|
||||
export.forbidDefaultPIN = getTyped(SettingsMask.ForbidDefaultPin)
|
||||
export.smartSecurityDelay = getTyped(SettingsMask.SmartSecurityDelay)
|
||||
export.protectIssuerDataAgainstReplay = getTyped(SettingsMask.ProtectIssuerDataAgainstReplay)
|
||||
export.skipSecurityDelayIfValidatedByIssuer = getTyped(SettingsMask.SkipSecurityDelayIfValidated)
|
||||
export.skipCheckPIN2andCVCIfValidatedByIssuer = getTyped(SettingsMask.SkipPin2CvcIfValidated)
|
||||
export.skipSecurityDelayIfValidatedByLinkedTerminal = getTyped(SettingsMask.SkipSecurityDelayOnLinkedTerminal)
|
||||
export.restrictOverwriteIssuerDataEx = getTyped(SettingsMask.RestrictOverwriteExtraIssuerData)
|
||||
export.protocolAllowUnencrypted = getTyped(SettingsMaskProtocolEnc.AllowUnencrypted)
|
||||
export.protocolAllowStaticEncryption = getTyped(SettingsMaskProtocolEnc.AllowStaticEncryption)
|
||||
export.useNDEF = getTyped(SettingsMaskNdef.UseNdef)
|
||||
export.useDynamicNDEF = getTyped(SettingsMaskNdef.DynamicNdef)
|
||||
export.disablePrecomputedNDEF = getTyped(SettingsMaskNdef.DisablePrecomputedNdef)
|
||||
export.aar = getTyped(SettingsMaskNdef.Aar)
|
||||
export.aarCustom = getTyped(SettingsMaskNdef.AarCustom)
|
||||
export.PIN = getTyped(Pins.Pin)
|
||||
export.PIN2 = getTyped(Pins.Pin2)
|
||||
export.PIN3 = getTyped(Pins.Pin3)
|
||||
export.CVC = getTyped(Pins.Cvc)
|
||||
export.pauseBeforePIN2 = getTyped(Pins.PauseBeforePin2)
|
||||
return export
|
||||
}
|
||||
|
||||
private inline fun <reified Type> getTyped(id: Id): Type {
|
||||
return getTypedBy<Type>(valuesHolder, id)!!
|
||||
}
|
||||
|
||||
private inline fun <reified Type> getTypedBy(holder: PersonalizeConfigValuesHolder, id: Id): Type? {
|
||||
Log.d(this, "getTyped for id: $id")
|
||||
var typedValue = holder.get(id)?.get()
|
||||
|
||||
typedValue = when (typedValue) {
|
||||
is ListValueWrapper -> (typedValue.selectedItem as KeyValue).value as Type
|
||||
else -> typedValue as? Type ?: null
|
||||
}
|
||||
return typedValue
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,298 @@
|
|||
package com.tangem.tangemtest.ucase.variants.personalize.converter.fromTo
|
||||
|
||||
import com.tangem.commands.CardData
|
||||
import com.tangem.commands.EllipticCurve
|
||||
import com.tangem.commands.ProductMaskBuilder
|
||||
import com.tangem.commands.personalization.entities.CardConfig
|
||||
import com.tangem.commands.personalization.entities.NdefRecord
|
||||
import com.tangem.tangemtest._arch.structure.Additional
|
||||
import com.tangem.tangemtest._arch.structure.Id
|
||||
import com.tangem.tangemtest._arch.structure.abstraction.*
|
||||
import com.tangem.tangemtest._arch.structure.impl.*
|
||||
import com.tangem.tangemtest.ucase.domain.paramsManager.PayloadKey
|
||||
import com.tangem.tangemtest.ucase.variants.personalize.*
|
||||
import com.tangem.tangemtest.ucase.variants.personalize.converter.PersonalizeConfigValuesHolder
|
||||
import com.tangem.tangemtest.ucase.variants.personalize.dto.PersonalizeConfig
|
||||
import ru.dev.gbixahue.eu4d.lib.kotlin.common.Converter
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
class PersonalizeConfigToItem : ModelToItems<PersonalizeConfig> {
|
||||
private val valuesHolder = PersonalizeConfigValuesHolder()
|
||||
private val itemTypes = ItemTypes()
|
||||
|
||||
override fun convert(from: PersonalizeConfig): List<Item> {
|
||||
valuesHolder.init(from)
|
||||
val blocList = mutableListOf<Item>()
|
||||
blocList.add(cardNumber())
|
||||
blocList.add(common())
|
||||
blocList.add(signingMethod())
|
||||
blocList.add(signHashExProperties())
|
||||
blocList.add(denomination())
|
||||
blocList.add(token())
|
||||
blocList.add(productMask())
|
||||
blocList.add(settingsMask())
|
||||
blocList.add(settingsMaskProtocolEnc())
|
||||
blocList.add(settingsMaskNdef())
|
||||
blocList.add(pins())
|
||||
val payloadBlock = ListItemBlock(Additional.JSON_TAILS)
|
||||
addPayload(payloadBlock, from)
|
||||
blocList.add(payloadBlock)
|
||||
blocList.iterate {
|
||||
if (itemTypes.hiddenList.contains(it.id)) {
|
||||
(it as BaseItem<*>)?.viewModel.viewState.isHiddenField = true
|
||||
}
|
||||
}
|
||||
return blocList
|
||||
}
|
||||
|
||||
private fun cardNumber(): Block {
|
||||
val block = createBlock(BlockId.CardNumber)
|
||||
mutableListOf(
|
||||
CardNumber.Series,
|
||||
CardNumber.Number,
|
||||
CardNumber.BatchId
|
||||
).forEach { createItem(block, it as Id) }
|
||||
return block
|
||||
}
|
||||
|
||||
private fun common(): Block {
|
||||
val block = createBlock(BlockId.Common)
|
||||
mutableListOf(
|
||||
Common.Curve,
|
||||
Common.Blockchain,
|
||||
Common.BlockchainCustom,
|
||||
Common.MaxSignatures,
|
||||
Common.CreateWallet
|
||||
).forEach { createItem(block, it) }
|
||||
return block
|
||||
}
|
||||
|
||||
private fun signingMethod(): Block {
|
||||
val block = createBlock(BlockId.SigningMethod)
|
||||
mutableListOf(
|
||||
SigningMethod.SignTx,
|
||||
SigningMethod.SignTxRaw,
|
||||
SigningMethod.SignValidatedTx,
|
||||
SigningMethod.SignValidatedTxRaw,
|
||||
SigningMethod.SignValidatedTxIssuer,
|
||||
SigningMethod.SignValidatedTxRawIssuer,
|
||||
SigningMethod.SignExternal
|
||||
).forEach { createItem(block, it) }
|
||||
return block
|
||||
}
|
||||
|
||||
private fun signHashExProperties(): Block {
|
||||
val block = createBlock(BlockId.SignHashExProp)
|
||||
mutableListOf(
|
||||
SignHashExProp.PinLessFloorLimit,
|
||||
SignHashExProp.CryptoExKey,
|
||||
SignHashExProp.RequireTerminalCertSig,
|
||||
SignHashExProp.RequireTerminalTxSig,
|
||||
SignHashExProp.CheckPin3
|
||||
).forEach { createItem(block, it) }
|
||||
return block
|
||||
}
|
||||
|
||||
private fun denomination(): Block {
|
||||
val block = createBlock(BlockId.Denomination)
|
||||
mutableListOf(
|
||||
Denomination.WriteOnPersonalize,
|
||||
Denomination.Denomination
|
||||
).forEach { createItem(block, it) }
|
||||
return block
|
||||
}
|
||||
|
||||
private fun token(): Block {
|
||||
val block = createBlock(BlockId.Token)
|
||||
mutableListOf(
|
||||
Token.ItsToken,
|
||||
Token.Symbol,
|
||||
Token.ContractAddress,
|
||||
Token.Decimal
|
||||
).forEach { createItem(block, it) }
|
||||
return block
|
||||
}
|
||||
|
||||
private fun productMask(): Block {
|
||||
val block = createBlock(BlockId.ProdMask)
|
||||
mutableListOf(
|
||||
ProductMask.Note,
|
||||
ProductMask.Tag,
|
||||
ProductMask.CardId
|
||||
).forEach { createItem(block, it) }
|
||||
return block
|
||||
}
|
||||
|
||||
private fun settingsMask(): Block {
|
||||
val block = createBlock(BlockId.SettingsMask)
|
||||
mutableListOf(
|
||||
SettingsMask.IsReusable,
|
||||
SettingsMask.NeedActivation,
|
||||
SettingsMask.ForbidPurge,
|
||||
SettingsMask.AllowSelectBlockchain,
|
||||
SettingsMask.UseBlock,
|
||||
SettingsMask.OneApdu,
|
||||
SettingsMask.UseCvc,
|
||||
SettingsMask.AllowSwapPin,
|
||||
SettingsMask.AllowSwapPin2,
|
||||
SettingsMask.ForbidDefaultPin,
|
||||
SettingsMask.SmartSecurityDelay,
|
||||
SettingsMask.ProtectIssuerDataAgainstReplay,
|
||||
SettingsMask.SkipSecurityDelayIfValidated,
|
||||
SettingsMask.SkipPin2CvcIfValidated,
|
||||
SettingsMask.SkipSecurityDelayOnLinkedTerminal,
|
||||
SettingsMask.RestrictOverwriteExtraIssuerData
|
||||
).forEach { createItem(block, it) }
|
||||
return block
|
||||
}
|
||||
|
||||
private fun settingsMaskProtocolEnc(): Block {
|
||||
val block = createBlock(BlockId.SettingsMaskProtocolEnc)
|
||||
mutableListOf(
|
||||
SettingsMaskProtocolEnc.AllowUnencrypted,
|
||||
SettingsMaskProtocolEnc.AllowStaticEncryption
|
||||
).forEach { createItem(block, it) }
|
||||
return block
|
||||
}
|
||||
|
||||
private fun settingsMaskNdef(): Block {
|
||||
val block = createBlock(BlockId.SettingsMaskNdef)
|
||||
mutableListOf(
|
||||
SettingsMaskNdef.UseNdef,
|
||||
SettingsMaskNdef.DynamicNdef,
|
||||
SettingsMaskNdef.DisablePrecomputedNdef,
|
||||
SettingsMaskNdef.Aar,
|
||||
SettingsMaskNdef.AarCustom
|
||||
).forEach { createItem(block, it) }
|
||||
return block
|
||||
}
|
||||
|
||||
private fun pins(): Block {
|
||||
val block = createBlock(BlockId.Pins)
|
||||
mutableListOf(Pins.Pin, Pins.Pin2, Pins.Pin3, Pins.Cvc, Pins.PauseBeforePin2)
|
||||
.forEach { createItem(block, it) }
|
||||
return block
|
||||
}
|
||||
|
||||
private fun createBlock(id: Id): ListItemBlock {
|
||||
return ListItemBlock(id).apply { addItem(TextItem(id)) }
|
||||
}
|
||||
|
||||
private fun addPayload(block: Block, from: PersonalizeConfig) {
|
||||
block.payload[PayloadKey.incomingJson] = from
|
||||
}
|
||||
|
||||
private fun createItem(block: ListItemBlock, id: Id) {
|
||||
val holder = valuesHolder.get(id) ?: return
|
||||
val item = when {
|
||||
itemTypes.blockIdList.contains(id) -> TextItem(id, holder.get() as? String)
|
||||
itemTypes.listItemList.contains(id) -> ListItem(id, holder.list as List<KeyValue>, holder.get())
|
||||
itemTypes.boolList.contains(id) -> BoolItem(id, holder.get() as? Boolean)
|
||||
itemTypes.editTextList.contains(id) -> EditTextItem(id, holder.get() as? String)
|
||||
itemTypes.numberList.contains(id) -> NumberItem(id, holder.get() as? Number)
|
||||
else -> ListItemBlock(Additional.UNDEFINED)
|
||||
}
|
||||
block.addItem(item)
|
||||
}
|
||||
}
|
||||
|
||||
class PersonalizeConfigToCardConfig : Converter<PersonalizeConfig, CardConfig> {
|
||||
|
||||
override fun convert(from: PersonalizeConfig): CardConfig {
|
||||
val signingMethod = com.tangem.commands.SigningMethod.build(
|
||||
signHash = from.SigningMethod0,
|
||||
signRaw = from.SigningMethod1,
|
||||
signHashValidatedByIssuer = from.SigningMethod2,
|
||||
signRawValidatedByIssuer = from.SigningMethod3,
|
||||
signHashValidatedByIssuerAndWriteIssuerData = from.SigningMethod4,
|
||||
signRawValidatedByIssuerAndWriteIssuerData = from.SigningMethod5,
|
||||
signPos = from.SigningMethod6
|
||||
)
|
||||
|
||||
val isNote = from.cardData.product_note
|
||||
val isTag = from.cardData.product_tag
|
||||
val isIdCard = from.cardData.product_id_card
|
||||
|
||||
val productMaskBuilder = ProductMaskBuilder()
|
||||
if (isNote) productMaskBuilder.add(com.tangem.commands.ProductMask.note)
|
||||
if (isTag) productMaskBuilder.add(com.tangem.commands.ProductMask.tag)
|
||||
if (isIdCard) productMaskBuilder.add(com.tangem.commands.ProductMask.idCard)
|
||||
val productMask = productMaskBuilder.build()
|
||||
|
||||
var tokenSymbol: String? = null
|
||||
var tokenContractAddress: String? = null
|
||||
var tokenDecimal: Int? = null
|
||||
if (from.itsToken) {
|
||||
tokenSymbol = from.symbol
|
||||
tokenContractAddress = from.contractAddress
|
||||
tokenDecimal = from.decimal.toInt()
|
||||
}
|
||||
|
||||
val blockchain = if (from.blockchain.isNotEmpty()) from.blockchain else from.blockchainCustom
|
||||
val cardData = CardData(
|
||||
blockchainName = blockchain,
|
||||
batchId = from.batchId,
|
||||
productMask = productMask,
|
||||
tokenSymbol = tokenSymbol,
|
||||
tokenContractAddress = tokenContractAddress,
|
||||
tokenDecimal = tokenDecimal,
|
||||
issuerName = null,
|
||||
manufactureDateTime = Calendar.getInstance().time,
|
||||
manufacturerSignature = null)
|
||||
|
||||
|
||||
val ndefs = mutableListOf<NdefRecord>()
|
||||
when (from.aar) {
|
||||
"None" -> null
|
||||
"--- CUSTOM ---" -> NdefRecord(NdefRecord.Type.AAR, from.aarCustom)
|
||||
else -> NdefRecord(NdefRecord.Type.AAR, from.aar)
|
||||
}?.let { ndefs.add(it) }
|
||||
|
||||
return CardConfig(
|
||||
"Tangem",
|
||||
"Tangem Test",
|
||||
from.series,
|
||||
from.startNumber,
|
||||
1000,
|
||||
from.PIN,
|
||||
from.PIN2,
|
||||
from.PIN3,
|
||||
from.hexCrExKey,
|
||||
from.CVC,
|
||||
from.pauseBeforePIN2.toInt(),
|
||||
from.smartSecurityDelay,
|
||||
EllipticCurve.byName(from.curveID) ?: EllipticCurve.Secp256k1,
|
||||
signingMethod,
|
||||
from.MaxSignatures.toInt(),
|
||||
from.isReusable,
|
||||
from.allowSwapPIN,
|
||||
from.allowSwapPIN2,
|
||||
from.useActivation,
|
||||
from.useCVC,
|
||||
from.useNDEF,
|
||||
from.useDynamicNDEF,
|
||||
from.oneApdu,
|
||||
from.useBlock,
|
||||
from.allowSelectBlockchain,
|
||||
from.forbidPurgeWallet,
|
||||
from.protocolAllowUnencrypted,
|
||||
from.protocolAllowStaticEncryption,
|
||||
from.protectIssuerDataAgainstReplay,
|
||||
from.forbidDefaultPIN,
|
||||
from.disablePrecomputedNDEF,
|
||||
from.skipSecurityDelayIfValidatedByIssuer,
|
||||
from.skipCheckPIN2andCVCIfValidatedByIssuer,
|
||||
from.skipSecurityDelayIfValidatedByLinkedTerminal,
|
||||
from.restrictOverwriteIssuerDataEx,
|
||||
from.requireTerminalTxSignature,
|
||||
from.requireTerminalCertSignature,
|
||||
from.checkPIN3onCard,
|
||||
from.createWallet,
|
||||
cardData,
|
||||
ndefs
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
package com.tangem.tangemtest.ucase.variants.personalize.dto
|
||||
|
||||
import com.tangem.commands.personalization.entities.Acquirer
|
||||
import com.tangem.commands.personalization.entities.Issuer
|
||||
import com.tangem.commands.personalization.entities.Manufacturer
|
||||
import com.tangem.common.KeyPair
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
|
||||
interface DefaultPersonalizeParams {
|
||||
companion object {
|
||||
|
||||
fun issuer(): Issuer {
|
||||
val name = "TANGEM SDK"
|
||||
return Issuer(
|
||||
name = name,
|
||||
id = name + "\u0000",
|
||||
dataKeyPair = KeyPair(
|
||||
"".toByteArray(),
|
||||
"11121314151617184771ED81F2BACF57479E4735EB1405083927372D40DA9E92".toByteArray()
|
||||
),
|
||||
transactionKeyPair = KeyPair(
|
||||
"".toByteArray(),
|
||||
"11121314151617184771ED81F2BACF57479E4735EB1405081918171615141312".toByteArray()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
fun acquirer(): Acquirer {
|
||||
val name = "Smart Cash"
|
||||
return Acquirer(
|
||||
name = name,
|
||||
id = name + "\u0000",
|
||||
keyPair = KeyPair(
|
||||
"".toByteArray(),
|
||||
"21222324252627284771ED81F2BACF57479E4735EB1405083927372D40DA9E92".toByteArray()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
fun manufacturer(): Manufacturer {
|
||||
val keys = Manufacturer(com.tangem.tangemtest.ucase.variants.personalize.dto.Manufacturer.Mode.Developer)
|
||||
return Manufacturer(
|
||||
name = "Tangem",
|
||||
keyPair = KeyPair(keys.publicKey, keys.privateKey)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
package com.tangem.tangemtest.ucase.variants.personalize.dto
|
||||
|
||||
class Manufacturer(private val mode: Mode) {
|
||||
|
||||
private val devPrivateKeyArray = byteArrayOf(
|
||||
0x1B.toByte(), 0x48.toByte(), 0xCF.toByte(), 0xD2.toByte(), 0x4B.toByte(), 0xBB.toByte(), 0x5B.toByte(), 0x39.toByte(),
|
||||
0x47.toByte(), 0x71.toByte(), 0xED.toByte(), 0x81.toByte(), 0xF2.toByte(), 0xBA.toByte(), 0xCF.toByte(), 0x57.toByte(),
|
||||
0x47.toByte(), 0x9E.toByte(), 0x47.toByte(), 0x35.toByte(), 0xEB.toByte(), 0x14.toByte(), 0x05.toByte(), 0x08.toByte(),
|
||||
0x39.toByte(), 0x27.toByte(), 0x37.toByte(), 0x2D.toByte(), 0x40.toByte(), 0xDA.toByte(), 0x9E.toByte(), 0x92.toByte()
|
||||
)
|
||||
private val devPublicKeyArray = byteArrayOf(0x04,
|
||||
0xBA.toByte(), 0xB8.toByte(), 0x6D.toByte(), 0x56.toByte(), 0x29.toByte(), 0x8C.toByte(), 0x99.toByte(), 0x6F.toByte(),
|
||||
0x56.toByte(), 0x4A.toByte(), 0x84.toByte(), 0xFC.toByte(), 0x88.toByte(), 0xE2.toByte(), 0x8A.toByte(), 0xED.toByte(),
|
||||
0x38.toByte(), 0x18.toByte(), 0x4B.toByte(), 0x12.toByte(), 0xF0.toByte(), 0x7E.toByte(), 0x51.toByte(), 0x91.toByte(),
|
||||
0x13.toByte(), 0xBE.toByte(), 0xF4.toByte(), 0x8C.toByte(), 0x76.toByte(), 0xF3.toByte(), 0xDF.toByte(), 0x3A.toByte(),
|
||||
0xDC.toByte(), 0x30.toByte(), 0x35.toByte(), 0x99.toByte(), 0xB0.toByte(), 0x8A.toByte(), 0xC0.toByte(), 0x5B.toByte(),
|
||||
0x55.toByte(), 0xEC.toByte(), 0x3D.toByte(), 0xF9.toByte(), 0x8D.toByte(), 0x93.toByte(), 0x38.toByte(), 0x57.toByte(),
|
||||
0x3A.toByte(), 0x62.toByte(), 0x42.toByte(), 0xF7.toByte(), 0x6F.toByte(), 0x5D.toByte(), 0x28.toByte(), 0xF4.toByte(),
|
||||
0xF0.toByte(), 0xF3.toByte(), 0x64.toByte(), 0xE8.toByte(), 0x7E.toByte(), 0x8F.toByte(), 0xCA.toByte(), 0x2F.toByte()
|
||||
)
|
||||
private val releasePublicKeyArray = byteArrayOf(0x04.toByte(),
|
||||
0x2E.toByte(), 0xDE.toByte(), 0x11.toByte(), 0x9B.toByte(), 0xF3.toByte(), 0x37.toByte(), 0xB2.toByte(), 0x64.toByte(),
|
||||
0xFD.toByte(), 0xA1.toByte(), 0x32.toByte(), 0xCF.toByte(), 0xC7.toByte(), 0xC1.toByte(), 0x77.toByte(), 0x82.toByte(),
|
||||
0x4D.toByte(), 0x36.toByte(), 0x17.toByte(), 0xDA.toByte(), 0xC8.toByte(), 0x0F.toByte(), 0x25.toByte(), 0xDB.toByte(),
|
||||
0xB2.toByte(), 0xA4.toByte(), 0xA8.toByte(), 0xA1.toByte(), 0x18.toByte(), 0x3C.toByte(), 0x03.toByte(), 0xB9.toByte(),
|
||||
0x15.toByte(), 0x23.toByte(), 0x05.toByte(), 0xF8.toByte(), 0xF1.toByte(), 0xDB.toByte(), 0x97.toByte(), 0x00.toByte(),
|
||||
0x45.toByte(), 0x18.toByte(), 0x48.toByte(), 0x0D.toByte(), 0x50.toByte(), 0x91.toByte(), 0xAD.toByte(), 0xC1.toByte(),
|
||||
0xCA.toByte(), 0xB9.toByte(), 0xEA.toByte(), 0xCC.toByte(), 0xC1.toByte(), 0x8E.toByte(), 0x1B.toByte(), 0x9E.toByte(),
|
||||
0x9C.toByte(), 0x3B.toByte(), 0xEF.toByte(), 0xB2.toByte(), 0x93.toByte(), 0xDD.toByte(), 0x37.toByte(), 0xB2.toByte()
|
||||
)
|
||||
|
||||
val publicKey: ByteArray
|
||||
get() = if (mode == Mode.Developer) {
|
||||
devPublicKeyArray
|
||||
} else {
|
||||
releasePublicKeyArray
|
||||
}
|
||||
|
||||
@get:Throws(Exception::class)
|
||||
val privateKey: ByteArray
|
||||
get() = if (mode == Mode.Developer) {
|
||||
devPrivateKeyArray
|
||||
} else {
|
||||
throw Exception("Real manufacturer key is very secret!")
|
||||
}
|
||||
|
||||
val name: String
|
||||
get() = if (mode == Mode.Release) "SMART CASH" else "SMART CASH SDK"
|
||||
|
||||
enum class Mode {
|
||||
Developer, Release
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,108 @@
|
|||
package com.tangem.tangemtest.ucase.variants.personalize.dto
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
class PersonalizeConfig {
|
||||
|
||||
// Card number
|
||||
var series = "BB"
|
||||
var startNumber: Long = 300000000000
|
||||
var batchId = "ffff"
|
||||
|
||||
|
||||
// Common
|
||||
var curveID = "ed25519"
|
||||
var blockchain = "BTC/test"
|
||||
var blockchainCustom = ""
|
||||
var MaxSignatures: Long = 100
|
||||
var createWallet = false
|
||||
|
||||
// Signing method
|
||||
var SigningMethod0 = true
|
||||
var SigningMethod1 = false
|
||||
var SigningMethod2 = false
|
||||
var SigningMethod3 = false
|
||||
var SigningMethod4 = false
|
||||
var SigningMethod5 = false
|
||||
var SigningMethod6 = false
|
||||
|
||||
|
||||
// Sign hash external properties
|
||||
var pinLessFloorLimit: Long = 100000
|
||||
var hexCrExKey = "00112233445566778899AABBCCDDEEFFFFEEDDCCBBAA998877665544332211000000111122223333444455556666777788889999AAAABBBBCCCCDDDDEEEEFFFF"
|
||||
var requireTerminalTxSignature = false
|
||||
var requireTerminalCertSignature = false
|
||||
var checkPIN3onCard = true
|
||||
|
||||
|
||||
// Denomination
|
||||
var writeOnPersonalization = false
|
||||
var denomination: Long = 1000000
|
||||
|
||||
|
||||
// Token
|
||||
var itsToken = false
|
||||
var symbol = ""
|
||||
var contractAddress = ""
|
||||
var decimal: Long = 0
|
||||
|
||||
|
||||
var cardData = CardData()
|
||||
|
||||
|
||||
// Settings mask
|
||||
var isReusable = true
|
||||
var useActivation = false
|
||||
var forbidPurgeWallet = false
|
||||
var allowSelectBlockchain = false
|
||||
var useBlock = false
|
||||
var oneApdu = false
|
||||
var useCVC = false
|
||||
var allowSwapPIN = true
|
||||
var allowSwapPIN2 = true
|
||||
var forbidDefaultPIN = false
|
||||
var smartSecurityDelay = false
|
||||
var protectIssuerDataAgainstReplay = true
|
||||
var skipSecurityDelayIfValidatedByIssuer = true
|
||||
var skipCheckPIN2andCVCIfValidatedByIssuer = true
|
||||
var skipSecurityDelayIfValidatedByLinkedTerminal = true
|
||||
var restrictOverwriteIssuerDataEx = false
|
||||
|
||||
|
||||
// Settings mask - protocol encryption
|
||||
var protocolAllowUnencrypted = true
|
||||
var protocolAllowStaticEncryption = true
|
||||
|
||||
|
||||
var useNDEF = true
|
||||
var useDynamicNDEF = true
|
||||
var disablePrecomputedNDEF = false
|
||||
var aar = "com.tangem.wallet"
|
||||
var aarCustom = ""
|
||||
|
||||
|
||||
// Pins
|
||||
var PIN = "000000"
|
||||
var PIN2 = "000"
|
||||
var PIN3 = ""
|
||||
var CVC = "000"
|
||||
var pauseBeforePIN2: Long = 15000
|
||||
|
||||
|
||||
// not used
|
||||
// var count: Long = 20
|
||||
// var numberFormat = ""
|
||||
// var issuerData = null
|
||||
// var releaseVersion = false
|
||||
// var issuerName = "TANGEM SDK"
|
||||
}
|
||||
|
||||
class CardData {
|
||||
var date = "2020-02-17"
|
||||
var batch = "FF87"
|
||||
var blockchain = "IROHA"
|
||||
var product_note = true
|
||||
var product_tag = false
|
||||
var product_id_card = false
|
||||
}
|
||||
|
|
@ -0,0 +1,116 @@
|
|||
package com.tangem.tangemtest.ucase.variants.personalize.ui
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.LinearLayout
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.lifecycle.Observer
|
||||
import com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
import com.google.android.material.snackbar.BaseTransientBottomBar
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import com.tangem.CardManager
|
||||
import com.tangem.tangem_sdk_new.extensions.init
|
||||
import com.tangem.tangemtest.R
|
||||
import com.tangem.tangemtest._arch.structure.Id
|
||||
import com.tangem.tangemtest._arch.structure.abstraction.Item
|
||||
import com.tangem.tangemtest._main.MainViewModel
|
||||
import com.tangem.tangemtest.ucase.domain.paramsManager.ItemsManager
|
||||
import com.tangem.tangemtest.ucase.domain.paramsManager.ParamsManagerFactory
|
||||
import com.tangem.tangemtest.ucase.domain.paramsManager.PayloadKey
|
||||
import com.tangem.tangemtest.ucase.resources.ActionType
|
||||
import com.tangem.tangemtest.ucase.tunnel.ActionView
|
||||
import com.tangem.tangemtest.ucase.tunnel.CardError
|
||||
import com.tangem.tangemtest.ucase.tunnel.ItemError
|
||||
import com.tangem.tangemtest.ucase.ui.ActionViewModelFactory
|
||||
import com.tangem.tangemtest.ucase.ui.ParamsViewModel
|
||||
import com.tangem.tangemtest.ucase.variants.personalize.dto.PersonalizeConfig
|
||||
import com.tangem.tangemtest.ucase.variants.personalize.ui.widgets.WidgetBuilder
|
||||
import ru.dev.gbixahue.eu4d.lib.android.global.log.Log
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
class PersonalizeFragment : Fragment(), ActionView {
|
||||
|
||||
private val mainActivityVM: MainViewModel by activityViewModels()
|
||||
private val personalizeVM: PersonalizeViewModel by viewModels { PersonalizeViewModelFactory(PersonalizeConfig()) }
|
||||
|
||||
private val itemsManager: ItemsManager by lazy { ParamsManagerFactory.createFactory().get(ActionType.Personalize)!! }
|
||||
private val paramsVM: ParamsViewModel by viewModels() { ActionViewModelFactory(itemsManager) }
|
||||
|
||||
private lateinit var mainView: View
|
||||
private val blockContainer: ViewGroup by lazy { mainView.findViewById<LinearLayout>(R.id.ll_container) }
|
||||
private val actionFab: FloatingActionButton by lazy { mainView.findViewById<FloatingActionButton>(R.id.fab_action) }
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
Log.d(this, "onCreateView")
|
||||
mainView = inflater.inflate(R.layout.fg_personalize, container, false)
|
||||
return mainView
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
val blockList = mutableListOf<Item>()
|
||||
personalizeVM.ldBlockList.observe(viewLifecycleOwner, Observer { list ->
|
||||
blockList.clear()
|
||||
blockList.addAll(list)
|
||||
blockList.forEach { WidgetBuilder().build(it, blockContainer) }
|
||||
paramsVM.attachToPayload(mutableMapOf(
|
||||
PayloadKey.actionView to this as ActionView,
|
||||
PayloadKey.itemList to blockList
|
||||
))
|
||||
|
||||
})
|
||||
mainActivityVM.ldDescriptionSwitch.observe(viewLifecycleOwner, Observer {
|
||||
personalizeVM.toggleDescriptionVisibility(it)
|
||||
})
|
||||
|
||||
val cardManager = CardManager.init(requireActivity())
|
||||
paramsVM.setCardManager(cardManager)
|
||||
paramsVM.ldResponse.observe(viewLifecycleOwner, Observer {
|
||||
Log.d(this, "action response: ${if (it.length > 50) it.substring(0..50) else it}")
|
||||
showSnackbarMessage(it)
|
||||
})
|
||||
paramsVM.seError.observe(viewLifecycleOwner, Observer { showSnackbarMessage(it) })
|
||||
|
||||
actionFab.setOnClickListener { paramsVM.invokeMainAction() }
|
||||
}
|
||||
|
||||
protected fun showSnackbarMessage(message: String) {
|
||||
Snackbar.make(mainView, message, BaseTransientBottomBar.LENGTH_SHORT).show()
|
||||
}
|
||||
|
||||
override fun showActionFab(show: Boolean) {
|
||||
if (show) actionFab.show() else actionFab.hide()
|
||||
}
|
||||
|
||||
override fun showSnackbar(id: Id) {
|
||||
// MainResourceHolder.safeGet<>()
|
||||
when (id) {
|
||||
CardError.NotPersonalized -> showSnackbar(R.string.card_error_not_personalized)
|
||||
ItemError.BadSeries -> showSnackbar(R.string.card_error_bad_series)
|
||||
ItemError.BadCardNumber -> showSnackbar(R.string.card_error_bad_series_number)
|
||||
else -> showSnackbar(requireContext().getString(R.string.unknown))
|
||||
}
|
||||
}
|
||||
|
||||
override fun showSnackbar(id: Int) {
|
||||
showSnackbar(requireContext().getString(id))
|
||||
}
|
||||
|
||||
override fun showSnackbar(message: String) {
|
||||
Snackbar.make(mainView, message, BaseTransientBottomBar.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
package com.tangem.tangemtest.ucase.variants.personalize.ui
|
||||
|
||||
import android.view.View
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import com.tangem.tangemtest._arch.structure.abstraction.BaseItem
|
||||
import com.tangem.tangemtest._arch.structure.abstraction.Item
|
||||
import com.tangem.tangemtest._arch.structure.abstraction.iterate
|
||||
import com.tangem.tangemtest.ucase.variants.personalize.converter.PersonalizeConfigConverter
|
||||
import com.tangem.tangemtest.ucase.variants.personalize.dto.PersonalizeConfig
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
class PersonalizeViewModelFactory(private val config: PersonalizeConfig) : ViewModelProvider.NewInstanceFactory() {
|
||||
override fun <T : ViewModel?> create(modelClass: Class<T>): T = PersonalizeViewModel(config) as T
|
||||
}
|
||||
|
||||
class PersonalizeViewModel(private val config: PersonalizeConfig) : ViewModel() {
|
||||
|
||||
val ldBlockList: MutableLiveData<List<Item>> by lazy { MutableLiveData(initBlockList()) }
|
||||
|
||||
private fun initBlockList(): List<Item> = PersonalizeConfigConverter().convert(config)
|
||||
|
||||
fun toggleDescriptionVisibility(state: Boolean) {
|
||||
ldBlockList.value?.iterate {
|
||||
val baseItem = it as? BaseItem<*> ?: return@iterate
|
||||
baseItem.viewModel.viewState.descriptionVisibility = if (state) View.VISIBLE else View.GONE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
package com.tangem.tangemtest.ucase.variants.personalize.ui.widgets
|
||||
|
||||
import android.view.ViewGroup
|
||||
import com.tangem.tangemtest._arch.structure.abstraction.BaseItem
|
||||
import com.tangem.tangemtest._arch.structure.abstraction.Block
|
||||
import com.tangem.tangemtest._arch.structure.abstraction.Item
|
||||
import com.tangem.tangemtest._arch.structure.abstraction.ListItemBlock
|
||||
import com.tangem.tangemtest._arch.structure.impl.*
|
||||
import com.tangem.tangemtest.ucase.variants.personalize.ui.widgets.abstraction.ViewWidget
|
||||
import com.tangem.tangemtest.ucase.variants.personalize.ui.widgets.impl.StubWidget
|
||||
import com.tangem.tangemtest.ucase.variants.personalize.ui.widgets.impl.block.LinearBlockWidget
|
||||
import com.tangem.tangemtest.ucase.variants.personalize.ui.widgets.impl.item.*
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
class WidgetBuilder {
|
||||
|
||||
fun build(item: Item, parent: ViewGroup): ViewWidget? {
|
||||
return when (item) {
|
||||
is Block -> buildBlock(item, parent)
|
||||
is BaseItem<*> -> buildUnitItem(item, parent)
|
||||
else -> StubWidget(parent)
|
||||
}
|
||||
}
|
||||
|
||||
private fun buildBlock(block: Block, parent: ViewGroup): ViewWidget {
|
||||
return when (block) {
|
||||
is ListItemBlock -> {
|
||||
val linearBlock = LinearBlockWidget(parent, block)
|
||||
block.getItems().forEach { build(it, linearBlock.view as ViewGroup) }
|
||||
linearBlock
|
||||
}
|
||||
else -> StubWidget(parent)
|
||||
}
|
||||
}
|
||||
|
||||
private fun buildUnitItem(item: BaseItem<*>, parent: ViewGroup): ViewWidget? {
|
||||
return when (item) {
|
||||
is TextItem -> TextWidget(parent, item)
|
||||
is EditTextItem -> EditTextWidget(parent, item)
|
||||
is NumberItem -> NumberWidget(parent, item)
|
||||
is BoolItem -> SwitchWidget(parent, item)
|
||||
is ListItem -> SpinnerWidget(parent, item)
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
package com.tangem.tangemtest.ucase.variants.personalize.ui.widgets.abstraction
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import com.tangem.tangemtest.R
|
||||
import com.tangem.tangemtest._arch.structure.DataHolder
|
||||
import com.tangem.tangemtest._arch.structure.abstraction.BaseItem
|
||||
import com.tangem.tangemtest._arch.structure.abstraction.ItemViewModel
|
||||
import com.tangem.tangemtest.ucase.resources.MainResourceHolder
|
||||
import com.tangem.tangemtest.ucase.resources.Resources
|
||||
import ru.dev.gbixahue.eu4d.lib.kotlin.common.LayoutHolder
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
interface ViewWidget : LayoutHolder {
|
||||
val view: View
|
||||
}
|
||||
|
||||
interface DataWidget<D> : ViewWidget {
|
||||
var dataItem: BaseItem<D>
|
||||
}
|
||||
|
||||
interface BlockViewWidget : ViewWidget, DataHolder<List<ItemViewModel<*>>>
|
||||
|
||||
abstract class BaseWidget<D>(
|
||||
parent: ViewGroup,
|
||||
override var dataItem: BaseItem<D>
|
||||
) : ViewWidget, DataWidget<D> {
|
||||
|
||||
override val view: View = inflate(getLayoutId(), parent)
|
||||
|
||||
init {
|
||||
if (dataItem.viewModel.viewState.isHiddenField) {
|
||||
view.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
abstract class BaseBlockWidget(parent: ViewGroup) : BlockViewWidget {
|
||||
override val view: View = inflate(getLayoutId(), parent)
|
||||
}
|
||||
|
||||
fun DataWidget<*>.getResNameId(): Int = MainResourceHolder.safeGet<Resources>(dataItem.id).resName
|
||||
|
||||
fun DataWidget<*>.getResDescription(): Int? = MainResourceHolder.safeGet<Resources>(dataItem.id).resDescription
|
||||
|
||||
internal fun inflate(id: Int, parent: ViewGroup): View {
|
||||
val layoutId = if (id <= 0) R.layout.w_empty else id
|
||||
val inflatedView = LayoutInflater.from(parent.context).inflate(layoutId, parent, false)
|
||||
parent.addView(inflatedView)
|
||||
return inflatedView
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
package com.tangem.tangemtest.ucase.variants.personalize.ui.widgets.impl
|
||||
|
||||
import android.view.ViewGroup
|
||||
import com.tangem.tangemtest.R
|
||||
import com.tangem.tangemtest._arch.structure.abstraction.ItemViewModel
|
||||
import com.tangem.tangemtest.ucase.variants.personalize.ui.widgets.abstraction.BaseBlockWidget
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
class StubWidget(parent: ViewGroup) : BaseBlockWidget(parent) {
|
||||
override fun getLayoutId(): Int = R.layout.w_empty
|
||||
override var viewModel: List<ItemViewModel<*>> = listOf()
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
package com.tangem.tangemtest.ucase.variants.personalize.ui.widgets.impl.block
|
||||
|
||||
import android.view.ViewGroup
|
||||
import com.tangem.tangemtest.R
|
||||
import com.tangem.tangemtest._arch.structure.abstraction.BaseItem
|
||||
import com.tangem.tangemtest._arch.structure.abstraction.ItemViewModel
|
||||
import com.tangem.tangemtest._arch.structure.abstraction.ListItemBlock
|
||||
import com.tangem.tangemtest.ucase.variants.personalize.ui.widgets.abstraction.BaseBlockWidget
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
class LinearBlockWidget(
|
||||
parent: ViewGroup,
|
||||
private val children: ListItemBlock
|
||||
) : BaseBlockWidget(parent) {
|
||||
override fun getLayoutId(): Int = R.layout.w_personilize_block
|
||||
|
||||
override var viewModel: List<ItemViewModel<*>> = listOf()
|
||||
get() = children.itemList.map { it as BaseItem<*> }.map { it.viewModel }
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
package com.tangem.tangemtest.ucase.variants.personalize.ui.widgets.impl.item
|
||||
|
||||
import android.view.ViewGroup
|
||||
import android.widget.TextView
|
||||
import androidx.transition.AutoTransition
|
||||
import androidx.transition.TransitionManager
|
||||
import com.tangem.tangemtest.R
|
||||
import com.tangem.tangemtest._arch.structure.abstraction.BaseItem
|
||||
import com.tangem.tangemtest.ucase.variants.personalize.ui.widgets.abstraction.BaseWidget
|
||||
import com.tangem.tangemtest.ucase.variants.personalize.ui.widgets.abstraction.getResDescription
|
||||
import ru.dev.gbixahue.eu4d.lib.android.global.log.Log
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
abstract class DescriptionWidget<D>(
|
||||
parent: ViewGroup,
|
||||
data: BaseItem<D>
|
||||
) : BaseWidget<D>(parent, data) {
|
||||
|
||||
private val descriptionContainer: ViewGroup by lazy { view.findViewById<ViewGroup>(R.id.container_description) }
|
||||
private val tvDescription: TextView? by lazy { descriptionContainer.findViewById<TextView>(R.id.tv_description) }
|
||||
|
||||
init {
|
||||
Log.d(this, "init id: ${dataItem.id}")
|
||||
initDescriptionWidget()
|
||||
}
|
||||
|
||||
private fun initDescriptionWidget() {
|
||||
dataItem.viewModel.viewState.onDescriptionVisibilityChanged = { changeDescriptionVisibility(it) }
|
||||
}
|
||||
|
||||
protected fun changeDescriptionVisibility(state: Int) {
|
||||
val tv = tvDescription ?: return
|
||||
val descriptionId = getResDescription() ?: return
|
||||
val description = tv.context.getString(descriptionId)
|
||||
if (description.isEmpty()) return
|
||||
|
||||
tv.text = description
|
||||
TransitionManager.beginDelayedTransition(view.parent as ViewGroup, AutoTransition())
|
||||
descriptionContainer.visibility = state
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
package com.tangem.tangemtest.ucase.variants.personalize.ui.widgets.impl.item
|
||||
|
||||
import android.text.Editable
|
||||
import android.text.TextWatcher
|
||||
import android.view.ViewGroup
|
||||
import com.google.android.material.textfield.TextInputEditText
|
||||
import com.google.android.material.textfield.TextInputLayout
|
||||
import com.tangem.tangemtest.R
|
||||
import com.tangem.tangemtest._arch.structure.impl.EditTextItem
|
||||
import com.tangem.tangemtest.ucase.variants.personalize.ui.widgets.abstraction.getResNameId
|
||||
import ru.dev.gbixahue.eu4d.lib.android._android.views.moveCursorToEnd
|
||||
import ru.dev.gbixahue.eu4d.lib.kotlin.stringOf
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
class EditTextWidget(parent: ViewGroup, data: EditTextItem) : DescriptionWidget<String>(parent, data) {
|
||||
override fun getLayoutId(): Int = R.layout.w_personalize_item_edit_text
|
||||
|
||||
private val tilItem = view.findViewById<TextInputLayout>(R.id.til_item)
|
||||
private val etItem = view.findViewById<TextInputEditText>(R.id.et_item)
|
||||
|
||||
private val watcher = object : TextWatcher {
|
||||
override fun afterTextChanged(s: Editable?) {
|
||||
dataItem.viewModel.updateDataByView(stringOf(s))
|
||||
}
|
||||
|
||||
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
|
||||
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {}
|
||||
}
|
||||
|
||||
init {
|
||||
tilItem.hint = tilItem.context.getString(getResNameId())
|
||||
etItem.setText(dataItem.viewModel.data)
|
||||
etItem.addTextChangedListener(watcher)
|
||||
dataItem.viewModel.onDataUpdated = { silentUpdate(it) }
|
||||
}
|
||||
|
||||
private fun silentUpdate(value: String?) {
|
||||
etItem.removeTextChangedListener(watcher)
|
||||
etItem.setText(value)
|
||||
etItem.moveCursorToEnd()
|
||||
etItem.addTextChangedListener(watcher)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
package com.tangem.tangemtest.ucase.variants.personalize.ui.widgets.impl.item
|
||||
|
||||
import android.text.Editable
|
||||
import android.text.InputFilter
|
||||
import android.text.TextWatcher
|
||||
import android.view.ViewGroup
|
||||
import com.google.android.material.textfield.TextInputEditText
|
||||
import com.google.android.material.textfield.TextInputLayout
|
||||
import com.tangem.tangemtest.R
|
||||
import com.tangem.tangemtest._arch.structure.impl.NumberItem
|
||||
import com.tangem.tangemtest.ucase.variants.personalize.CardNumber
|
||||
import com.tangem.tangemtest.ucase.variants.personalize.ui.widgets.abstraction.getResNameId
|
||||
import ru.dev.gbixahue.eu4d.lib.android._android.views.addInputFilter
|
||||
import ru.dev.gbixahue.eu4d.lib.android._android.views.moveCursorToEnd
|
||||
import ru.dev.gbixahue.eu4d.lib.kotlin.stringOf
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
class NumberWidget(parent: ViewGroup, data: NumberItem) : DescriptionWidget<Number>(parent, data) {
|
||||
override fun getLayoutId(): Int = R.layout.w_personalize_item_number
|
||||
|
||||
private val tilItem = view.findViewById<TextInputLayout>(R.id.til_item)
|
||||
private val etItem = view.findViewById<TextInputEditText>(R.id.et_item)
|
||||
|
||||
private val watcher = object : TextWatcher {
|
||||
override fun afterTextChanged(s: Editable?) {
|
||||
dataItem.viewModel.updateDataByView(getValue(stringOf(s)))
|
||||
}
|
||||
|
||||
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
|
||||
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {}
|
||||
}
|
||||
|
||||
init {
|
||||
tilItem.hint = tilItem.context.getString(getResNameId())
|
||||
|
||||
//TODO: remove from Widget
|
||||
if (dataItem.id == CardNumber.Number) etItem.addInputFilter(InputFilter.LengthFilter(13))
|
||||
|
||||
etItem.setText(stringOf(dataItem.viewModel.data))
|
||||
etItem.addTextChangedListener(watcher)
|
||||
dataItem.viewModel.onDataUpdated = { silentUpdate(it) }
|
||||
}
|
||||
|
||||
private fun silentUpdate(value: Number?) {
|
||||
etItem.removeTextChangedListener(watcher)
|
||||
etItem.setText(stringOf(value))
|
||||
etItem.moveCursorToEnd()
|
||||
etItem.addTextChangedListener(watcher)
|
||||
}
|
||||
|
||||
private fun getValue(value: String): Long {
|
||||
return if (value.isEmpty()) 0L else value.toLong()
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,92 @@
|
|||
package com.tangem.tangemtest.ucase.variants.personalize.ui.widgets.impl.item
|
||||
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.AdapterView
|
||||
import android.widget.BaseAdapter
|
||||
import android.widget.Spinner
|
||||
import android.widget.TextView
|
||||
import com.tangem.tangemtest.R
|
||||
import com.tangem.tangemtest._arch.structure.impl.KeyValue
|
||||
import com.tangem.tangemtest._arch.structure.impl.ListItem
|
||||
import com.tangem.tangemtest._arch.structure.impl.ListValueWrapper
|
||||
import com.tangem.tangemtest.ucase.variants.personalize.ui.widgets.abstraction.getResNameId
|
||||
import ru.dev.gbixahue.eu4d.lib.android._android.views.inflate
|
||||
import ru.dev.gbixahue.eu4d.lib.kotlin.stringOf
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
class SpinnerWidget(parent: ViewGroup, listItem: ListItem) : DescriptionWidget<ListValueWrapper>(parent, listItem) {
|
||||
override fun getLayoutId(): Int = R.layout.w_personalize_item_spinner
|
||||
|
||||
private val data: ListValueWrapper = listItem.getData()!!
|
||||
|
||||
private val spItem = view.findViewById<Spinner>(R.id.sp_item)
|
||||
private val spAdapter = SpItemAdapter(data.itemList)
|
||||
|
||||
private val onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
|
||||
override fun onNothingSelected(parent: AdapterView<*>?) {}
|
||||
|
||||
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
|
||||
data.selectedItem = data.itemList[position]
|
||||
dataItem.viewModel.updateDataByView(data)
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
val name = view.findViewById<TextView>(R.id.tv_name)
|
||||
name.setText(getResNameId())
|
||||
spItem.adapter = spAdapter
|
||||
spAdapter.getItemPosition(stringOf(data.selectedItem))?.let {
|
||||
spItem.setSelection(it)
|
||||
}
|
||||
spItem.onItemSelectedListener = onItemSelectedListener
|
||||
dataItem.viewModel.onDataUpdated = {
|
||||
it?.apply {
|
||||
spItem.onItemSelectedListener = null
|
||||
this.itemList.firstOrNull { item -> item.value == selectedItem }?.let {
|
||||
val position = itemList.indexOf(it)
|
||||
spItem.setSelection(position)
|
||||
}
|
||||
spItem.onItemSelectedListener = onItemSelectedListener
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class SpItemAdapter(list: List<KeyValue>?) : BaseAdapter() {
|
||||
|
||||
private val itemList: List<KeyValue> = list ?: listOf()
|
||||
|
||||
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
|
||||
val view = convertView ?: parent.inflate(R.layout.vh_sp_item)
|
||||
|
||||
val tv = view.findViewById<TextView>(R.id.tv_sp_item)
|
||||
tv.text = extractData(itemList[position])
|
||||
|
||||
return view
|
||||
}
|
||||
|
||||
override fun getDropDownView(position: Int, convertView: View?, parent: ViewGroup): View {
|
||||
val view = convertView ?: parent.inflate(R.layout.vh_sp_dropdown_item)
|
||||
|
||||
val tv = view.findViewById<TextView>(R.id.tv_sp_item)
|
||||
tv.text = extractData(itemList[position])
|
||||
|
||||
return view
|
||||
}
|
||||
|
||||
private fun extractData(item: KeyValue): String? = item.key
|
||||
|
||||
override fun getItem(position: Int): String = stringOf(itemList[position])
|
||||
|
||||
override fun getItemId(position: Int): Long = position.toLong()
|
||||
|
||||
override fun getCount(): Int = itemList.size
|
||||
|
||||
fun getItemPosition(item: String): Int? {
|
||||
val kv = itemList.firstOrNull { it.value == item } ?: return null
|
||||
return itemList.indexOf(kv)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
package com.tangem.tangemtest.ucase.variants.personalize.ui.widgets.impl.item
|
||||
|
||||
import android.view.ViewGroup
|
||||
import android.widget.CompoundButton
|
||||
import androidx.appcompat.widget.SwitchCompat
|
||||
import com.tangem.tangemtest.R
|
||||
import com.tangem.tangemtest._arch.structure.impl.BoolItem
|
||||
import com.tangem.tangemtest.ucase.variants.personalize.ui.widgets.abstraction.getResNameId
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
class SwitchWidget(parent: ViewGroup, data: BoolItem) : DescriptionWidget<Boolean>(parent, data) {
|
||||
override fun getLayoutId(): Int = R.layout.w_personalize_item_switch
|
||||
|
||||
private val switchItem = view.findViewById<SwitchCompat>(R.id.sw_item)
|
||||
|
||||
private val changeListener = CompoundButton.OnCheckedChangeListener { buttonView, isChecked ->
|
||||
dataItem.viewModel.updateDataByView(isChecked)
|
||||
}
|
||||
|
||||
init {
|
||||
switchItem.setText(getResNameId())
|
||||
switchItem.isChecked = dataItem.viewModel.data ?: false
|
||||
switchItem.setOnCheckedChangeListener(changeListener)
|
||||
dataItem.viewModel.onDataUpdated = {
|
||||
switchItem.setOnCheckedChangeListener(null)
|
||||
switchItem.isChecked = it ?: false
|
||||
switchItem.setOnCheckedChangeListener(changeListener)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
package com.tangem.tangemtest.ucase.variants.personalize.ui.widgets.impl.item
|
||||
|
||||
import android.view.ViewGroup
|
||||
import android.widget.TextView
|
||||
import com.tangem.tangemtest.R
|
||||
import com.tangem.tangemtest._arch.structure.impl.TextItem
|
||||
import com.tangem.tangemtest.ucase.variants.personalize.ui.widgets.abstraction.getResNameId
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
class TextWidget(parent: ViewGroup, data: TextItem) : DescriptionWidget<String>(parent, data) {
|
||||
override fun getLayoutId(): Int = R.layout.w_personalize_item_text
|
||||
|
||||
private val tvName = view.findViewById<TextView>(R.id.tv_name)
|
||||
|
||||
init {
|
||||
tvName?.setText(getResNameId())
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
package com.tangem.tangemtest.ucase.variants.scan.ui
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.widget.TextView
|
||||
import androidx.lifecycle.Observer
|
||||
import com.tangem.tangemtest.R
|
||||
import com.tangem.tangemtest.ucase.resources.ActionType
|
||||
import com.tangem.tangemtest.ucase.ui.BaseCardActionFragment
|
||||
import ru.dev.gbixahue.eu4d.lib.android._android.views.show
|
||||
import ru.dev.gbixahue.eu4d.lib.android.global.log.Log
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
class ScanActionFragment : BaseCardActionFragment() {
|
||||
|
||||
override fun getLayoutId(): Int = R.layout.fg_action_card_scan
|
||||
|
||||
override fun getAction(): ActionType = ActionType.Scan
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
showActionFab(true)
|
||||
}
|
||||
|
||||
override fun listenResponse() {
|
||||
val tvStub = mainView.findViewById<TextView>(R.id.tv_screen_stub)
|
||||
val tvResponse by lazy { mainView.findViewById<TextView>(R.id.tv_action_response_json) }
|
||||
paramsVM.ldReadResponse.observe(viewLifecycleOwner, Observer {
|
||||
Log.d(this, "action response: ${if (it.length > 50) it.substring(0..50) else it}")
|
||||
tvStub.show(false)
|
||||
tvResponse.text = it
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
package com.tangem.tangemtest.ucase.variants.sign.ui
|
||||
|
||||
import com.tangem.tangemtest.R
|
||||
import com.tangem.tangemtest.ucase.resources.ActionType
|
||||
import com.tangem.tangemtest.ucase.ui.BaseCardActionFragment
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
class SignActionFragment : BaseCardActionFragment() {
|
||||
|
||||
override fun getLayoutId(): Int = R.layout.fg_action_card_sign
|
||||
|
||||
override fun getAction(): ActionType = ActionType.Sign
|
||||
}
|
||||
22
tangem-demo/src/main/res/anim/fade_in.xml
Normal file
22
tangem-demo/src/main/res/anim/fade_in.xml
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ Copyright (C) 2018 The Android Open Source Project
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<alpha
|
||||
android:duration="@android:integer/config_mediumAnimTime"
|
||||
android:fromAlpha="0.0"
|
||||
android:toAlpha="1.0" />
|
||||
</set>
|
||||
22
tangem-demo/src/main/res/anim/fade_out.xml
Normal file
22
tangem-demo/src/main/res/anim/fade_out.xml
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ Copyright (C) 2018 The Android Open Source Project
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<alpha
|
||||
android:duration="@android:integer/config_mediumAnimTime"
|
||||
android:fromAlpha="1.0"
|
||||
android:toAlpha="0.0" />
|
||||
</set>
|
||||
10
tangem-demo/src/main/res/anim/slide_in_left.xml
Normal file
10
tangem-demo/src/main/res/anim/slide_in_left.xml
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<translate
|
||||
android:duration="@android:integer/config_mediumAnimTime"
|
||||
android:fromXDelta="-100%"
|
||||
android:fromYDelta="0%"
|
||||
android:toXDelta="0%"
|
||||
android:toYDelta="0%" />
|
||||
</set>
|
||||
10
tangem-demo/src/main/res/anim/slide_in_right.xml
Normal file
10
tangem-demo/src/main/res/anim/slide_in_right.xml
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<translate
|
||||
android:duration="@android:integer/config_mediumAnimTime"
|
||||
android:fromXDelta="100%"
|
||||
android:fromYDelta="0%"
|
||||
android:toXDelta="0%"
|
||||
android:toYDelta="0%" />
|
||||
</set>
|
||||
10
tangem-demo/src/main/res/anim/slide_out_left.xml
Normal file
10
tangem-demo/src/main/res/anim/slide_out_left.xml
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<translate
|
||||
android:duration="@android:integer/config_mediumAnimTime"
|
||||
android:fromXDelta="0%"
|
||||
android:fromYDelta="0%"
|
||||
android:toXDelta="-100%"
|
||||
android:toYDelta="0%" />
|
||||
</set>
|
||||
10
tangem-demo/src/main/res/anim/slide_out_right.xml
Normal file
10
tangem-demo/src/main/res/anim/slide_out_right.xml
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<translate
|
||||
android:duration="@android:integer/config_mediumAnimTime"
|
||||
android:fromXDelta="0%"
|
||||
android:fromYDelta="0%"
|
||||
android:toXDelta="100%"
|
||||
android:toYDelta="0%" />
|
||||
</set>
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:color="@color/switchThumbActive" android:state_checked="true" />
|
||||
<item android:color="@color/switchThumb" />
|
||||
</selector>
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:color="@color/switchTrackActive" android:state_checked="true" />
|
||||
<item android:color="@color/switchTrack" />
|
||||
</selector>
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:color="?attr/colorPrimary" android:state_enabled="true" />
|
||||
<item android:alpha="0.12" android:color="?attr/colorOnSurface" />
|
||||
</selector>
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:color="@android:color/primary_text_dark" android:state_enabled="true" />
|
||||
<item android:alpha="0.38" android:color="?attr/colorOnSurface" />
|
||||
</selector>
|
||||
22
tangem-demo/src/main/res/layout/a_main.xml
Normal file
22
tangem-demo/src/main/res/layout/a_main.xml
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@color/colorPrimary"
|
||||
android:theme="@style/ThemeOverlay.MaterialComponents.Dark.ActionBar" />
|
||||
|
||||
<fragment
|
||||
android:id="@+id/nav_host_fragment"
|
||||
android:name="androidx.navigation.fragment.NavHostFragment"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:defaultNavHost="true"
|
||||
app:navGraph="@navigation/navigation" />
|
||||
</LinearLayout>
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue