Updated on 2026-08-14

This commit is contained in:
Tangem 2018-05-29 13:28:36 +03:00
parent 0374157cad
commit 8c17a8d4f0
48 changed files with 3490 additions and 3417 deletions

View file

@ -0,0 +1,126 @@
package com.tangem.presentation.activity;
import android.os.Bundle;
import android.support.design.widget.TabLayout;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v7.app.AppCompatActivity;
import android.view.Menu;
import android.view.MenuItem;
import com.tangem.wallet.R;
import com.tangem.wallet.Tangem_Card;
import com.tangem.wallet.WalletInfoFragment;
public class CardInfoActivity extends AppCompatActivity implements WalletInfoFragment.OnFragmentInteractionListener {
/**
* The {@link android.support.v4.view.PagerAdapter} that will provide
* fragments for each of the sections. We use a
* {@link FragmentPagerAdapter} derivative, which will keep every
* loaded fragment in memory. If this becomes too memory intensive, it
* may be best to switch to a
* {@link android.support.v4.app.FragmentStatePagerAdapter}.
*/
private SectionsPagerAdapter mSectionsPagerAdapter;
/**
* The {@link ViewPager} that will host the section contents.
*/
private ViewPager mViewPager;
private Tangem_Card mCard;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_card_info);
// Create the adapter that will return a fragment for each of the three
// primary sections of the activity.
mSectionsPagerAdapter = new SectionsPagerAdapter(getSupportFragmentManager());
// Set up the ViewPager with the sections adapter.
mViewPager = (ViewPager) findViewById(R.id.container);
mViewPager.setAdapter(mSectionsPagerAdapter);
TabLayout tabLayout = (TabLayout) findViewById(R.id.tabs);
tabLayout.setupWithViewPager(mViewPager);
String UID = getIntent().getStringExtra("UID");
mCard = new Tangem_Card(UID);
mCard.LoadFromBundle(getIntent().getBundleExtra("Card"));
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_card_info, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
/**
* A {@link FragmentPagerAdapter} that returns a fragment corresponding to
* one of the sections/tabs/pages.
*/
public class SectionsPagerAdapter extends FragmentPagerAdapter {
public SectionsPagerAdapter(FragmentManager fm) {
super(fm);
}
@Override
public Fragment getItem(int position) {
// getItem is called to instantiate the fragment for the given page.
// Return a PlaceholderFragment (defined as a static inner class below).
if (position == 0) {
return WalletInfoFragment.newInstance(mCard);
} /*else if (position == 1) {
return WalletUnspentFragment.newInstance(mCard);
} else if (position == 2) {
return WalletHistoryFragment.newInstance(mCard);
}*/
return null;
}
@Override
public int getCount() {
// Show 3 total pages.
return 3;
}
@Override
public CharSequence getPageTitle(int position) {
switch (position) {
case 0:
return "Wallet info";
case 1:
return "Unspent";
case 2:
return "History";
}
return null;
}
}
}

View file

@ -0,0 +1,723 @@
package com.tangem.presentation.activity;
import android.app.Activity;
import android.content.Intent;
import android.nfc.NfcAdapter;
import android.nfc.Tag;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.text.Editable;
import android.text.Html;
import android.text.Spanned;
import android.text.TextWatcher;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.RadioGroup;
import android.widget.TextView;
import android.widget.Toast;
import com.tangem.cardReader.NfcManager;
import com.tangem.cardReader.Util;
import com.tangem.wallet.BTCUtils;
import com.tangem.wallet.Blockchain;
import com.tangem.wallet.CoinEngine;
import com.tangem.wallet.CoinEngineFactory;
import com.tangem.wallet.DerEncodingUtil;
import com.tangem.wallet.Electrum_Request;
import com.tangem.wallet.Electrum_Task;
import com.tangem.wallet.Fee_Request;
import com.tangem.wallet.Fee_Task;
import com.tangem.wallet.FormatUtil;
import com.tangem.wallet.Infura_Request;
import com.tangem.wallet.Infura_Task;
import com.tangem.wallet.R;
import com.tangem.wallet.SharedData;
import com.tangem.wallet.Tangem_Card;
import com.tangem.wallet.Transaction;
import com.tangem.wallet.UnspentOutputInfo;
import org.json.JSONException;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
public class ConfirmPaymentActivity extends AppCompatActivity implements NfcAdapter.ReaderCallback {
private static final int REQUEST_CODE_SIGN_PAYMENT = 1;
private static final int REQUEST_CODE_REQUEST_PIN2 = 2;
Button btnSend;
boolean feeRequestSuccess = false;
boolean balanceRequestSuccess = false;
EditText etWallet;
TextView tvCardID, tvBalance, tvCurrency, tvCurrency2, tvBalanceEquivalent, tvAmountEquivalent, tvFeeEquivalent;
EditText etAmount;
EditText etFee;
ImageView ivCamera;
Tangem_Card mCard;
RadioGroup rgFee;
String minFee = null, maxFee = null, normalFee = null;
Long minFeeInInternalUnits = 0L;
private NfcManager mNfcManager;
int requestPIN2Count = 0;
ProgressBar progressBar;
boolean nodeCheck = false;
Date dtVerifyed = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_confirm_payment);
MainActivity.commonInit(getApplicationContext());
mNfcManager = new NfcManager(this, this);
mCard = new Tangem_Card(getIntent().getStringExtra("UID"));
mCard.LoadFromBundle(getIntent().getExtras().getBundle("Card"));
progressBar = findViewById(R.id.progressBar);
btnSend = findViewById(R.id.btnSend);
etWallet = findViewById(R.id.etWallet);
tvCardID = findViewById(R.id.tvCardID);
tvBalance = findViewById(R.id.tvBalance);
tvCurrency = findViewById(R.id.tvCurrency);
tvCurrency2 = findViewById(R.id.tvCurrency2);
etAmount = findViewById(R.id.etAmount);
etFee = findViewById(R.id.etFee);
tvBalanceEquivalent = findViewById(R.id.tvBalanceEquivalent);
tvAmountEquivalent = findViewById(R.id.tvAmountEquivalent);
tvFeeEquivalent = findViewById(R.id.tvFeeEquivalent);
ivCamera = findViewById(R.id.ivCamera);
rgFee = findViewById(R.id.rgFee);
rgFee.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(RadioGroup group, int checkedId) {
doSetFee(checkedId);
}
});
etAmount.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
try {
CoinEngine engine = CoinEngineFactory.Create(mCard.getBlockchain());
tvAmountEquivalent.setText(engine.GetAmountEqualentDescriptor(mCard, etAmount.getText().toString()));
if (!mCard.getAmountEquivalentDescriptionAvailable()) {
tvAmountEquivalent.setError("Service unavailable");
} else {
tvAmountEquivalent.setError(null);
}
} catch (Exception e) {
e.printStackTrace();
tvAmountEquivalent.setText("");
}
}
@Override
public void afterTextChanged(Editable s) {
}
});
etFee.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
try {
CoinEngine engine = CoinEngineFactory.Create(mCard.getBlockchain());
String eqFee = engine.EvaluteFeeEquivalent(mCard, etFee.getText().toString());
tvFeeEquivalent.setText(eqFee);
if (!mCard.getAmountEquivalentDescriptionAvailable()) {
tvFeeEquivalent.setError("Service unavailable");
} else {
tvFeeEquivalent.setError(null);
}
} catch (Exception e) {
e.printStackTrace();
tvFeeEquivalent.setText("");
}
}
@Override
public void afterTextChanged(Editable s) {
}
});
CoinEngine engine = CoinEngineFactory.Create(mCard.getBlockchain());
//tvBalance.setText(engine.GetBalanceWithAlter(mCard));
if (mCard.getBlockchain() == Blockchain.Token) {
Spanned html = Html.fromHtml(engine.GetBalanceWithAlter(mCard));
tvBalance.setText(html);
} else {
tvBalance.setText(engine.GetBalanceWithAlter(mCard));
}
etAmount.setText(getIntent().getStringExtra("Amount"));
tvCurrency.setText(engine.GetBalanceCurrency(mCard));
tvCurrency2.setText(engine.GetFeeCurrency());
tvCardID.setText(mCard.getCIDDescription());
//tvBalanceEquivalent.setText(mCard.getBalanceEquivalentDescription());
tvBalanceEquivalent.setText(engine.GetBalanceEquivalent(mCard));
if (!mCard.getAmountEquivalentDescriptionAvailable()) {
tvBalanceEquivalent.setError("Service unavailable");
} else {
tvBalanceEquivalent.setError(null);
}
etWallet.setText(getIntent().getStringExtra("Wallet"));
etFee.setText("?");
btnSend.setVisibility(View.INVISIBLE);
feeRequestSuccess = false;
balanceRequestSuccess = false;
btnSend.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.MINUTE, -1);
if (dtVerifyed == null || dtVerifyed.before(calendar.getTime())) {
FinishActivityWithError(Activity.RESULT_CANCELED, "The obtained data is outdated! Try again");
return;
}
CoinEngine engineCoin = CoinEngineFactory.Create(mCard.getBlockchain());
if (engineCoin.IsNeedCheckNode() && !nodeCheck) {
Toast.makeText(getBaseContext(), "Cannot reach current active blockchain node. Try again", Toast.LENGTH_LONG).show();
return;
}
String txFee = etFee.getText().toString();
String txAmount = etAmount.getText().toString();
if (!engineCoin.HasBalanceInfo(mCard)) {
FinishActivityWithError(Activity.RESULT_CANCELED, "Cannot check balance! No connection with blockchain nodes");
return;
} else if (!engineCoin.IsBalanceNotZero(mCard)) {
FinishActivityWithError(Activity.RESULT_CANCELED, "The wallet is empty");
return;
} else if (!engineCoin.CheckUnspentTransaction(mCard)) {
//else if (mCard.getUnspentTransactions().size() == 0 && mCard.getBlockchain() != Blockchain.Ethereum) {
FinishActivityWithError(Activity.RESULT_CANCELED, "Please wait for confirmation of incoming transaction");
return;
}
if (!engineCoin.CheckAmountValie(mCard, txAmount, txFee, minFeeInInternalUnits)) {
FinishActivityWithError(Activity.RESULT_CANCELED, "Fee exceeds payment amount. Enter correct value and repeat sending.");
return;
}
requestPIN2Count = 0;
Intent intent = new Intent(getBaseContext(), RequestPINActivity.class);
intent.putExtra("mode", RequestPINActivity.Mode.RequestPIN2.toString());
intent.putExtra("UID", mCard.getUID());
intent.putExtra("Card", mCard.getAsBundle());
startActivityForResult(intent, REQUEST_CODE_REQUEST_PIN2);
}
});
if (mCard.getBlockchain() == Blockchain.Ethereum || mCard.getBlockchain() == Blockchain.EthereumTestNet || mCard.getBlockchain() == Blockchain.Token) {
ETHRequestTask task = new ETHRequestTask(mCard.getBlockchain());
Infura_Request req = Infura_Request.GetGasPrise(mCard.getWallet());
req.setID(67);
req.setBlockchain(mCard.getBlockchain());
rgFee.setEnabled(false);
task.execute(req);
} else {
rgFee.setEnabled(true);
SharedData data = new SharedData(SharedData.COUNT_REQUEST);
CoinEngine engineCoin = CoinEngineFactory.Create(mCard.getBlockchain());
for (int i = 0; i < data.allRequest; ++i) {
String nodeAddress = engineCoin.GetNextNode(mCard);
int nodePort = engineCoin.GetNextNodePort(mCard);
//ConnectTask connectTaskEx = new ConnectTask(Blockchain.getNextServiceHost(mCard), Blockchain.getNextServicePort(mCard), data);
ConnectTask connectTaskEx = new ConnectTask(nodeAddress, nodePort, data);
//connectTaskEx.execute(Electrum_Request.CheckBalance(mCard.getWallet()));
connectTaskEx.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, Electrum_Request.CheckBalance(mCard.getWallet()));
}
String nodeAddress = engineCoin.GetNode(mCard);
int nodePort = engineCoin.GetNodePort(mCard);
ConnectTask connectTask = new ConnectTask(nodeAddress, nodePort, data);
//ConnectTask connectTask = new ConnectTask(Blockchain.getServiceHost(mCard), Blockchain.getServicePort(mCard));
connectTask.execute(/*Electrum_Request.CheckBalance(mCard.getWallet()), */Electrum_Request.GetFee(mCard.getWallet()));
int calcSize = 256;
try {
calcSize = BuildSize(etWallet.getText().toString(), "0.00", etAmount.getText().toString());
} catch (Exception ex) {
Log.e("Build Fee error", ex.getMessage());
}
SharedData sharedFee = new SharedData(SharedData.COUNT_REQUEST);
progressBar.setVisibility(View.VISIBLE);
for (int i = 0; i < SharedData.COUNT_REQUEST; ++i) {
ConnectFeeTask feeTask = new ConnectFeeTask(sharedFee);
feeTask.execute(Fee_Request.GetFee(mCard.getWallet(), calcSize, Fee_Request.NORMAL),
Fee_Request.GetFee(mCard.getWallet(), calcSize, Fee_Request.MINIMAL),
Fee_Request.GetFee(mCard.getWallet(), calcSize, Fee_Request.PRIORITY));
}
}
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
switch (keyCode) {
case KeyEvent.KEYCODE_BACK:
Intent intent = new Intent();
intent.putExtra("message", "Operation canceled");
setResult(Activity.RESULT_CANCELED, intent);
finish();
return true;
}
return super.onKeyDown(keyCode, event);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_CODE_SIGN_PAYMENT) {
if (data != null && data.getExtras().containsKey("UID") && data.getExtras().containsKey("Card")) {
Tangem_Card updatedCard = new Tangem_Card(data.getStringExtra("UID"));
updatedCard.LoadFromBundle(data.getBundleExtra("Card"));
mCard = updatedCard;
}
if (resultCode == SignPaymentActivity.RESULT_INVALID_PIN && requestPIN2Count < 2) {
requestPIN2Count++;
Intent intent = new Intent(getBaseContext(), RequestPINActivity.class);
intent.putExtra("mode", RequestPINActivity.Mode.RequestPIN2.toString());
intent.putExtra("UID", mCard.getUID());
intent.putExtra("Card", mCard.getAsBundle());
startActivityForResult(intent, REQUEST_CODE_REQUEST_PIN2);
return;
}
setResult(resultCode, data);
finish();
} else if (requestCode == REQUEST_CODE_REQUEST_PIN2) {
if (resultCode == Activity.RESULT_OK) {
Intent intent = new Intent(getBaseContext(), SignPaymentActivity.class);
intent.putExtra("UID", mCard.getUID());
intent.putExtra("Card", mCard.getAsBundle());
intent.putExtra("Wallet", etWallet.getText().toString());
intent.putExtra("Amount", etAmount.getText().toString());
intent.putExtra("Fee", etFee.getText().toString());
startActivityForResult(intent, REQUEST_CODE_SIGN_PAYMENT);
} else {
Toast.makeText(getBaseContext(), "PIN2 is required to sign the payment", Toast.LENGTH_LONG).show();
}
}
}
void FinishActivityWithError(int errorCode, String message) {
//Snackbar.make(etFee, message, Snackbar.LENGTH_LONG).show();
Intent intent = new Intent();
intent.putExtra("message", message);
setResult(errorCode, intent);
finish();
}
@Override
public void onTagDiscovered(Tag tag) {
try {
Log.w(getClass().getName(), "Ignore discovered tag!");
mNfcManager.IgnoreTag(tag);
} catch (IOException e) {
e.printStackTrace();
}
}
int BuildSize(String outputAddress, String outFee, String outAmount) throws Exception {
String myAddress = mCard.getWallet();
String changeAddress = myAddress; //"n2eMqTT929pb1RDNuqEnxdaLau1rxy3efi";
byte[] pbKey = mCard.getWalletPublicKey();
byte[] pbComprKey = mCard.getWalletPublicKeyRar();
// Build script for our address
List<Tangem_Card.UnspentTransaction> rawTxList = mCard.getUnspentTransactions();
byte[] outputScriptWeAreAbleToSpend = Transaction.Script.buildOutput(myAddress).bytes;
// Collect unspent
ArrayList<UnspentOutputInfo> unspentOutputs = BTCUtils.getOutputs(rawTxList, outputScriptWeAreAbleToSpend);
long fullAmount = 0;
for (int i = 0; i < unspentOutputs.size(); ++i) {
fullAmount += unspentOutputs.get(i).value;
}
// Get first unspent
UnspentOutputInfo outPut = unspentOutputs.get(0);
int outPutIndex = outPut.outputIndex;
// get prev TX id;
String prevTXID = rawTxList.get(0).txID;//"f67b838d6e2c0c587f476f583843e93ff20368eaf96a798bdc25e01f53f8f5d2";
long fees = FormatUtil.ConvertStringToLong(outFee);
long amount = FormatUtil.ConvertStringToLong(outAmount);
amount = amount - fees;
long change = fullAmount - fees - amount;
if (amount + fees > fullAmount) {
throw new Exception(String.format("Balance (%d) < amount (%d) + (%d)", fullAmount, change, amount));
}
byte[][] hashesForSign = new byte[unspentOutputs.size()][];
for (int i = 0; i < unspentOutputs.size(); ++i) {
byte[] newTX = BTCUtils.buildTXForSign(myAddress, outputAddress, changeAddress, unspentOutputs, i, amount, change);
byte[] hashData = Util.calculateSHA256(newTX);
byte[] doubleHashData = Util.calculateSHA256(hashData);
Log.e("TX_BODY_1", BTCUtils.toHex(newTX));
Log.e("TX_HASH_1", BTCUtils.toHex(hashData));
Log.e("TX_HASH_2", BTCUtils.toHex(doubleHashData));
unspentOutputs.get(i).bodyDoubleHash = doubleHashData;
unspentOutputs.get(i).bodyHash = hashData;
hashesForSign[i] = doubleHashData;
}
byte[] signFromCard = new byte[64 * unspentOutputs.size()];
for (int i = 0; i < unspentOutputs.size(); ++i) {
BigInteger r = new BigInteger(1, Arrays.copyOfRange(signFromCard, 0 + i * 64, 32 + i * 64));
BigInteger s = new BigInteger(1, Arrays.copyOfRange(signFromCard, 32 + i * 64, 64 + i * 64));
byte[] encodingSign = DerEncodingUtil.packSignDer(r, s, pbKey);
unspentOutputs.get(i).scriptForBuild = encodingSign;
}
byte[] realTX = BTCUtils.buildTXForSend(outputAddress, changeAddress, unspentOutputs, amount, change);
return realTX.length;
}
private class ConnectTask extends Electrum_Task {
public ConnectTask(String host, int port) {
super(host, port);
}
public ConnectTask(String host, int port, SharedData sharedData) {
super(host, port, sharedData);
}
@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
}
@Override
protected void onPostExecute(List<Electrum_Request> requests) {
super.onPostExecute(requests);
for (Electrum_Request request : requests) {
try {
if (request.error == null) {
if (request.isMethod(Electrum_Request.METHOD_GetBalance)) {
try {
etFee.setText("--");
//String mWalletAddress = request.getParams().getString(0);
if ((request.getResult().getInt("confirmed") + request.getResult().getInt("unconfirmed")) / mCard.getBlockchain().getMultiplier() * 1000000.0 < Float.parseFloat(etAmount.getText().toString())) {
etFee.setError("Not enough funds");
balanceRequestSuccess = false;
btnSend.setVisibility(View.INVISIBLE);
dtVerifyed = null;
nodeCheck = false;
} else {
etFee.setError(null);
balanceRequestSuccess = true;
if (feeRequestSuccess && balanceRequestSuccess) {
btnSend.setVisibility(View.VISIBLE);
}
dtVerifyed = new Date();
nodeCheck = true;
}
} catch (JSONException e) {
if (sharedCounter != null) {
int errCounter = sharedCounter.errorRequest.incrementAndGet();
if (errCounter >= sharedCounter.allRequest) {
e.printStackTrace();
FinishActivityWithError(Activity.RESULT_CANCELED, "Cannot check balance! No connection with blockchain nodes");
}
} else {
e.printStackTrace();
FinishActivityWithError(Activity.RESULT_CANCELED, "Cannot check balance! No connection with blockchain nodes");
}
}
} else if (request.isMethod(Electrum_Request.METHOD_GetFee)) {
if (request.getResultString() == "-1") {
etFee.setText("3");
}
}
} else {
// etFee.setError(request.error);
// btnSend.setVisibility(View.INVISIBLE);
if (sharedCounter != null) {
int errCounter = sharedCounter.errorRequest.incrementAndGet();
if (errCounter >= sharedCounter.allRequest) {
FinishActivityWithError(Activity.RESULT_CANCELED, "Cannot calculate fee! No connection with blockchain nodes");
}
} else {
FinishActivityWithError(Activity.RESULT_CANCELED, "Cannot calculate fee! No connection with blockchain nodes");
}
return;
}
} catch (JSONException e) {
if (sharedCounter != null) {
int errCounter = sharedCounter.errorRequest.incrementAndGet();
if (errCounter >= sharedCounter.allRequest) {
e.printStackTrace();
FinishActivityWithError(Activity.RESULT_CANCELED, "Cannot calculate fee! No connection with blockchain nodes");
}
} else {
e.printStackTrace();
FinishActivityWithError(Activity.RESULT_CANCELED, "Cannot calculate fee! No connection with blockchain nodes");
}
}
}
}
}
private class ETHRequestTask extends Infura_Task {
ETHRequestTask(Blockchain blockchain) {
super(blockchain);
}
@Override
protected void onPostExecute(List<Infura_Request> requests) {
super.onPostExecute(requests);
for (Infura_Request request : requests) {
try {
Long price = 0L;
if (request.error == null) {
if (request.isMethod(Infura_Request.METHOD_ETH_GetGasPrice)) {
try {
String gasPrice = request.getResultString();
gasPrice = gasPrice.substring(2);
BigInteger l = new BigInteger(gasPrice, 16);
BigInteger m = mCard.getBlockchain() == Blockchain.Token ? BigInteger.valueOf(55000) : BigInteger.valueOf(21000);
l = l.multiply(m);
String feeInGwei = mCard.getAmountInGwei(String.valueOf(l));
minFee = feeInGwei;
maxFee = feeInGwei;
normalFee = feeInGwei;
etFee.setText(feeInGwei);
etFee.setError(null);
btnSend.setVisibility(View.VISIBLE);
feeRequestSuccess = true;
balanceRequestSuccess = true;
dtVerifyed = new Date();
minFeeInInternalUnits = mCard.InternalUnitsFromString(feeInGwei);
} catch (JSONException e) {
e.printStackTrace();
FinishActivityWithError(Activity.RESULT_CANCELED, "Can't calculate fee! No connection with blockchain nodes");
}
}
} else {
FinishActivityWithError(Activity.RESULT_CANCELED, "Can't calculate fee! No connection with blockchain nodes");
}
} catch (JSONException e) {
e.printStackTrace();
FinishActivityWithError(Activity.RESULT_CANCELED, "Can't calculate fee! No connection with blockchain nodes");
}
}
}
}
private class ConnectFeeTask extends Fee_Task {
public ConnectFeeTask(SharedData sharedData) {
super(sharedData);
}
@Override
protected void onPostExecute(List<Fee_Request> requests) {
super.onPostExecute(requests);
for (Fee_Request request : requests) {
if (request.error == null) {
long minFeeRate = 0;
try {
try {
String tmpAnswer = request.getAsString();
BigDecimal minFeeBD = new BigDecimal(tmpAnswer);
BigDecimal multiplicator = new BigDecimal("100000000");
minFeeBD = minFeeBD.multiply(multiplicator);
BigInteger minFeeBI = minFeeBD.toBigInteger();
minFeeRate = minFeeBI.longValue();
} catch (Exception e) {
if (sharedCounter != null) {
int errCounter = sharedCounter.errorRequest.incrementAndGet();
if (errCounter >= sharedCounter.allRequest) {
progressBar.setVisibility(View.INVISIBLE);
FinishActivityWithError(Activity.RESULT_CANCELED, "Cannot calculate fee! No connection with blockchain nodes");
}
} else {
progressBar.setVisibility(View.INVISIBLE);
FinishActivityWithError(Activity.RESULT_CANCELED, "Cannot calculate fee! No connection with blockchain nodes");
}
//FinishActivityWithError(Activity.RESULT_CANCELED, "Cannot calculate fee! No connection with blockchain nodes");
return;
}
if (minFeeRate == 0) {
progressBar.setVisibility(View.INVISIBLE);
FinishActivityWithError(Activity.RESULT_CANCELED, "Cannot calculate fee! Wrong data received from the node");
return;
}
long inputCount = request.txSize;
if (inputCount != 0) {
minFeeRate = minFeeRate * inputCount;
} else {
minFeeRate = minFeeRate * 256;
}
} catch (Exception e) {
e.printStackTrace();
if (sharedCounter != null) {
int errCounter = sharedCounter.errorRequest.incrementAndGet();
if (errCounter >= sharedCounter.allRequest) {
progressBar.setVisibility(View.INVISIBLE);
FinishActivityWithError(Activity.RESULT_CANCELED, "Cannot calculate fee! No connection with blockchain nodes");
}
} else {
progressBar.setVisibility(View.INVISIBLE);
FinishActivityWithError(Activity.RESULT_CANCELED, "Cannot calculate fee! No connection with blockchain nodes");
}
return;
}
progressBar.setVisibility(View.INVISIBLE);
float finalFee = (float) minFeeRate / (float) 10000;
finalFee = Math.round(finalFee) / (float) 10000;
if (request.getBlockCount() == Fee_Request.MINIMAL) {
minFee = String.valueOf(finalFee);
minFeeInInternalUnits = mCard.InternalUnitsFromString(String.valueOf(finalFee));
} else if (request.getBlockCount() == Fee_Request.NORMAL) {
normalFee = String.valueOf(finalFee);
} else if (request.getBlockCount() == Fee_Request.PRIORITY) {
maxFee = String.valueOf(finalFee);
}
doSetFee(rgFee.getCheckedRadioButtonId());
etFee.setError(null);
feeRequestSuccess = true;
if (feeRequestSuccess && balanceRequestSuccess) {
btnSend.setVisibility(View.VISIBLE);
}
dtVerifyed = new Date();
} else {
if (sharedCounter != null) {
int errCounter = sharedCounter.errorRequest.incrementAndGet();
if (errCounter >= sharedCounter.allRequest) {
progressBar.setVisibility(View.INVISIBLE);
FinishActivityWithError(Activity.RESULT_CANCELED, "Cannot calculate fee! No connection with blockchain nodes");
}
} else {
progressBar.setVisibility(View.INVISIBLE);
FinishActivityWithError(Activity.RESULT_CANCELED, "Cannot calculate fee! No connection with blockchain nodes");
}
}
}
}
}
private void doSetFee(int checkedRadioButtonId) {
switch (checkedRadioButtonId) {
case R.id.rbMinimalFee:
if (minFee != null) etFee.setText(minFee);
else etFee.setText("?");
break;
case R.id.rbNormalFee:
if (normalFee != null) etFee.setText(normalFee);
else etFee.setText("?");
break;
case R.id.rbMaximumFee:
if (maxFee != null) etFee.setText(maxFee);
else etFee.setText("?");
break;
}
}
@Override
public void onResume() {
super.onResume();
mNfcManager.onResume();
}
@Override
public void onPause() {
super.onPause();
mNfcManager.onPause();
}
@Override
public void onStop() {
super.onStop();
mNfcManager.onStop();
}
}

View file

@ -0,0 +1,334 @@
package com.tangem.presentation.activity;
import android.app.Activity;
import android.content.Intent;
import android.content.res.ColorStateList;
import android.graphics.Color;
import android.nfc.NfcAdapter;
import android.nfc.Tag;
import android.nfc.tech.IsoDep;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import com.tangem.cardReader.CardProtocol;
import com.tangem.cardReader.NfcManager;
import com.tangem.cardReader.Util;
import com.tangem.wallet.NoExtendedLengthSupportDialog;
import com.tangem.wallet.PINStorage;
import com.tangem.wallet.R;
import com.tangem.wallet.Tangem_Card;
import com.tangem.wallet.WaitSecurityDelayDialog;
public class CreateNewWalletActivity extends AppCompatActivity implements NfcAdapter.ReaderCallback, CardProtocol.Notifications {
public static final int RESULT_INVALID_PIN = Activity.RESULT_FIRST_USER;
private Tangem_Card mCard;
private TextView tvCardID;
private NfcManager mNfcManager;
private static final String logTag = "CreateNewActivity";
private ProgressBar progressBar;
private CreateNewWalletTask createNewWalletTask;
private boolean lastReadSuccess = true;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_create_new_wallet);
MainActivity.commonInit(getApplicationContext());
mCard = new Tangem_Card(getIntent().getStringExtra("UID"));
mCard.LoadFromBundle(getIntent().getExtras().getBundle("Card"));
tvCardID = (TextView) findViewById(R.id.tvCardID);
tvCardID.setText(mCard.getCIDDescription());
mNfcManager = new NfcManager(this, this);
progressBar = (ProgressBar) findViewById(R.id.progressBar);
progressBar.setProgressTintList(ColorStateList.valueOf(Color.DKGRAY));
progressBar.setVisibility(View.INVISIBLE);
}
@Override
public void onTagDiscovered(Tag tag) {
try {
// get IsoDep handle and run cardReader thread
final IsoDep isoDep = IsoDep.get(tag);
if (isoDep == null) {
throw new CardProtocol.TangemException(getString(R.string.wrong_tag_err));
}
byte UID[] = tag.getId();
String sUID = Util.byteArrayToHexString(UID);
Log.v(logTag, "UID: " + sUID);
if (sUID.equals(mCard.getUID())) {
if (lastReadSuccess) {
isoDep.setTimeout(mCard.getPauseBeforePIN2() + 5000);
} else {
isoDep.setTimeout(mCard.getPauseBeforePIN2() + 65000);
}
createNewWalletTask = new CreateNewWalletTask(isoDep, this);
createNewWalletTask.start();
} else {
Log.d(logTag, "Mismatch card UID (" + sUID + " instead of " + mCard.getUID() + ")");
mNfcManager.IgnoreTag(isoDep.getTag());
return;
}
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void onResume() {
super.onResume();
mNfcManager.onResume();
}
@Override
public void onPause() {
mNfcManager.onPause();
if (createNewWalletTask != null) {
createNewWalletTask.cancel(true);
}
super.onPause();
}
@Override
public void onStop() {
// dismiss enable NFC dialog
mNfcManager.onStop();
if (createNewWalletTask != null) {
createNewWalletTask.cancel(true);
}
super.onStop();
}
// @Override
// public Dialog CreateNFCDialog(int id, AlertDialogWrapper.Builder builder, LayoutInflater li) {
// return mNfcManager.ShowNFCEnableDialog(); //onCreateDialog(id, builder, li);
// }
private class CreateNewWalletTask extends Thread {
IsoDep mIsoDep;
CardProtocol.Notifications mNotifications;
private boolean isCancelled = false;
public CreateNewWalletTask(IsoDep isoDep, CardProtocol.Notifications notifications) {
mIsoDep = isoDep;
mNotifications = notifications;
}
@Override
public void run() {
if (mIsoDep == null) {
return;
}
CardProtocol protocol = new CardProtocol(getBaseContext(), mIsoDep, mCard, mNotifications);
mNotifications.OnReadStart(protocol);
try {
// for Samsung's bugs -
// Workaround for the Samsung Galaxy S5 (since the
// first connection always hangs on transceive).
int timeout = mIsoDep.getTimeout();
mIsoDep.connect();
mIsoDep.close();
mIsoDep.connect();
mIsoDep.setTimeout(timeout);
try {
mNotifications.OnReadProgress(protocol, 5);
Log.i("CreateNewWalletTask", "[-- Start create new wallet --]");
if (isCancelled) return;
protocol.run_VerifyCard();
Log.i("CreateNewWalletTask", "Manufacturer: " + protocol.getCard().getManufacturer().getOfficialName());
mNotifications.OnReadProgress(protocol, 30);
if (isCancelled) return;
// if (mCard.getPauseBeforePIN2() > 0) {
// mNotifications.OnReadWait(mCard.getPauseBeforePIN2());
// }
// try {
protocol.run_CreateWallet(PINStorage.getPIN2());
// } finally {
// mNotifications.OnReadWait(0);
// }
mNotifications.OnReadProgress(protocol, 60);
if (isCancelled) return;
protocol.run_Read();
} finally {
mNfcManager.IgnoreTag(mIsoDep.getTag());
}
} catch (Exception e) {
e.printStackTrace();
protocol.setError(e);
} finally {
Log.i("CreateNewWalletTask", "[-- Finish create new wallet --]");
mNotifications.OnReadFinish(protocol);
}
}
public void cancel(Boolean AllowInterrupt) {
try {
if (this.isAlive()) {
isCancelled = true;
join(500);
}
if (this.isAlive() && AllowInterrupt) {
interrupt();
mNotifications.OnReadCancel();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
public void OnReadStart(CardProtocol cardProtocol) {
progressBar.post(new Runnable() {
@Override
public void run() {
progressBar.setVisibility(View.VISIBLE);
progressBar.setProgress(5);
}
});
}
public void OnReadFinish(final CardProtocol cardProtocol) {
createNewWalletTask = null;
if (cardProtocol != null) {
if (cardProtocol.getError() == null) {
progressBar.post(new Runnable() {
@Override
public void run() {
progressBar.setProgress(100);
progressBar.setProgressTintList(ColorStateList.valueOf(Color.GREEN));
Intent intent = new Intent();
intent.putExtra("UID", cardProtocol.getCard().getUID());
intent.putExtra("Card", cardProtocol.getCard().getAsBundle());
setResult(Activity.RESULT_OK, intent);
finish();
}
});
} else {
lastReadSuccess = false;
if (cardProtocol.getError() instanceof CardProtocol.TangemException_InvalidPIN) {
progressBar.post(new Runnable() {
@Override
public void run() {
progressBar.setProgress(100);
progressBar.setProgressTintList(ColorStateList.valueOf(Color.RED));
}
});
progressBar.postDelayed(new Runnable() {
@Override
public void run() {
try {
progressBar.setProgress(0);
progressBar.setProgressTintList(ColorStateList.valueOf(Color.DKGRAY));
progressBar.setVisibility(View.INVISIBLE);
Intent intent = new Intent();
intent.putExtra("message", "Cannot create wallet. Make sure you enter correct PIN2!");
intent.putExtra("UID", cardProtocol.getCard().getUID());
intent.putExtra("Card", cardProtocol.getCard().getAsBundle());
setResult(RESULT_INVALID_PIN, intent);
finish();
} catch (Exception e) {
e.printStackTrace();
}
}
}, 500);
return;
} else {
progressBar.post(new Runnable() {
@Override
public void run() {
if (cardProtocol.getError() instanceof CardProtocol.TangemException_ExtendedLengthNotSupported) {
if (!NoExtendedLengthSupportDialog.allreadyShowed) {
new NoExtendedLengthSupportDialog().show(getFragmentManager(), "NoExtendedLengthSupportDialog");
}
} else {
Toast.makeText(getBaseContext(), "Try to scan again", Toast.LENGTH_LONG).show();
}
progressBar.setProgress(100);
progressBar.setProgressTintList(ColorStateList.valueOf(Color.RED));
}
});
}
}
}
progressBar.postDelayed(new Runnable() {
@Override
public void run() {
try {
progressBar.setProgress(0);
progressBar.setProgressTintList(ColorStateList.valueOf(Color.DKGRAY));
progressBar.setVisibility(View.INVISIBLE);
} catch (Exception e) {
e.printStackTrace();
}
}
}, 500);
}
public void OnReadProgress(CardProtocol protocol, final int progress) {
progressBar.post(new Runnable() {
@Override
public void run() {
progressBar.setProgress(progress);
}
});
}
public void OnReadCancel() {
createNewWalletTask = null;
progressBar.postDelayed(new Runnable() {
@Override
public void run() {
try {
progressBar.setProgress(0);
progressBar.setProgressTintList(ColorStateList.valueOf(Color.DKGRAY));
progressBar.setVisibility(View.INVISIBLE);
} catch (Exception e) {
e.printStackTrace();
}
}
}, 500);
}
@Override
public void OnReadWait(final int msec) {
WaitSecurityDelayDialog.OnReadWait(this, msec);
}
@Override
public void OnReadBeforeRequest(int timeout) {
WaitSecurityDelayDialog.onReadBeforeRequest(this, timeout);
}
@Override
public void OnReadAfterRequest() {
WaitSecurityDelayDialog.onReadAfterRequest(this);
}
}

View file

@ -0,0 +1,361 @@
package com.tangem.presentation.activity;
import android.app.Activity;
import android.content.Intent;
import android.content.res.ColorStateList;
import android.graphics.Color;
import android.nfc.NfcAdapter;
import android.nfc.Tag;
import android.nfc.tech.IsoDep;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import com.tangem.cardReader.CardProtocol;
import com.tangem.cardReader.NfcManager;
import com.tangem.cardReader.Util;
import com.tangem.wallet.NoExtendedLengthSupportDialog;
import com.tangem.wallet.R;
import com.tangem.wallet.Tangem_Card;
import com.tangem.wallet.VerifyCardTask;
import com.tangem.wallet.WaitSecurityDelayDialog;
public class EmptyWalletActivity extends AppCompatActivity implements NfcAdapter.ReaderCallback, CardProtocol.Notifications {
private static final int REQUEST_CODE_CREATE_NEW_WALLET_ACTIVITY = 2;
private static final int REQUEST_CODE_REQUEST_PIN2 = 3;
private static final int REQUEST_CODE_VERIFY_CARD = 4;
Tangem_Card mCard;
TextView tvCardID, tvIssuer, tvIssuerData, tvBlockchain;
ProgressBar progressBar;
ImageView ivBlockchain, ivPIN, ivPIN2orSecurityDelay, ivDeveloperVersion;
private NfcManager mNfcManager;
private final String logTag = "EmptyWalletActivity";
private boolean lastReadSuccess = true;
private VerifyCardTask verifyCardTask = null;
private int requestPIN2Count = 0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_empty_wallet);
MainActivity.commonInit(getApplicationContext());
mNfcManager = new NfcManager(this, this);
mCard = new Tangem_Card(getIntent().getStringExtra("UID"));
mCard.LoadFromBundle(getIntent().getExtras().getBundle("Card"));
tvCardID = findViewById(R.id.tvCardID);
tvCardID.setText(mCard.getCIDDescription());
tvIssuer = findViewById(R.id.tvIssuer);
tvIssuerData = findViewById(R.id.tvIssuerData);
tvBlockchain = findViewById(R.id.tvBlockchain);
tvIssuer.setText(mCard.getIssuerDescription());
tvIssuerData.setText(mCard.getIssuerDataDescription());
//tvBlockchain.setText(mCard.getBlockchain().getOfficialName());
tvBlockchain.setText(mCard.getBlockchainName());
progressBar = findViewById(R.id.progressBar);
ivBlockchain = findViewById(R.id.imgBlockchain);
ivPIN = findViewById(R.id.imgPIN);
ivPIN2orSecurityDelay = findViewById(R.id.imgPIN2orSecurityDelay);
ivDeveloperVersion = findViewById(R.id.imgDeveloperVersion);
ivBlockchain.setImageResource(mCard.getBlockchain().getImageResource(this, mCard.getTokenSymbol()));
if (mCard.useDefaultPIN1()) {
ivPIN.setImageResource(R.drawable.unlock_pin1);
ivPIN.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(EmptyWalletActivity.this, "This banknote is protected by default PIN1 code", Toast.LENGTH_LONG).show();
}
});
} else {
ivPIN.setImageResource(R.drawable.lock_pin1);
ivPIN.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(EmptyWalletActivity.this, "This banknote is protected by user's PIN1 code", Toast.LENGTH_LONG).show();
}
});
}
if (mCard.getPauseBeforePIN2() > 0 && (mCard.useDefaultPIN2() || !mCard.useSmartSecurityDelay())) {
ivPIN2orSecurityDelay.setImageResource(R.drawable.timer);
ivPIN2orSecurityDelay.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(EmptyWalletActivity.this, String.format("This banknote will enforce %.0f seconds security delay for all operations requiring PIN2 code", mCard.getPauseBeforePIN2() / 1000.0), Toast.LENGTH_LONG).show();
}
});
} else if (mCard.useDefaultPIN2()) {
ivPIN2orSecurityDelay.setImageResource(R.drawable.unlock_pin2);
ivPIN2orSecurityDelay.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(EmptyWalletActivity.this, "This banknote is protected by default PIN2 code", Toast.LENGTH_LONG).show();
}
});
} else {
ivPIN2orSecurityDelay.setImageResource(R.drawable.lock_pin2);
ivPIN2orSecurityDelay.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(EmptyWalletActivity.this, "This banknote is protected by user's PIN2 code", Toast.LENGTH_LONG).show();
}
});
}
if (mCard.useDevelopersFirmware()) {
ivDeveloperVersion.setImageResource(R.drawable.ic_developer_version);
ivDeveloperVersion.setVisibility(View.VISIBLE);
ivDeveloperVersion.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(EmptyWalletActivity.this, "Unlocked banknote, only for development use", Toast.LENGTH_LONG).show();
}
});
} else {
ivDeveloperVersion.setVisibility(View.INVISIBLE);
}
Button btnNewWallet = findViewById(R.id.btnNewWallet);
btnNewWallet.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//CreateSelectBlockchainDialog();
requestPIN2Count = 0;
Intent intent = new Intent(getBaseContext(), RequestPINActivity.class);
intent.putExtra("mode", RequestPINActivity.Mode.RequestPIN2.toString());
intent.putExtra("UID", mCard.getUID());
intent.putExtra("Card", mCard.getAsBundle());
startActivityForResult(intent, REQUEST_CODE_REQUEST_PIN2);
}
});
if (getIntent().getExtras().containsKey(NfcAdapter.EXTRA_TAG)) {
Tag tag = getIntent().getParcelableExtra(NfcAdapter.EXTRA_TAG);
if (tag != null) {
onTagDiscovered(tag);
}
}
}
private void doCreateNewWallet() {
Intent intent = new Intent(this, CreateNewWalletActivity.class);
intent.putExtra("UID", mCard.getUID());
intent.putExtra("Card", mCard.getAsBundle());
// intent.putExtra("newPIN",mCard.getPIN());
// intent.putExtra("newPIN2","12345678");
startActivityForResult(intent, REQUEST_CODE_CREATE_NEW_WALLET_ACTIVITY);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_CODE_CREATE_NEW_WALLET_ACTIVITY) {
if (resultCode == Activity.RESULT_OK) {
if (data != null) {
data.putExtra("modification", "updateAndViewCard");
data.putExtra("updateDelay", 0);
setResult(Activity.RESULT_OK, data);
}
finish();
} else {
if (data != null && data.getExtras().containsKey("UID") && data.getExtras().containsKey("Card")) {
Tangem_Card updatedCard = new Tangem_Card(data.getStringExtra("UID"));
updatedCard.LoadFromBundle(data.getBundleExtra("Card"));
mCard = updatedCard;
}
if (resultCode == CreateNewWalletActivity.RESULT_INVALID_PIN && requestPIN2Count < 2) {
requestPIN2Count++;
Intent intent = new Intent(getBaseContext(), RequestPINActivity.class);
intent.putExtra("mode", RequestPINActivity.Mode.RequestPIN2.toString());
intent.putExtra("UID", mCard.getUID());
intent.putExtra("Card", mCard.getAsBundle());
startActivityForResult(intent, REQUEST_CODE_REQUEST_PIN2);
return;
}
}
setResult(resultCode, data);
finish();
} else if (requestCode == REQUEST_CODE_REQUEST_PIN2) {
if (resultCode == Activity.RESULT_OK) {
doCreateNewWallet();
}
}
}
@Override
public void onTagDiscovered(Tag tag) {
try {
final IsoDep isoDep = IsoDep.get(tag);
if (isoDep == null) {
throw new CardProtocol.TangemException(getString(R.string.wrong_tag_err));
}
byte UID[] = tag.getId();
String sUID = Util.byteArrayToHexString(UID);
if (!mCard.getUID().equals(sUID)) {
Log.d(logTag, "Invalid UID: " + sUID);
mNfcManager.IgnoreTag(isoDep.getTag());
return;
} else {
Log.v(logTag, "UID: " + sUID);
}
if (lastReadSuccess) {
isoDep.setTimeout(1000);
} else {
isoDep.setTimeout(65000);
}
//lastTag = tag;
verifyCardTask = new VerifyCardTask(this, mCard, mNfcManager, isoDep, this);
verifyCardTask.start();
} catch (Exception e) {
e.printStackTrace();
}
}
public void OnReadStart(CardProtocol cardProtocol) {
progressBar.post(new Runnable() {
@Override
public void run() {
progressBar.setVisibility(View.VISIBLE);
progressBar.setProgress(5);
}
});
}
public void OnReadFinish(final CardProtocol cardProtocol) {
verifyCardTask = null;
if (cardProtocol != null) {
if (cardProtocol.getError() == null) {
progressBar.post(new Runnable() {
@Override
public void run() {
progressBar.setProgress(100);
progressBar.setProgressTintList(ColorStateList.valueOf(Color.GREEN));
Intent intent = new Intent(EmptyWalletActivity.this, VerifyCardActivity.class);
// TODO обновить карту mCard
intent.putExtra("UID", cardProtocol.getCard().getUID());
intent.putExtra("Card", cardProtocol.getCard().getAsBundle());
startActivityForResult(intent, REQUEST_CODE_VERIFY_CARD);
//addCard(cardProtocol.getCard());
}
});
} else {
// remove last UIDs because of error and no card read
progressBar.post(new Runnable() {
@Override
public void run() {
lastReadSuccess = false;
if (cardProtocol.getError() instanceof CardProtocol.TangemException_ExtendedLengthNotSupported) {
if (!NoExtendedLengthSupportDialog.allreadyShowed) {
new NoExtendedLengthSupportDialog().show(getFragmentManager(), "NoExtendedLengthSupportDialog");
}
} else {
Toast.makeText(EmptyWalletActivity.this, "Try to scan again", Toast.LENGTH_LONG).show();
}
progressBar.setProgress(100);
progressBar.setProgressTintList(ColorStateList.valueOf(Color.RED));
}
});
}
}
progressBar.postDelayed(new Runnable() {
@Override
public void run() {
try {
progressBar.setProgress(0);
progressBar.setProgressTintList(ColorStateList.valueOf(Color.DKGRAY));
progressBar.setVisibility(View.INVISIBLE);
} catch (Exception e) {
e.printStackTrace();
}
}
}, 500);
}
public void OnReadProgress(CardProtocol protocol, final int progress) {
progressBar.post(new Runnable() {
@Override
public void run() {
progressBar.setProgress(progress);
}
});
}
public void OnReadCancel() {
verifyCardTask = null;
progressBar.postDelayed(new Runnable() {
@Override
public void run() {
try {
progressBar.setProgress(0);
progressBar.setProgressTintList(ColorStateList.valueOf(Color.DKGRAY));
progressBar.setVisibility(View.INVISIBLE);
} catch (Exception e) {
e.printStackTrace();
}
}
}, 500);
}
@Override
public void OnReadWait(int msec) {
WaitSecurityDelayDialog.OnReadWait(this, msec);
}
@Override
public void OnReadBeforeRequest(int timeout) {
WaitSecurityDelayDialog.onReadBeforeRequest(this, timeout);
}
@Override
public void OnReadAfterRequest() {
WaitSecurityDelayDialog.onReadAfterRequest(this);
}
@Override
public void onResume() {
super.onResume();
mNfcManager.onResume();
}
@Override
public void onPause() {
super.onPause();
mNfcManager.onPause();
}
@Override
public void onStop() {
super.onStop();
mNfcManager.onStop();
}
}

View file

@ -0,0 +1,40 @@
package com.tangem.presentation.activity;
import android.app.Activity;
import android.content.Intent;
import android.nfc.NfcAdapter;
import android.nfc.Tag;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import com.tangem.wallet.LoadedWalletActivityFragment;
import com.tangem.wallet.R;
public class LoadedWalletActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_loaded_wallet);
MainActivity.commonInit(getApplicationContext());
if( getIntent().getExtras().containsKey(NfcAdapter.EXTRA_TAG) )
{
Tag tag=getIntent().getParcelableExtra(NfcAdapter.EXTRA_TAG);
if (tag != null ) {
LoadedWalletActivityFragment fragment=(LoadedWalletActivityFragment)(getSupportFragmentManager().findFragmentById(R.id.loaded_wallet_fragment));
fragment.onTagDiscovered(tag);
}
}
}
@Override
public void onBackPressed() {
LoadedWalletActivityFragment loadedWalletActivityFragment=(LoadedWalletActivityFragment) getSupportFragmentManager().findFragmentById(R.id.loaded_wallet_fragment);
Intent data= loadedWalletActivityFragment.prepareResultIntent();
data.putExtra("modification", "update");
setResult(Activity.RESULT_OK, data);
finish();
}
}

View file

@ -0,0 +1,71 @@
package com.tangem.presentation.activity;
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import com.tangem.wallet.BuildConfig;
import com.tangem.wallet.R;
/**
* An example full-screen activity that shows and hides the system UI (i.e.
* status bar and navigation/system bar) with user interaction.
*/
public class LogoActivity extends AppCompatActivity {
private final Runnable mHideRunnable = new Runnable() {
@Override
public void run() {
hide();
}
};
ImageView imgLogo;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_logo);
imgLogo= (ImageView) findViewById(R.id.imgLogo);
// Set up the user interaction to manually show or hide the system UI.
imgLogo.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {hide();
}
});
}
@Override
protected void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
// Trigger the initial hide() shortly after the activity has been
// created, to briefly hint to the user that UI controls
// are available.
TextView AppVersion = (TextView) findViewById(R.id.AppVersion);
AppVersion.setText("BETA v." + BuildConfig.VERSION_NAME);
if( !getIntent().getBooleanExtra("skipAutoHide",false)) {
delayedHide(1000);
}
}
private void hide() {
Intent intent=new Intent(getBaseContext(),MainActivity.class);
startActivity(intent);
finish();
}
/**
* Schedules a call to hide() in [delay] milliseconds, canceling any
* previously scheduled calls.
*/
private void delayedHide(int delayMillis) {
//imgLogo.removeCallbacks(mHideRunnable);
imgLogo.postDelayed(mHideRunnable, delayMillis);
}
}

View file

@ -0,0 +1,413 @@
package com.tangem.presentation.activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.DialogFragment;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ResolveInfo;
import android.net.Uri;
import android.nfc.NfcAdapter;
import android.nfc.Tag;
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.PopupMenu;
import android.util.Log;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.Transformation;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;
import com.scottyab.rootbeer.RootBeer;
import com.skyfishjy.library.RippleBackground;
import com.tangem.wallet.BuildConfig;
import com.tangem.wallet.DeviceNFCAntennaLocation;
import com.tangem.wallet.LastSignStorage;
import com.tangem.wallet.LogFileProvider;
import com.tangem.wallet.Logger;
import com.tangem.wallet.MainActivityFragment;
import com.tangem.wallet.PINStorage;
import com.tangem.wallet.PhoneUtility;
import com.tangem.wallet.R;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
public class MainActivity extends AppCompatActivity implements PopupMenu.OnMenuItemClickListener {
public static final int DIALOG_ENABLE_INTERNET = 1;
private static final int REQUEST_CODE_SEND_EMAIL = 2;
private String logTag = "MainActivity";
public interface OnCardsClean {
void doClean();
}
// public interface OnCreateNFCDialog {
// Dialog CreateNFCDialog(int id, AlertDialogWrapper.Builder builder, LayoutInflater li);
// }
OnCardsClean onCardsClean;
// OnCreateNFCDialog onCreateNFCDialog;
NfcAdapter.ReaderCallback onNFCReaderCallback;
FloatingActionButton fab;
public void setOnCardsClean(OnCardsClean onCardsClean) {
this.onCardsClean = onCardsClean;
}
// public void setOnCreateNFCDialog(OnCreateNFCDialog onCreateNFCDialog) {
// this.onCreateNFCDialog = onCreateNFCDialog;
// }
public void setNfcAdapterReaderCallback(NfcAdapter.ReaderCallback callback) {
this.onNFCReaderCallback = callback;
}
public void showCleanButton() {
findViewById(R.id.tvTapPrompt).setVisibility(View.INVISIBLE);
}
public void hideCleanButton() {
findViewById(R.id.tvTapPrompt).setVisibility(View.VISIBLE);
}
public static class RootFoundDialog extends DialogFragment {
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
return new AlertDialog.Builder(getActivity())
.setIcon(R.drawable.tangem_logo_small_new)
.setTitle("Your Android device is rooted. Security at risk!")
.setCancelable(false)
.setPositiveButton("Got it",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
}
}
)
.create();
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
RootBeer rootBeer = new RootBeer(this);
if (rootBeer.isRootedWithoutBusyBoxCheck()) {
//we found indication of root
new RootFoundDialog().show(getFragmentManager(), "RootFoundDialog");
}
setContentView(R.layout.activity_main);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_NOSENSOR);
commonInit(getApplicationContext());
TextView tvNFCHint = findViewById(R.id.tvNFCHint);
if(tvNFCHint != null)
{
// tvNFCHint.setText("Scan a banknote with your\n" + PhoneUtility.GetPhoneName() + "\nas shown above");
tvNFCHint.setText("Scan a banknote with your\n smartphone as shown above");
}
DeviceNFCAntennaLocation antenna = new DeviceNFCAntennaLocation();
antenna.getAntennaLocation();
final LinearLayout hand = findViewById(R.id.llHand);
final LinearLayout nfc = findViewById(R.id.llNFC);
final RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams) hand.getLayoutParams();
final RelativeLayout.LayoutParams lp2 = (RelativeLayout.LayoutParams) nfc.getLayoutParams();
final float dp = getResources().getDisplayMetrics().density;
final float lm = dp*(69 + antenna.X * 75);
lp.topMargin = (int) (dp*(-100 + antenna.Y * 250));
lp2.topMargin = (int) (dp*(-125 + antenna.Y * 250));
nfc.setLayoutParams(lp2);
Animation a = new Animation() {
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
lp.leftMargin = (int)(lm * interpolatedTime);
hand.setLayoutParams(lp);
}
};
a.setDuration(2000); // in ms
a.setInterpolator(new DecelerateInterpolator());
hand.startAnimation(a);
fab = findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
showMenu(view);
}
});
MainActivityFragment mainActivityFragment=(MainActivityFragment)getSupportFragmentManager().findFragmentById(R.id.fragmentMain);
if( mainActivityFragment.getCardListAdapter().getItemCount()>0 )
{
showCleanButton();
}else {
hideCleanButton();
}
final RippleBackground rippleBackground=(RippleBackground)findViewById(R.id.imNFC);
rippleBackground.startRippleAnimation();
Intent intent = getIntent();
if (intent != null && (NfcAdapter.ACTION_TECH_DISCOVERED.equals(intent.getAction()) || NfcAdapter.ACTION_NDEF_DISCOVERED.equals(intent.getAction()))) {
Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
if (tag != null && onNFCReaderCallback != null) {
onNFCReaderCallback.onTagDiscovered(tag);
}
}
}
public static void commonInit(Context context)
{
if( PINStorage.needInit() ) {
PINStorage.Init(context);
}
if( LastSignStorage.needInit() ) {
LastSignStorage.Init(context);
}
}
@Override
protected void onDestroy() {
// Logger.StopSaveToFile(getApplicationContext());
super.onDestroy();
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
if (intent != null && (NfcAdapter.ACTION_TECH_DISCOVERED.equals(intent.getAction()) || NfcAdapter.ACTION_NDEF_DISCOVERED.equals(intent.getAction()))) {
Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
if (tag != null && onNFCReaderCallback != null) {
onNFCReaderCallback.onTagDiscovered(tag);
}
}
}
@Override
public boolean onKeyDown(int keycode, KeyEvent e) {
switch (keycode) {
case KeyEvent.KEYCODE_MENU:
fab.requestFocus();
showMenu(fab);
return true;
}
return super.onKeyDown(keycode, e);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
if( BuildConfig.DEBUG ) {
for(int i=0; i<menu.size(); i++ ) menu.getItem(i).setVisible(true);
}
return true;
}
public class Compress {
private static final int BUFFER = 2048;
private String[] _files;
private String _zipFile;
Compress(String[] files, String zipFile) {
_files = files;
_zipFile = zipFile;
}
void zip() {
try {
BufferedInputStream origin = null;
FileOutputStream dest = new FileOutputStream(_zipFile);
ZipOutputStream out = new ZipOutputStream(new BufferedOutputStream(dest));
byte data[] = new byte[BUFFER];
for (String _file : _files) {
Log.v("Compress", "Adding: " + _file);
FileInputStream fi = new FileInputStream(_file);
origin = new BufferedInputStream(fi, BUFFER);
ZipEntry entry = new ZipEntry(_file.substring(_file.lastIndexOf("/") + 1));
out.putNextEntry(entry);
int count;
while ((count = origin.read(data, 0, BUFFER)) != -1) {
out.write(data, 0, count);
}
origin.close();
}
out.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
File zipFile = null;
private void sendEmail(String subject, String text, File[] filelocations) {
if (zipFile != null) return;
try {
Intent intent = new Intent(Intent.ACTION_SEND)
//.setData(new Uri.Builder().scheme("mailto").build())
.setType("text/plain")
.putExtra(Intent.EXTRA_EMAIL, new String[]{"android@tangem.com"})
.putExtra(Intent.EXTRA_SUBJECT, subject)
.putExtra(Intent.EXTRA_TEXT, text);
if (filelocations != null && filelocations.length > 0) {
String[] fileNames = new String[filelocations.length];
for (int i = 0; i < filelocations.length; i++)
fileNames[i] = filelocations[i].getAbsolutePath();
zipFile = File.createTempFile("tangemLogs", ".zip", filelocations[0].getParentFile());
Compress compress = new Compress(fileNames, zipFile.getAbsolutePath());
compress.zip();
Log.e(logTag, String.format("Send %d bytes zip with logs", zipFile.length()));
Uri attachment = Uri.parse("content://" + LogFileProvider.AUTHORITY + "/"
+ zipFile.getName());
intent.putExtra(Intent.EXTRA_STREAM, attachment);
zipFile.deleteOnExit();
}
List<ResolveInfo> activities = getPackageManager().queryIntentActivities(intent, 0);
boolean isIntentSafe = activities.size() > 0;
if (isIntentSafe) {
startActivity(intent);
return;
}
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_CODE_SEND_EMAIL) {
if (zipFile != null) {
zipFile.delete();
zipFile = null;
}
}
super.onActivityResult(requestCode, resultCode, data);
}
public void showMenu(View v) {
PopupMenu popup = new PopupMenu(this, v);
MenuInflater inflater = popup.getMenuInflater();
inflater.inflate(R.menu.menu_main, popup.getMenu());
if( BuildConfig.DEBUG ) {
for(int i=0; i<popup.getMenu().size(); i++ ) popup.getMenu().getItem(i).setVisible(true);
}
popup.setOnMenuItemClickListener(this);
popup.show();
}
@Override
public boolean onMenuItemClick(MenuItem item) {
return onOptionsItemSelected(item);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
switch (id) {
case R.id.sendLogs:
File f = null;
try {
f = Logger.collectLogs(this);
if (f != null) {
Log.e(logTag, String.format("Collect %d log bytes", f.length()));
sendEmail("Logs", PhoneUtility.getDeviceInfo(), new File[]{f});
} else {
Log.e(logTag, "Can't create temporaly log file");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (f != null && f.exists()) {
f.delete();
}
}
return true;
case R.id.managePIN:
showSavePinActivity();
return true;
case R.id.managePIN2:
showSavePin2Activity();
return true;
case R.id.cleanCards:
if (onCardsClean != null) onCardsClean.doClean();
hideCleanButton();
return true;
case R.id.about:
showLogoActivity();
return true;
}
return super.onOptionsItemSelected(item);
}
private void showLogoActivity() {
Intent intent = new Intent(getBaseContext(), LogoActivity.class);
intent.putExtra("skipAutoHide",true);
startActivity(intent);
}
private void showSavePinActivity() {
Intent intent = new Intent(getBaseContext(), SavePINActivity.class);
intent.putExtra("PIN2", false);
startActivity(intent);
}
private void showSavePin2Activity() {
Intent intent = new Intent(getBaseContext(), SavePINActivity.class);
intent.putExtra("PIN2", true);
startActivity(intent);
}
}

View file

@ -0,0 +1,227 @@
package com.tangem.presentation.activity;
import android.app.Activity;
import android.content.Intent;
import android.nfc.NfcAdapter;
import android.nfc.Tag;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.text.Editable;
import android.text.Html;
import android.text.Spanned;
import android.text.TextWatcher;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.TextView;
import com.tangem.cardReader.NfcManager;
import com.tangem.wallet.Blockchain;
import com.tangem.wallet.CoinEngine;
import com.tangem.wallet.CoinEngineFactory;
import com.tangem.wallet.FormatUtil;
import com.tangem.wallet.R;
import com.tangem.wallet.Tangem_Card;
import java.io.IOException;
public class PreparePaymentActivity extends AppCompatActivity implements NfcAdapter.ReaderCallback {
private static final int REQUEST_CODE_SCAN_QR = 1;
private static final int REQUEST_CODE_SEND_PAYMENT = 2;
Button btnVerify;
EditText etWallet;
EditText etAmount;
TextView tvCurrency;
TextView tvCardId, tvBalance, tvBalanceEquivalent, tvAmountEquivalent;
ImageView ivCamera;
boolean use_mCurrency;
Tangem_Card mCard;
private NfcManager mNfcManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_prepare_payment);
MainActivity.commonInit(getApplicationContext());
mNfcManager = new NfcManager(this, this);
mCard = new Tangem_Card(getIntent().getStringExtra("UID"));
mCard.LoadFromBundle(getIntent().getExtras().getBundle("Card"));
btnVerify = (Button) findViewById(R.id.btnVerify);
etWallet = (EditText) findViewById(R.id.etWallet);
etAmount = (EditText) findViewById(R.id.etAmount);
ivCamera = (ImageView) findViewById(R.id.ivCamera);
tvCurrency = (TextView) findViewById(R.id.tvCurrency);
tvCardId = (TextView) findViewById(R.id.tvCardID);
tvBalance = (TextView) findViewById(R.id.tvBalance);
tvBalanceEquivalent = (TextView) findViewById(R.id.tvBalanceEquivalent);
tvAmountEquivalent = (TextView) findViewById(R.id.tvAmountEquivalent);
tvCardId.setText(mCard.getCIDDescription());
CoinEngine engine = CoinEngineFactory.Create(mCard.getBlockchain());
if (mCard.getBlockchain() == Blockchain.Token) {
Spanned html = Html.fromHtml(engine.GetBalanceWithAlter(mCard));
tvBalance.setText(html);
} else {
tvBalance.setText(engine.GetBalanceWithAlter(mCard));
}
tvBalanceEquivalent.setText(engine.GetBalanceEquivalent(mCard));
if (etAmount != null && mCard.getRemainingSignatures() < 2) {
etAmount.setEnabled(false);
}
if (!mCard.getAmountEquivalentDescriptionAvailable()) {
tvBalanceEquivalent.setError("Service unavailable");
} else {
tvBalanceEquivalent.setError(null);
}
etAmount.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
try {
CoinEngine engine = CoinEngineFactory.Create(mCard.getBlockchain());
tvAmountEquivalent.setText(engine.GetAmountEqualentDescriptor(mCard, etAmount.getText().toString()));
if (!mCard.getAmountEquivalentDescriptionAvailable()) {
tvAmountEquivalent.setError("Service unavailable");
} else {
tvAmountEquivalent.setError(null);
}
} catch (Exception e) {
e.printStackTrace();
tvAmountEquivalent.setText("");
}
}
@Override
public void afterTextChanged(Editable s) {
}
});
if (mCard.getBlockchain() == Blockchain.Ethereum || mCard.getBlockchain() == Blockchain.EthereumTestNet) {
tvCurrency.setText(engine.GetBalanceCurrency(mCard));
use_mCurrency = false;
etAmount.setText(engine.GetBalanceValue(mCard));
} else if (mCard.getBlockchain() == Blockchain.Bitcoin || mCard.getBlockchain() == Blockchain.BitcoinTestNet) {
Double balance = engine.GetBalanceLong(mCard) / (mCard.getBlockchain().getMultiplier() / 1000.0);
tvCurrency.setText("m" + mCard.getBlockchain().getCurrency());
use_mCurrency = true;
String output = FormatUtil.DoubleToString(balance);
etAmount.setText(output);
} else if (mCard.getBlockchain() == Blockchain.BitcoinCash || mCard.getBlockchain() == Blockchain.BitcoinCashTestNet) {
Double balance = engine.GetBalanceLong(mCard) / (mCard.getBlockchain().getMultiplier() / 1000.0);
tvCurrency.setText("m" + mCard.getBlockchain().getCurrency());
use_mCurrency = true;
String output = FormatUtil.DoubleToString(balance);
etAmount.setText(output);
} else {
tvCurrency.setText(engine.GetBalanceCurrency(mCard));
use_mCurrency = false;
etAmount.setText(engine.GetBalanceValue(mCard));
}
btnVerify.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String strAmount;
strAmount = etAmount.getText().toString();
CoinEngine engine = CoinEngineFactory.Create(mCard.getBlockchain());
try {
if (!engine.CheckAmount(mCard, etAmount.getText().toString())) {
etAmount.setError("Not enough funds on your card");
}
} catch (Exception e) {
etAmount.setError("Unknown amount format");
return;
}
boolean checkAddress = engine.ValdateAddress(etWallet.getText().toString(), mCard);
if (!checkAddress) {
etWallet.setError("Incorrect destination wallet address");
return;
}
if (etWallet.getText().toString().equals(mCard.getWallet())) {
etWallet.setError("Destination wallet address equal source address");
return;
}
Intent intent = new Intent(getBaseContext(), ConfirmPaymentActivity.class);
intent.putExtra("UID", mCard.getUID());
intent.putExtra("Card", mCard.getAsBundle());
intent.putExtra("Wallet", etWallet.getText().toString());
intent.putExtra("Amount", strAmount);
startActivityForResult(intent, REQUEST_CODE_SEND_PAYMENT);
}
});
ivCamera.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(getBaseContext(), QRScanActivity.class);
startActivityForResult(intent, REQUEST_CODE_SCAN_QR);
}
});
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_CODE_SCAN_QR && resultCode == Activity.RESULT_OK && data != null && data.getExtras().containsKey("QRCode")) {
String code = data.getStringExtra("QRCode");
if (code.contains("bitcoin:")) {
String tmp[] = code.split("bitcoin:");
code = tmp[1];
}
etWallet.setText(code);
} else if (requestCode == REQUEST_CODE_SEND_PAYMENT) {
setResult(resultCode, data);
finish();
}
}
@Override
public void onTagDiscovered(Tag tag) {
try {
Log.w(getClass().getName(), "Ignore discovered tag!");
mNfcManager.IgnoreTag(tag);
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void onResume() {
super.onResume();
mNfcManager.onResume();
}
@Override
public void onPause() {
super.onPause();
mNfcManager.onPause();
}
@Override
public void onStop() {
super.onStop();
mNfcManager.onStop();
}
}

View file

@ -0,0 +1,338 @@
package com.tangem.presentation.activity;
import android.app.Activity;
import android.content.Intent;
import android.content.res.ColorStateList;
import android.graphics.Color;
import android.nfc.NfcAdapter;
import android.nfc.Tag;
import android.nfc.tech.IsoDep;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import com.tangem.cardReader.CardProtocol;
import com.tangem.cardReader.NfcManager;
import com.tangem.cardReader.Util;
import com.tangem.wallet.NoExtendedLengthSupportDialog;
import com.tangem.wallet.PINStorage;
import com.tangem.wallet.R;
import com.tangem.wallet.Tangem_Card;
import com.tangem.wallet.WaitSecurityDelayDialog;
public class PurgeActivity extends AppCompatActivity implements NfcAdapter.ReaderCallback, CardProtocol.Notifications {
public static final int RESULT_INVALID_PIN = Activity.RESULT_FIRST_USER;
private Tangem_Card mCard;
private TextView tvCardID;
private NfcManager mNfcManager;
private static final String logTag = "Purge";
private ProgressBar progressBar;
private PurgeTask purgeTask;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_purge);
MainActivity.commonInit(getApplicationContext());
mCard = new Tangem_Card(getIntent().getStringExtra("UID"));
mCard.LoadFromBundle(getIntent().getExtras().getBundle("Card"));
tvCardID = (TextView) findViewById(R.id.tvCardID);
tvCardID.setText(mCard.getCIDDescription());
mNfcManager = new NfcManager(this, this);
progressBar = (ProgressBar) findViewById(R.id.progressBar);
progressBar.setProgressTintList(ColorStateList.valueOf(Color.DKGRAY));
progressBar.setVisibility(View.INVISIBLE);
}
@Override
public void onTagDiscovered(Tag tag) {
try {
// get IsoDep handle and run cardReader thread
final IsoDep isoDep = IsoDep.get(tag);
if (isoDep == null) {
throw new CardProtocol.TangemException(getString(R.string.wrong_tag_err));
}
byte UID[] = tag.getId();
String sUID = Util.byteArrayToHexString(UID);
Log.v(logTag, "UID: " + sUID);
if (sUID.equals(mCard.getUID())) {
isoDep.setTimeout(mCard.getPauseBeforePIN2() + 65000);
purgeTask = new PurgeTask(isoDep, this);
purgeTask.start();
} else {
Log.d(logTag, "Mismatch card UID (" + sUID + " instead of " + mCard.getUID() + ")");
mNfcManager.IgnoreTag(isoDep.getTag());
return;
}
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void onResume() {
super.onResume();
mNfcManager.onResume();
}
@Override
public void onPause() {
mNfcManager.onPause();
if (purgeTask != null) {
purgeTask.cancel(true);
}
super.onPause();
}
@Override
public void onStop() {
// dismiss enable NFC dialog
mNfcManager.onStop();
if (purgeTask != null) {
purgeTask.cancel(true);
}
super.onStop();
}
// @Override
// public Dialog CreateNFCDialog(int id, AlertDialogWrapper.Builder builder, LayoutInflater li) {
// return mNfcManager.onCreateDialog(id, builder, li);
// }
private class PurgeTask extends Thread {
private String txOutAddress;
IsoDep mIsoDep;
CardProtocol.Notifications mNotifications;
private boolean isCancelled = false;
public PurgeTask(IsoDep isoDep, CardProtocol.Notifications notifications) {
mIsoDep = isoDep;
mNotifications = notifications;
}
@Override
public void run() {
if (mIsoDep == null) {
return;
}
CardProtocol protocol = new CardProtocol(getBaseContext(), mIsoDep, mCard, mNotifications);
mNotifications.OnReadStart(protocol);
try {
// for Samsung's bugs -
// Workaround for the Samsung Galaxy S5 (since the
// first connection always hangs on transceive).
int timeout = mIsoDep.getTimeout();
mIsoDep.connect();
mIsoDep.close();
mIsoDep.connect();
mIsoDep.setTimeout(timeout);
try {
mNotifications.OnReadProgress(protocol, 5);
Log.i("PurgeTask", "[-- Start purge --]");
if (isCancelled) return;
if (mCard.getPauseBeforePIN2() > 0) {
mNotifications.OnReadWait(mCard.getPauseBeforePIN2());
}
// try {
protocol.run_PurgeWallet(PINStorage.getPIN2());
// } finally {
// mNotifications.OnReadWait(0);
// }
mNotifications.OnReadProgress(protocol, 50);
protocol.run_Read();
mNotifications.OnReadProgress(protocol, 100);
if (isCancelled) return;
} finally {
mNfcManager.IgnoreTag(mIsoDep.getTag());
}
} catch (Exception e) {
e.printStackTrace();
protocol.setError(e);
} finally {
Log.i("PurgeTask", "[-- Finish purge --]");
mNotifications.OnReadFinish(protocol);
}
}
public void cancel(Boolean AllowInterrupt) {
try {
if (this.isAlive()) {
isCancelled = true;
join(500);
}
if (this.isAlive() && AllowInterrupt) {
interrupt();
mNotifications.OnReadCancel();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
public void OnReadStart(CardProtocol cardProtocol) {
progressBar.post(new Runnable() {
@Override
public void run() {
progressBar.setVisibility(View.VISIBLE);
progressBar.setProgress(5);
}
});
}
public void OnReadFinish(final CardProtocol cardProtocol) {
purgeTask = null;
if (cardProtocol != null) {
if (cardProtocol.getError() == null) {
progressBar.post(new Runnable() {
@Override
public void run() {
progressBar.setProgress(100);
progressBar.setProgressTintList(ColorStateList.valueOf(Color.GREEN));
Intent intent = new Intent();
intent.putExtra("UID", cardProtocol.getCard().getUID());
intent.putExtra("Card", cardProtocol.getCard().getAsBundle());
setResult(Activity.RESULT_OK, intent);
finish();
}
});
} else {
if (cardProtocol.getError() instanceof CardProtocol.TangemException_InvalidPIN) {
progressBar.post(new Runnable() {
@Override
public void run() {
progressBar.setProgress(100);
progressBar.setProgressTintList(ColorStateList.valueOf(Color.RED));
}
});
progressBar.postDelayed(new Runnable() {
@Override
public void run() {
try {
progressBar.setProgress(0);
progressBar.setProgressTintList(ColorStateList.valueOf(Color.DKGRAY));
progressBar.setVisibility(View.INVISIBLE);
Intent intent = new Intent();
intent.putExtra("UID", cardProtocol.getCard().getUID());
intent.putExtra("Card", cardProtocol.getCard().getAsBundle());
intent.putExtra("message", "Cannot erase wallet. Make sure you enter correct PIN2!");
setResult(RESULT_INVALID_PIN, intent);
finish();
} catch (Exception e) {
e.printStackTrace();
}
}
}, 500);
return;
} else {
progressBar.post(new Runnable() {
@Override
public void run() {
if (cardProtocol.getError() instanceof CardProtocol.TangemException_ExtendedLengthNotSupported) {
if (!NoExtendedLengthSupportDialog.allreadyShowed) {
new NoExtendedLengthSupportDialog().show(getFragmentManager(), "NoExtendedLengthSupportDialog");
}
} else {
Toast.makeText(getBaseContext(), "Try to scan again", Toast.LENGTH_LONG).show();
}
progressBar.setProgress(100);
progressBar.setProgressTintList(ColorStateList.valueOf(Color.RED));
}
});
}
}
}
progressBar.postDelayed(new Runnable() {
@Override
public void run() {
try {
progressBar.setProgress(0);
progressBar.setProgressTintList(ColorStateList.valueOf(Color.DKGRAY));
progressBar.setVisibility(View.INVISIBLE);
} catch (Exception e) {
e.printStackTrace();
}
}
}, 500);
}
public void OnReadProgress(CardProtocol protocol, final int progress) {
progressBar.post(new Runnable() {
@Override
public void run() {
progressBar.setProgress(progress);
}
});
}
public void OnReadCancel() {
purgeTask = null;
progressBar.postDelayed(new Runnable() {
@Override
public void run() {
try {
progressBar.setProgress(0);
progressBar.setProgressTintList(ColorStateList.valueOf(Color.DKGRAY));
progressBar.setVisibility(View.INVISIBLE);
} catch (Exception e) {
e.printStackTrace();
}
}
}, 500);
}
@Override
public void OnReadWait(final int msec) {
WaitSecurityDelayDialog.OnReadWait(this, msec);
}
@Override
public void OnReadBeforeRequest(int timeout) {
WaitSecurityDelayDialog.onReadBeforeRequest(this, timeout);
}
@Override
public void OnReadAfterRequest() {
WaitSecurityDelayDialog.onReadAfterRequest(this);
}
}

View file

@ -0,0 +1,90 @@
package com.tangem.presentation.activity;
import android.Manifest;
import android.app.Activity;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.support.v4.app.ActivityCompat;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import com.google.zxing.Result;
import me.dm7.barcodescanner.zxing.ZXingScannerView;
public class QRScanActivity extends AppCompatActivity implements ZXingScannerView.ResultHandler{
private ZXingScannerView mScannerView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//setContentView(R.layout.activity_qrscan);
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
Log.e("QRScanActivity","User hasn't granted permission to use camera");
ActivityCompat.requestPermissions(this, new String[] { Manifest.permission.CAMERA}, 1);
}else {
runScanner();
}
}
void runScanner()
{
mScannerView = new ZXingScannerView(this); // Programmatically initialize the scanner view
setContentView(mScannerView);
mScannerView.setResultHandler(this); // Register ourselves as a handler for scan results.
mScannerView.startCamera();
}
@Override
public void onRequestPermissionsResult(int requestCode,
String permissions[], int[] grantResults) {
switch (requestCode) {
case 1: {
// If request is cancelled, the result arrays are empty.
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Log.i("QRScanActivity","permission was granted");
// permission was granted, yay! Do the
// contacts-related task you need to do.
runScanner();
} else {
Log.e("QRScanActivity","permission denied");
setResult(Activity.RESULT_CANCELED);
finish();
// permission denied, boo! Disable the
// functionality that depends on this permission.
}
return;
}
// other 'case' lines to check for other
// permissions this app might request
}
}
@Override
public void handleResult(Result result) {
Intent data=new Intent();
data.putExtra("QRCode", result.getText());
setResult(Activity.RESULT_OK, data);
finish();
}
@Override
protected void onPause() {
super.onPause();
if( mScannerView!=null ) mScannerView.stopCamera(); // Stop camera on pause
}
@Override
protected void onResume() {
super.onResume();
if( mScannerView!=null ) mScannerView.startCamera();
}
}

View file

@ -0,0 +1,535 @@
package com.tangem.presentation.activity;
import android.Manifest;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.app.Activity;
import android.app.KeyguardManager;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.hardware.fingerprint.FingerprintManager;
import android.nfc.NfcAdapter;
import android.nfc.Tag;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.KeyPermanentlyInvalidatedException;
import android.security.keystore.KeyProperties;
import android.support.v4.app.ActivityCompat;
import android.support.v7.app.AppCompatActivity;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import com.tangem.cardReader.NfcManager;
import com.tangem.wallet.FingerprintHelper;
import com.tangem.wallet.PINStorage;
import com.tangem.wallet.R;
import com.tangem.wallet.Tangem_Card;
import java.io.IOException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
public class RequestPINActivity extends AppCompatActivity implements NfcAdapter.ReaderCallback, FingerprintHelper.FingerprintHelperListener {
public enum Mode {RequestPIN, RequestPIN2, RequestNewPIN, RequestNewPIN2, ConfirmNewPIN, ConfirmNewPIN2}
Mode mode;
boolean allowFingerprint = false;
private NfcManager mNfcManager;
private TextView tvPIN;
private StartFingerprintReaderTask mStartFingerprintReaderTask;
public static final String KEY_ALIAS = "pinKey";
public static final String KEYSTORE = "AndroidKeyStore";
private FingerprintManager fingerprintManager;
private FingerprintHelper fingerprintHelper;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_request_pin);
MainActivity.commonInit(getApplicationContext());
mNfcManager = new NfcManager(this, this);
tvPIN = findViewById(R.id.pin);
OnClickListener onButtonNClick = new OnClickListener() {
@Override
public void onClick(View view) {
tvPIN.setText(tvPIN.getText() + (String) ((Button) view).getText());
}
};
Button btn0 = findViewById(R.id.btn0);
btn0.setOnClickListener(onButtonNClick);
Button btn1 = findViewById(R.id.btn1);
btn1.setOnClickListener(onButtonNClick);
Button btn2 = findViewById(R.id.btn2);
btn2.setOnClickListener(onButtonNClick);
Button btn3 = findViewById(R.id.btn3);
btn3.setOnClickListener(onButtonNClick);
Button btn4 = findViewById(R.id.btn4);
btn4.setOnClickListener(onButtonNClick);
Button btn5 = findViewById(R.id.btn5);
btn5.setOnClickListener(onButtonNClick);
Button btn6 = findViewById(R.id.btn6);
btn6.setOnClickListener(onButtonNClick);
Button btn7 = findViewById(R.id.btn7);
btn7.setOnClickListener(onButtonNClick);
Button btn8 = findViewById(R.id.btn8);
btn8.setOnClickListener(onButtonNClick);
Button btn9 = findViewById(R.id.btn9);
btn9.setOnClickListener(onButtonNClick);
Button btnBS = findViewById(R.id.btnBackspace);
btnBS.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
String S = tvPIN.getText().toString();
if (S.length() > 0) {
tvPIN.setText(S.substring(0, S.length() - 1));
}
}
});
Button btnContinue = findViewById(R.id.btnContinue);
btnContinue.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
doContinue();
}
});
mode = Mode.valueOf(getIntent().getStringExtra("mode"));
TextView tvPrompt = findViewById(R.id.pin_prompt);
if (mode == Mode.RequestNewPIN) {
if (PINStorage.haveEncryptedPIN()) {
allowFingerprint = true;
tvPrompt.setText("Enter new PIN or use fingerprint scanner");
} else {
tvPrompt.setText("Enter new PIN");
}
} else if (mode == Mode.ConfirmNewPIN) {
tvPrompt.setText("Confirm new PIN");
} else if (mode == Mode.RequestPIN) {
if (PINStorage.haveEncryptedPIN()) {
allowFingerprint = true;
tvPrompt.setText("Enter PIN or use fingerprint scanner");
} else {
tvPrompt.setText("Enter PIN");
}
} else if (mode == Mode.RequestNewPIN2) {
if (PINStorage.haveEncryptedPIN2()) {
allowFingerprint = true;
tvPrompt.setText("Enter new PIN2 or use fingerprint scanner");
} else {
tvPrompt.setText("Enter new PIN2");
}
} else if (mode == Mode.ConfirmNewPIN2) {
tvPrompt.setText("Confirm new PIN2");
} else if (mode == Mode.RequestPIN2) {
String UID = getIntent().getStringExtra("UID");
Tangem_Card mCard = new Tangem_Card(UID);
mCard.LoadFromBundle(getIntent().getBundleExtra("Card"));
if (mCard.PIN2 == Tangem_Card.PIN2_Mode.DefaultPIN2 || mCard.PIN2 == Tangem_Card.PIN2_Mode.Unchecked) {
// if we know PIN2 or not try default previously - use it
PINStorage.setPIN2(PINStorage.getDefaultPIN2());
setResult(Activity.RESULT_OK);
finish();
return;
}
if (PINStorage.haveEncryptedPIN2()) {
allowFingerprint = true;
tvPrompt.setText("Enter PIN2 or use fingerprint scanner");
} else {
tvPrompt.setText("Enter PIN2");
}
}
ImageView imgFingerprint = findViewById(R.id.imgFingerprint);
if (!allowFingerprint) {
imgFingerprint.setVisibility(View.GONE);
} else {
imgFingerprint.setVisibility(View.VISIBLE);
}
}
@Override
protected void onPause() {
super.onPause();
if (fingerprintHelper != null)
fingerprintHelper.cancel();
if (mStartFingerprintReaderTask != null) {
mStartFingerprintReaderTask.cancel(true);
mStartFingerprintReaderTask = null;
}
mNfcManager.onPause();
}
@Override
protected void onStop() {
super.onStop();
if (fingerprintHelper != null)
fingerprintHelper.cancel();
if (mStartFingerprintReaderTask != null) {
mStartFingerprintReaderTask.cancel(true);
mStartFingerprintReaderTask = null;
}
mNfcManager.onStop();
}
@Override
protected void onResume() {
super.onResume();
mNfcManager.onResume();
if (allowFingerprint) {
startFingerprintReader();
}
}
private void doContinue() {
if (mStartFingerprintReaderTask != null) {
return;
}
// Reset errors.
tvPIN.setError(null);
// Store values at the time of the login attempt.
String pin = tvPIN.getText().toString();
boolean cancel = false;
View focusView = null;
if (mode == Mode.ConfirmNewPIN) {
if (!pin.equals(getIntent().getStringExtra("newPIN"))) {
tvPIN.setError(getString(R.string.error_pin_confirmation_failed));
focusView = tvPIN;
cancel = true;
}
} else if (mode == Mode.ConfirmNewPIN2) {
if (!pin.equals(getIntent().getStringExtra("newPIN2"))) {
tvPIN.setError(getString(R.string.error_pin_confirmation_failed));
focusView = tvPIN;
cancel = true;
}
} else {
if (TextUtils.isEmpty(pin)) {
tvPIN.setError(getString(R.string.error_empty_pin));
focusView = tvPIN;
cancel = true;
}
}
if (cancel) {
focusView.requestFocus();
} else {
if (mode == Mode.RequestNewPIN || mode == Mode.ConfirmNewPIN) {
Intent resultData = new Intent();
resultData.putExtra("newPIN", pin);
if (mode == Mode.ConfirmNewPIN) {
resultData.putExtra("confirmPIN", pin);
}
setResult(Activity.RESULT_OK, resultData);
finish();
} else if (mode == Mode.RequestNewPIN2 || mode == Mode.ConfirmNewPIN2) {
Intent resultData = new Intent();
resultData.putExtra("newPIN2", pin);
if (mode == Mode.ConfirmNewPIN2) {
resultData.putExtra("confirmPIN2", pin);
}
setResult(Activity.RESULT_OK, resultData);
finish();
} else if (mode == Mode.RequestPIN) {
PINStorage.setUserPIN(pin);
setResult(Activity.RESULT_OK);
finish();
} else if (mode == Mode.RequestPIN2) {
PINStorage.setPIN2(pin);
setResult(Activity.RESULT_OK);
finish();
}
}
}
@Override
public void authenticationFailed(String error) {
doLog(error);
}
@TargetApi(Build.VERSION_CODES.M)
@Override
public void authenticationSucceeded(FingerprintManager.AuthenticationResult result) {
doLog("Authentication succeeded!");
Cipher cipher = result.getCryptoObject().getCipher();
if (mode == Mode.RequestNewPIN || mode == Mode.ConfirmNewPIN) {
Intent resultData = new Intent();
String pin = PINStorage.loadEncryptedPIN(cipher);
resultData.putExtra("newPIN", pin);
resultData.putExtra("confirmPIN", pin);
setResult(Activity.RESULT_OK, resultData);
finish();
} else if (mode == Mode.RequestNewPIN2 || mode == Mode.ConfirmNewPIN2) {
Intent resultData = new Intent();
String pin = PINStorage.loadEncryptedPIN2(cipher);
resultData.putExtra("newPIN2", pin);
resultData.putExtra("confirmPIN2", pin);
setResult(Activity.RESULT_OK, resultData);
finish();
} else if (mode == Mode.RequestPIN) {
PINStorage.loadEncryptedPIN(cipher);
setResult(Activity.RESULT_OK);
} else if (mode == Mode.RequestPIN2) {
PINStorage.loadEncryptedPIN2(cipher);
setResult(Activity.RESULT_OK);
}
finish();
}
private void startFingerprintReader() {
if (!testFingerPrintSettings())
return;
if (!allowFingerprint)
return;
fingerprintHelper = new FingerprintHelper(RequestPINActivity.this);
mStartFingerprintReaderTask = new StartFingerprintReaderTask(this, fingerprintManager, fingerprintHelper);
mStartFingerprintReaderTask.execute((Void) null);
}
@Override
public void onTagDiscovered(Tag tag) {
try {
Log.w(getClass().getName(), "Ignore discovered tag!");
mNfcManager.IgnoreTag(tag);
} catch (IOException e) {
e.printStackTrace();
}
}
public static class StartFingerprintReaderTask extends AsyncTask<Void, Void, Boolean> {
private KeyStore keyStore;
private Cipher cipher;
private FingerprintManager.CryptoObject cryptoObject;
FingerprintManager fingerprintManager;
FingerprintHelper fingerprintHelper;
RequestPINActivity activity;
StartFingerprintReaderTask(RequestPINActivity activity, FingerprintManager fingerprintManager, FingerprintHelper fingerprintHelper) {
this.fingerprintManager = fingerprintManager;
this.fingerprintHelper = fingerprintHelper;
this.activity = activity;
}
@Override
protected Boolean doInBackground(Void... params) {
if (!getKeyStore())
return false;
if (!createNewKey(false))
return false;
if (!getCipher())
return false;
if (!initCipher(Cipher.DECRYPT_MODE))
return false;
return initCryptObject();
}
@Override
protected void onPostExecute(final Boolean success) {
onCancelled();
if (!success) {
doLog("Authentication failed!");
} else {
fingerprintHelper.startAuth(fingerprintManager, cryptoObject);
doLog("Authenticate using fingerprint!");
}
}
@Override
protected void onCancelled() {
activity.mStartFingerprintReaderTask = null;
}
private boolean getKeyStore() {
doLog("Getting keystore...");
try {
keyStore = KeyStore.getInstance(KEYSTORE);
keyStore.load(null); // Create empty keystore
return true;
} catch (KeyStoreException | CertificateException | NoSuchAlgorithmException | IOException e) {
e.printStackTrace();
}
return false;
}
@TargetApi(Build.VERSION_CODES.M)
public boolean createNewKey(boolean forceCreate) {
doLog("Creating new key...");
try {
if (forceCreate)
keyStore.deleteEntry(KEY_ALIAS);
if (!keyStore.containsAlias(KEY_ALIAS)) {
KeyGenerator generator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, KEYSTORE);
generator.init(new KeyGenParameterSpec.Builder(KEY_ALIAS,
KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_CBC)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7)
.setUserAuthenticationRequired(true)
.build()
);
generator.generateKey();
doLog("Key created.");
} else
doLog("Key exists.");
return true;
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
private boolean getCipher() {
doLog("Getting cipher...");
try {
cipher = Cipher.getInstance(
KeyProperties.KEY_ALGORITHM_AES + "/"
+ KeyProperties.BLOCK_MODE_CBC + "/"
+ KeyProperties.ENCRYPTION_PADDING_PKCS7);
return true;
} catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
e.printStackTrace();
}
return false;
}
@TargetApi(Build.VERSION_CODES.M)
private boolean initCipher(int mode) {
doLog("Initializing cipher...");
try {
keyStore.load(null);
SecretKey keyspec = (SecretKey) keyStore.getKey(KEY_ALIAS, null);
if (mode == Cipher.ENCRYPT_MODE) {
cipher.init(mode, keyspec);
} else {
byte[] iv = null;
if (activity.mode == Mode.RequestPIN || activity.mode == Mode.RequestNewPIN || activity.mode == Mode.ConfirmNewPIN) {
iv = PINStorage.loadEncryptedIV();
} else if (activity.mode == Mode.RequestPIN2 || activity.mode == Mode.RequestNewPIN2 || activity.mode == Mode.ConfirmNewPIN2) {
iv = PINStorage.loadEncryptedIV2();
}
IvParameterSpec ivspec = new IvParameterSpec(iv);
cipher.init(mode, keyspec, ivspec);
}
return true;
} catch (KeyPermanentlyInvalidatedException e) {
e.printStackTrace();
createNewKey(true); // Retry after clearing entry
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
@TargetApi(Build.VERSION_CODES.M)
private boolean initCryptObject() {
doLog("Initializing crypt object...");
try {
cryptoObject = new FingerprintManager.CryptoObject(cipher);
return true;
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
}
public static void doLog(String text) {
// Log.e("FP", text);
}
@SuppressLint("NewApi")
private boolean testFingerPrintSettings() {
doLog("Testing Fingerprint Settings");
KeyguardManager keyguardManager = (KeyguardManager) getSystemService(KEYGUARD_SERVICE);
fingerprintManager = (FingerprintManager) getSystemService(FINGERPRINT_SERVICE);
assert keyguardManager != null;
if (!keyguardManager.isKeyguardSecure()) {
doLog("User hasn't enabled Lock Screen");
return false;
}
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.USE_FINGERPRINT) != PackageManager.PERMISSION_GRANTED) {
doLog("User hasn't granted permission to use Fingerprint");
return false;
}
if (!fingerprintManager.hasEnrolledFingerprints()) {
doLog("User hasn't registered any fingerprints");
return false;
}
doLog("Fingerprint authentication is set.\n");
return true;
}
}

View file

@ -0,0 +1,547 @@
package com.tangem.presentation.activity;
import android.Manifest;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.app.Dialog;
import android.app.KeyguardManager;
import android.content.pm.PackageManager;
import android.hardware.fingerprint.FingerprintManager;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.KeyPermanentlyInvalidatedException;
import android.security.keystore.KeyProperties;
import android.support.v4.app.ActivityCompat;
import android.support.v7.app.AppCompatActivity;
import android.text.TextUtils;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.TextView;
import android.widget.Toast;
import com.tangem.wallet.FingerprintHelper;
import com.tangem.wallet.PINStorage;
import com.tangem.wallet.R;
import java.io.IOException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
//import com.afollestad.materialdialogs.AlertDialogWrapper;
public class SavePINActivity extends AppCompatActivity implements FingerprintHelper.FingerprintHelperListener {
private TextView tvPIN;
private CheckBox chkUseFingerprint;
private ConfirmWithFingerprintTask mConfirmWithFingerprintTask;
private KeyStore keyStore;
private Cipher cipher;
private FingerprintManager fingerprintManager;
private FingerprintManager.CryptoObject cryptoObject;
private FingerprintHelper fingerprintHelper;
private boolean UsePIN2=false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_save_pin);
MainActivity.commonInit(getApplicationContext());
UsePIN2=getIntent().getBooleanExtra("PIN2", false);
tvPIN = findViewById(R.id.pin);
OnClickListener onButtonNClick = new OnClickListener() {
@Override
public void onClick(View view) {
tvPIN.setText(String.format("%s%s", tvPIN.getText(),((Button) view).getText()));
}
};
if( UsePIN2 ) {
((TextView) findViewById(R.id.pin_prompt)).setText(R.string.enter_pin2_and_use_fingerprint_to_save_it);
}else{
((TextView) findViewById(R.id.pin_prompt)).setText(R.string.enter_pin_and_use_fingerprint_to_save_it);
}
chkUseFingerprint = (CheckBox) findViewById(R.id.chkUseFingerprint);
if( UsePIN2 ) {
chkUseFingerprint.setChecked(true);
chkUseFingerprint.setEnabled(false);
}else{
chkUseFingerprint.setChecked(PINStorage.haveEncryptedPIN());
chkUseFingerprint.setEnabled(true);
}
Button btn0 = findViewById(R.id.btn0);
btn0.setOnClickListener(onButtonNClick);
Button btn1 = findViewById(R.id.btn1);
btn1.setOnClickListener(onButtonNClick);
Button btn2 = findViewById(R.id.btn2);
btn2.setOnClickListener(onButtonNClick);
Button btn3 = findViewById(R.id.btn3);
btn3.setOnClickListener(onButtonNClick);
Button btn4 = findViewById(R.id.btn4);
btn4.setOnClickListener(onButtonNClick);
Button btn5 = findViewById(R.id.btn5);
btn5.setOnClickListener(onButtonNClick);
Button btn6 = findViewById(R.id.btn6);
btn6.setOnClickListener(onButtonNClick);
Button btn7 = findViewById(R.id.btn7);
btn7.setOnClickListener(onButtonNClick);
Button btn8 = findViewById(R.id.btn8);
btn8.setOnClickListener(onButtonNClick);
Button btn9 = findViewById(R.id.btn9);
btn9.setOnClickListener(onButtonNClick);
Button btnBS = findViewById(R.id.btnBackspace);
btnBS.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
String S = tvPIN.getText().toString();
if (S.length() > 0) {
tvPIN.setText(S.substring(0, S.length() - 1));
}
}
});
Button btnSave = findViewById(R.id.btnSavePIN);
btnSave.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
doSavePIN();
}
});
Button btnDelete = findViewById(R.id.btnDeletePIN);
btnDelete.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
doDeletePIN();
}
});
}
@Override
protected void onPause() {
super.onPause();
if (fingerprintHelper != null)
fingerprintHelper.cancel();
if (mConfirmWithFingerprintTask != null)
mConfirmWithFingerprintTask.cancel(true);
}
@Override
protected void onStop() {
super.onStop();
if (fingerprintHelper != null)
fingerprintHelper.cancel();
if (mConfirmWithFingerprintTask != null)
mConfirmWithFingerprintTask.cancel(true);
}
private enum OnConfirmAction {Save, DeleteEncryptedAndSave, Delete}
OnConfirmAction onConfirmAction;
private void doSavePIN() {
if (mConfirmWithFingerprintTask != null) {
return;
}
tvPIN.setError(null);
String pin = tvPIN.getText().toString();
boolean cancel = false;
View focusView = null;
if (TextUtils.isEmpty(pin)) {
tvPIN.setError(getString(R.string.error_empty_pin));
focusView = tvPIN;
cancel = true;
}
if (cancel) {
focusView.requestFocus();
} else {
if( UsePIN2 )
{
if (!testFingerPrintSettings()) {
tvPIN.postDelayed(new Runnable() {
@Override
public void run() {
finish();
}
}, 2000);
return;
}
onConfirmAction = OnConfirmAction.Save;
mConfirmWithFingerprintTask = new ConfirmWithFingerprintTask();
mConfirmWithFingerprintTask.execute((Void) null);
}else {
if (chkUseFingerprint.isChecked() || PINStorage.haveEncryptedPIN()) {
if (!testFingerPrintSettings()) {
tvPIN.postDelayed(new Runnable() {
@Override
public void run() {
finish();
}
}, 2000);
return;
}
if (chkUseFingerprint.isChecked()) {
onConfirmAction = OnConfirmAction.Save;
} else {
onConfirmAction = OnConfirmAction.DeleteEncryptedAndSave;
}
// Show a progress spinner, and kick off a background task to
// perform the user login attempt.
//showProgress(true);
mConfirmWithFingerprintTask = new ConfirmWithFingerprintTask();
mConfirmWithFingerprintTask.execute((Void) null);
} else {
PINStorage.savePIN(tvPIN.getText().toString());
finish();
}
}
}
}
private void doDeletePIN() {
if( UsePIN2 )
{
if (PINStorage.haveEncryptedPIN2()) {
if (!testFingerPrintSettings()) {
tvPIN.postDelayed(new Runnable() {
@Override
public void run() {
finish();
}
}, 2000);
return;
}
onConfirmAction = OnConfirmAction.Delete;
mConfirmWithFingerprintTask = new ConfirmWithFingerprintTask();
mConfirmWithFingerprintTask.execute((Void) null);
} else {
tvPIN.setText("");
finish();
}
}else {
if (chkUseFingerprint.isChecked() || PINStorage.haveEncryptedPIN()) {
if (!testFingerPrintSettings()) {
tvPIN.postDelayed(new Runnable() {
@Override
public void run() {
finish();
}
}, 2000);
return;
}
onConfirmAction = OnConfirmAction.Delete;
mConfirmWithFingerprintTask = new ConfirmWithFingerprintTask();
mConfirmWithFingerprintTask.execute((Void) null);
} else {
tvPIN.setText("");
PINStorage.deletePIN();
finish();
}
}
}
@Override
public void authenticationFailed(String error) {
print(error);
}
@TargetApi(Build.VERSION_CODES.M)
@Override
public void authenticationSucceeded(FingerprintManager.AuthenticationResult result) {
print("Authentication succeeded!");
cipher = result.getCryptoObject().getCipher();
switch (onConfirmAction) {
case Save:
String textToEncrypt = tvPIN.getText().toString();
if( UsePIN2 )
{
PINStorage.saveEncryptedPIN2(cipher, textToEncrypt);
}else {
PINStorage.saveEncryptedPIN(cipher, textToEncrypt);
}
print(R.string.pin_save_success);
break;
case Delete:
if( UsePIN2 )
{
PINStorage.deleteEncryptedPIN2();
}else {
PINStorage.deleteEncryptedPIN();
PINStorage.deletePIN();
}
tvPIN.setText("");
break;
case DeleteEncryptedAndSave:
if( UsePIN2 )
{
PINStorage.deleteEncryptedPIN2();
PINStorage.saveEncryptedPIN2(cipher, tvPIN.getText().toString());
}else {
PINStorage.deleteEncryptedPIN();
PINStorage.savePIN(tvPIN.getText().toString());
}
break;
}
dFingerPrintConfirmation.dismiss();
finish();
}
private class ConfirmWithFingerprintTask extends AsyncTask<Void, Void, Boolean> {
ConfirmWithFingerprintTask() {
fingerprintHelper = new FingerprintHelper(SavePINActivity.this);
}
@Override
protected Boolean doInBackground(Void... params) {
if (!getKeyStore())
return false;
if (!createNewKey(false))
return false;
if (!getCipher())
return false;
return initCipher(Cipher.ENCRYPT_MODE) && initCryptObject();
}
@Override
protected void onPostExecute(final Boolean success) {
onCancelled();
if (!success) {
Toast.makeText(getBaseContext(), R.string.pin_save_fail, Toast.LENGTH_LONG).show();
} else {
print("Confirm PIN action using fingerprint!");
fingerprintHelper.startAuth(fingerprintManager, cryptoObject);
CreateFingerPrintConfirmationDialog();
}
}
@Override
protected void onCancelled() {
mConfirmWithFingerprintTask = null;
if (dFingerPrintConfirmation != null) {
dFingerPrintConfirmation.cancel();
}
}
}
public void print(String text) {
// Log.e("FP", text);
}
public void print(int id) {
print(getString(id));
}
@SuppressLint("NewApi")
private boolean testFingerPrintSettings() {
print("Testing Fingerprint Settings");
KeyguardManager keyguardManager = (KeyguardManager) getSystemService(KEYGUARD_SERVICE);
fingerprintManager = (FingerprintManager) getSystemService(FINGERPRINT_SERVICE);
if (!keyguardManager.isKeyguardSecure()) {
print("User hasn't enabled Lock Screen");
Toast.makeText(getBaseContext(), "User hasn't enabled Lock Screen", Toast.LENGTH_LONG).show();
return false;
}
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.USE_FINGERPRINT) != PackageManager.PERMISSION_GRANTED) {
print("User hasn't granted permission to use Fingerprint");
Toast.makeText(getBaseContext(), "User hasn't granted permission to use Fingerprint", Toast.LENGTH_LONG).show();
return false;
}
if (!fingerprintManager.hasEnrolledFingerprints()) {
print("User hasn't registered any fingerprints");
Toast.makeText(getBaseContext(), "User hasn't registered any fingerprints", Toast.LENGTH_LONG).show();
return false;
}
print("Fingerprint authentication is set.\n");
return true;
}
private boolean getKeyStore() {
print("Getting keystore...");
try {
keyStore = KeyStore.getInstance(RequestPINActivity.KEYSTORE);
keyStore.load(null); // Create empty keystore
return true;
} catch (KeyStoreException | CertificateException | NoSuchAlgorithmException | IOException e) {
e.printStackTrace();
}
return false;
}
@TargetApi(Build.VERSION_CODES.M)
public boolean createNewKey(boolean forceCreate) {
print("Creating new key...");
try {
if (forceCreate)
keyStore.deleteEntry(RequestPINActivity.KEY_ALIAS);
if (!keyStore.containsAlias(RequestPINActivity.KEY_ALIAS)) {
KeyGenerator generator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, RequestPINActivity.KEYSTORE);
generator.init(new KeyGenParameterSpec.Builder(RequestPINActivity.KEY_ALIAS,
KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_CBC)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7)
.setUserAuthenticationRequired(true)
.build()
);
generator.generateKey();
print("Key created.");
} else
print("Key exists.");
return true;
} catch (Exception e) {
print(e.getMessage());
}
return false;
}
private boolean getCipher() {
print("Getting cipher...");
try {
cipher = Cipher.getInstance(
KeyProperties.KEY_ALGORITHM_AES + "/"
+ KeyProperties.BLOCK_MODE_CBC + "/"
+ KeyProperties.ENCRYPTION_PADDING_PKCS7);
return true;
} catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
e.printStackTrace();
}
return false;
}
@TargetApi(Build.VERSION_CODES.M)
private boolean initCipher(int mode) {
print("Initializing cipher...");
try {
keyStore.load(null);
SecretKey keyspec = (SecretKey) keyStore.getKey(RequestPINActivity.KEY_ALIAS, null);
if (mode == Cipher.ENCRYPT_MODE) {
cipher.init(mode, keyspec);
} else {
byte[] iv = PINStorage.loadEncryptedIV();
IvParameterSpec ivspec = new IvParameterSpec(iv);
cipher.init(mode, keyspec, ivspec);
}
return true;
} catch (KeyPermanentlyInvalidatedException e) {
e.printStackTrace();
createNewKey(true); // Retry after clearing entry
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
@TargetApi(Build.VERSION_CODES.M)
private boolean initCryptObject() {
print("Initializing crypt object...");
try {
cryptoObject = new FingerprintManager.CryptoObject(cipher);
return true;
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
Dialog dFingerPrintConfirmation = null;
private void CreateFingerPrintConfirmationDialog() {
// final AlertDialogWrapper.Builder b = new AlertDialogWrapper.Builder(this);
// switch (onConfirmAction)
// {
// case Save:
// if( UsePIN2) {
// b.setTitle("Confirm new PIN2 saving ");
// }else{
// b.setTitle("Confirm new PIN saving ");
// }
// break;
// case Delete:
// if( UsePIN2 ) {
// b.setTitle("Confirm PIN2 deleting");
// }else{
// b.setTitle("Confirm PIN deleting");
// }
// break;
// case DeleteEncryptedAndSave:
// if( UsePIN2 ) {
// b.setTitle("Confirm deleting old saved PIN2");
// }else {
// b.setTitle("Confirm deleting old saved PIN");
// }
// break;
// }
// View view = getLayoutInflater().inflate(R.layout.dialog_fingerprint_confirmation, null);
// b.setView(view);
// dFingerPrintConfirmation = b.show();
// dFingerPrintConfirmation.setOnCancelListener(new DialogInterface.OnCancelListener() {
// @Override
// public void onCancel(DialogInterface dialog) {
// fingerprintHelper.cancel();
// print("Cancel fingerprint confirmation");
// }
// });
}
}

View file

@ -0,0 +1,33 @@
package com.tangem.presentation.activity;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import com.tangem.wallet.R;
public class SelectBlockchainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_select_blockchain);
// Spinner spBlockchain = (Spinner) findViewById(R.id.spBlockchain);
// ArrayAdapter<Blockchain> adapter = new ArrayAdapter<Blockchain>(this, android.R.layout.simple_spinner_item, Blockchain.values());
// spBlockchain.setAdapter(adapter);
// spBlockchain.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
// @Override
// public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
// Intent intent = new Intent();
// intent.putExtra("blockchain", Blockchain.values()[position].toString());
// setResult(RESULT_OK, intent);
// finish();
// }
//
// @Override
// public void onNothingSelected(AdapterView<?> parent) {
//
// }
// });
}
}

View file

@ -0,0 +1,219 @@
package com.tangem.presentation.activity;
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.KeyEvent;
import android.widget.ProgressBar;
import android.widget.Toast;
import com.tangem.wallet.Blockchain;
import com.tangem.wallet.CoinEngine;
import com.tangem.wallet.CoinEngineFactory;
import com.tangem.wallet.Electrum_Request;
import com.tangem.wallet.Electrum_Task;
import com.tangem.wallet.Infura_Request;
import com.tangem.wallet.Infura_Task;
import com.tangem.wallet.LastSignStorage;
import com.tangem.wallet.R;
import com.tangem.wallet.SharedData;
import com.tangem.wallet.Tangem_Card;
import org.json.JSONException;
import org.json.JSONObject;
import java.math.BigInteger;
import java.util.List;
public class SendTransactionActivity extends AppCompatActivity {
ProgressBar progressBar;
private Tangem_Card mCard;
private String tx;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_send_transaction);
MainActivity.commonInit(getApplicationContext());
progressBar = findViewById(R.id.progressBar);
Intent intent = getIntent();
mCard = new Tangem_Card(getIntent().getStringExtra("UID"));
mCard.LoadFromBundle(intent.getExtras().getBundle("Card"));
tx = intent.getStringExtra("TX");
CoinEngine engine = CoinEngineFactory.Create(mCard.getBlockchain());
if (mCard.getBlockchain() == Blockchain.Ethereum || mCard.getBlockchain() == Blockchain.EthereumTestNet || mCard.getBlockchain() == Blockchain.Token) {
ETHRequestTask task = new ETHRequestTask(mCard.getBlockchain());
Infura_Request req = Infura_Request.SendTransaction(mCard.getWallet(), tx);
req.setID(67);
req.setBlockchain(mCard.getBlockchain());
task.execute(req);
} else if (mCard.getBlockchain() == Blockchain.Bitcoin || mCard.getBlockchain() == Blockchain.BitcoinTestNet ) {
String nodeAddress = engine.GetNode(mCard);
int nodePort = engine.GetNodePort(mCard);
ConnectTask connectTask = new ConnectTask(nodeAddress, nodePort);
connectTask.execute(Electrum_Request.Broadcast(mCard.getWallet(), tx));
}
else if (mCard.getBlockchain() == Blockchain.BitcoinCash || mCard.getBlockchain() == Blockchain.BitcoinCashTestNet ) {
String nodeAddress = engine.GetNode(mCard);
int nodePort = engine.GetNodePort(mCard);
ConnectTask connectTask = new ConnectTask(nodeAddress, nodePort);
connectTask.execute(Electrum_Request.Broadcast(mCard.getWallet(), tx));
}
}
@Override
public boolean onKeyDown(int keycode, KeyEvent e) {
switch (keycode) {
case KeyEvent.KEYCODE_BACK:
Toast.makeText(getBaseContext(),"Please wait while the payment is sent...",Toast.LENGTH_LONG).show();
return true;
}
return super.onKeyDown(keycode, e);
}
void FinishWithError(String Message) {
Intent intent = new Intent();
intent.putExtra("message", "Failed to send transaction. Try again.");
setResult(MainActivity.RESULT_CANCELED, intent);
finish();
}
void FinishWithSuccess() {
Intent intent = new Intent();
intent.putExtra("message", "Transaction has been successfully signed and sent to blockchain node. Wallet balance will be updated in a while");
setResult(MainActivity.RESULT_OK, intent);
finish();
}
private class ETHRequestTask extends Infura_Task {
ETHRequestTask(Blockchain blockchain){
super(blockchain);
}
@Override
protected void onPostExecute(List<Infura_Request> requests) {
super.onPostExecute(requests);
for (Infura_Request request : requests) {
try {
if (request.error == null) {
if (request.isMethod(Infura_Request.METHOD_ETH_SendRawTransaction)) {
try {
String hashTX = "";
try {
String tmp = request.getResultString();
hashTX = tmp;
}catch(JSONException e)
{
JSONObject msg = request.getAnswer();
JSONObject err = msg.getJSONObject("error");
hashTX = err.getString("message");
LastSignStorage.setLastMessage(mCard.getWallet(), hashTX);
FinishWithError(hashTX);
return;
}
try {
if (hashTX.startsWith("0x") || hashTX.startsWith("0X")) {
hashTX = hashTX.substring(2);
}
BigInteger bigInt = new BigInteger(hashTX, 16); //TODO: очень плохой способ
LastSignStorage.setTxWasSend(mCard.getWallet());
LastSignStorage.setLastMessage(mCard.getWallet(), "");
BigInteger nonce = mCard.GetConfirmTXCount();
nonce.add(BigInteger.valueOf(1));
mCard.SetConfirmTXCount(nonce);
Log.e("TX_RESULT", hashTX);
FinishWithSuccess();
}catch(Exception e)
{
FinishWithError(hashTX);
}
} catch (JSONException e) {
e.printStackTrace();
FinishWithError(e.toString());
}
}
} else if (request.error != null) {
FinishWithError(request.error);
}
} catch (JSONException e) {
e.printStackTrace();
FinishWithError(e.toString());
}
}
}
}
private class ConnectTask extends Electrum_Task {
public ConnectTask(String host, int port) {
super(host, port);
}
public ConnectTask(String host, int port, SharedData sharedData) {
super(host, port, sharedData);
}
@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
}
@Override
protected void onPostExecute(List<Electrum_Request> requests) {
super.onPostExecute(requests);
CoinEngine engine = CoinEngineFactory.Create(Blockchain.Bitcoin);
for (Electrum_Request request : requests) {
try {
if (request.error == null) {
if (request.isMethod(Electrum_Request.METHOD_SendTransaction)) {
try {
String hashTX = request.getResultString();
try
{
LastSignStorage.setLastMessage(mCard.getWallet(), hashTX);
if (hashTX.startsWith("0x") || hashTX.startsWith("0X")) {
hashTX = hashTX.substring(2);
}
BigInteger bigInt = new BigInteger(hashTX, 16); //TODO: очень плохой способ
LastSignStorage.setTxWasSend(mCard.getWallet());
LastSignStorage.setLastMessage(mCard.getWallet(), "");
Log.e("TX_RESULT", hashTX);
FinishWithSuccess();
}catch(Exception e)
{
engine.SwitchNode(null);
FinishWithError(hashTX);
return;
}
} catch (JSONException e) {
e.printStackTrace();
engine.SwitchNode(null);
FinishWithError(e.toString());
}
}
} else if (request.error != null) {
engine.SwitchNode(null);
FinishWithError(request.error);
}
} catch (JSONException e) {
e.printStackTrace();
engine.SwitchNode(null);
FinishWithError(e.toString());
}
}
}
}
}

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,331 @@
package com.tangem.presentation.activity;
import android.app.Activity;
import android.content.Intent;
import android.content.res.ColorStateList;
import android.graphics.Color;
import android.nfc.NfcAdapter;
import android.nfc.Tag;
import android.nfc.tech.IsoDep;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import com.tangem.cardReader.CardProtocol;
import com.tangem.cardReader.NfcManager;
import com.tangem.cardReader.Util;
import com.tangem.wallet.NoExtendedLengthSupportDialog;
import com.tangem.wallet.PINStorage;
import com.tangem.wallet.R;
import com.tangem.wallet.Tangem_Card;
import com.tangem.wallet.WaitSecurityDelayDialog;
public class SwapPINActivity extends AppCompatActivity implements NfcAdapter.ReaderCallback, CardProtocol.Notifications {
public static final int RESULT_INVALID_PIN = Activity.RESULT_FIRST_USER;
private Tangem_Card mCard;
private NfcManager mNfcManager;
private static final String logTag = "SwapPIN";
private ProgressBar progressBar;
private SwapPINTask swapPinTask;
private String newPIN, newPIN2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_swap_pin);
MainActivity.commonInit(getApplicationContext());
mCard = new Tangem_Card(getIntent().getStringExtra("UID"));
mCard.LoadFromBundle(getIntent().getExtras().getBundle("Card"));
newPIN = getIntent().getStringExtra("newPIN");
newPIN2 = getIntent().getStringExtra("newPIN2");
TextView tvCardID = findViewById(R.id.tvCardID);
tvCardID.setText(mCard.getCIDDescription());
mNfcManager = new NfcManager(this, this);
progressBar = findViewById(R.id.progressBar);
progressBar.setProgressTintList(ColorStateList.valueOf(Color.DKGRAY));
progressBar.setVisibility(View.INVISIBLE);
}
@Override
public void onTagDiscovered(Tag tag) {
try {
// get IsoDep handle and run cardReader thread
final IsoDep isoDep = IsoDep.get(tag);
if (isoDep == null) {
throw new CardProtocol.TangemException(getString(R.string.wrong_tag_err));
}
byte UID[] = tag.getId();
String sUID = Util.byteArrayToHexString(UID);
Log.v(logTag, "UID: " + sUID);
if (sUID.equals(mCard.getUID())) {
isoDep.setTimeout(mCard.getPauseBeforePIN2() + 65000);
swapPinTask = new SwapPINTask(isoDep, this);
swapPinTask.start();
} else {
Log.d(logTag, "Mismatch card UID (" + sUID + " instead of " + mCard.getUID() + ")");
mNfcManager.IgnoreTag(isoDep.getTag());
}
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void onResume() {
super.onResume();
mNfcManager.onResume();
}
@Override
public void onPause() {
mNfcManager.onPause();
if (swapPinTask != null) {
swapPinTask.cancel(true);
}
super.onPause();
}
@Override
public void onStop() {
// dismiss enable NFC dialog
mNfcManager.onStop();
if (swapPinTask != null) {
swapPinTask.cancel(true);
}
super.onStop();
}
private class SwapPINTask extends Thread {
IsoDep mIsoDep;
CardProtocol.Notifications mNotifications;
private boolean isCancelled = false;
SwapPINTask(IsoDep isoDep, CardProtocol.Notifications notifications) {
mIsoDep = isoDep;
mNotifications = notifications;
}
@Override
public void run() {
if (mIsoDep == null) {
return;
}
CardProtocol protocol = new CardProtocol(getBaseContext(), mIsoDep, mCard, mNotifications);
mNotifications.OnReadStart(protocol);
try {
// for Samsung's bugs -
// Workaround for the Samsung Galaxy S5 (since the
// first connection always hangs on transceive).
int timeout = mIsoDep.getTimeout();
mIsoDep.connect();
mIsoDep.close();
mIsoDep.connect();
mIsoDep.setTimeout(timeout);
try {
mNotifications.OnReadProgress(protocol, 5);
Log.i("SwapTask", "[-- Start swap pin --]");
if (isCancelled) return;
if (mCard.getPauseBeforePIN2() > 0) {
mNotifications.OnReadWait(mCard.getPauseBeforePIN2());
}
// try {
protocol.run_SwapPIN(PINStorage.getPIN2(), newPIN, newPIN2, false);
protocol.setPIN(newPIN);
mCard.setPIN(newPIN);
// } finally {
// mNotifications.OnReadWait(0);
// }
mNotifications.OnReadProgress(protocol, 50);
protocol.run_Read();
mNotifications.OnReadProgress(protocol, 100);
} finally {
mNfcManager.IgnoreTag(mIsoDep.getTag());
}
} catch (Exception e) {
e.printStackTrace();
protocol.setError(e);
} finally {
Log.i("SwapPINTask", "[-- Finish purge --]");
mNotifications.OnReadFinish(protocol);
}
}
public void cancel(Boolean AllowInterrupt) {
try {
if (this.isAlive()) {
isCancelled = true;
join(500);
}
if (this.isAlive() && AllowInterrupt) {
interrupt();
mNotifications.OnReadCancel();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
public void OnReadStart(CardProtocol cardProtocol) {
progressBar.post(new Runnable() {
@Override
public void run() {
progressBar.setVisibility(View.VISIBLE);
progressBar.setProgress(5);
}
});
}
public void OnReadFinish(final CardProtocol cardProtocol) {
swapPinTask = null;
if (cardProtocol != null) {
if (cardProtocol.getError() == null) {
progressBar.post(new Runnable() {
@Override
public void run() {
progressBar.setProgress(100);
progressBar.setProgressTintList(ColorStateList.valueOf(Color.GREEN));
Intent intent = new Intent();
intent.putExtra("UID", cardProtocol.getCard().getUID());
intent.putExtra("Card", cardProtocol.getCard().getAsBundle());
setResult(Activity.RESULT_OK, intent);
finish();
}
});
} else if (cardProtocol.getError() instanceof CardProtocol.TangemException_InvalidPIN) {
progressBar.post(new Runnable() {
@Override
public void run() {
progressBar.setProgress(100);
progressBar.setProgressTintList(ColorStateList.valueOf(Color.RED));
}
});
progressBar.postDelayed(new Runnable() {
@Override
public void run() {
try {
progressBar.setProgress(0);
progressBar.setProgressTintList(ColorStateList.valueOf(Color.DKGRAY));
progressBar.setVisibility(View.INVISIBLE);
Intent intent = new Intent();
intent.putExtra("message", "Cannot change PIN(s). Make sure you enter correct PIN2!");
intent.putExtra("UID", cardProtocol.getCard().getUID());
intent.putExtra("Card", cardProtocol.getCard().getAsBundle());
setResult(RESULT_INVALID_PIN, intent);
finish();
} catch (Exception e) {
e.printStackTrace();
}
}
}, 500);
return;
} else {
progressBar.post(new Runnable() {
@Override
public void run() {
if (cardProtocol.getError() instanceof CardProtocol.TangemException_ExtendedLengthNotSupported) {
if (!NoExtendedLengthSupportDialog.allreadyShowed) {
new NoExtendedLengthSupportDialog().show(getFragmentManager(), "NoExtendedLengthSupportDialog");
}
} else {
Toast.makeText(getBaseContext(), "Try to scan again", Toast.LENGTH_LONG).show();
}
progressBar.setProgress(100);
progressBar.setProgressTintList(ColorStateList.valueOf(Color.RED));
}
});
}
progressBar.postDelayed(new Runnable() {
@Override
public void run() {
try {
progressBar.setProgress(0);
progressBar.setProgressTintList(ColorStateList.valueOf(Color.DKGRAY));
progressBar.setVisibility(View.INVISIBLE);
} catch (Exception e) {
e.printStackTrace();
}
}
}, 500);
}
}
public void OnReadProgress(CardProtocol protocol, final int progress) {
progressBar.post(new Runnable() {
@Override
public void run() {
progressBar.setProgress(progress);
}
});
}
public void OnReadCancel() {
swapPinTask = null;
progressBar.postDelayed(new Runnable() {
@Override
public void run() {
try {
progressBar.setProgress(0);
progressBar.setProgressTintList(ColorStateList.valueOf(Color.DKGRAY));
progressBar.setVisibility(View.INVISIBLE);
} catch (Exception e) {
e.printStackTrace();
}
}
}, 500);
}
@Override
public void OnReadWait(final int msec) {
WaitSecurityDelayDialog.OnReadWait(this, msec);
}
@Override
public void OnReadBeforeRequest(int timeout) {
WaitSecurityDelayDialog.onReadBeforeRequest(this, timeout);
}
@Override
public void OnReadAfterRequest() {
WaitSecurityDelayDialog.onReadAfterRequest(this);
}
}

View file

@ -0,0 +1,30 @@
package com.tangem.presentation.activity;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import com.tangem.wallet.R;
import com.tangem.wallet.VerifyCardActivityFragment;
public class VerifyCardActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_verify_card);
MainActivity.commonInit(getApplicationContext());
}
@Override
public void onBackPressed() {
//super.onBackPressed();
VerifyCardActivityFragment verifyCardActivityFragment = (VerifyCardActivityFragment) getSupportFragmentManager().findFragmentById(R.id.verify_card_fragment);
Intent data = verifyCardActivityFragment.prepareResultIntent();
data.putExtra("modification", "update");
setResult(Activity.RESULT_OK, data);
finish();
}
}