Updated on 2026-08-14
This commit is contained in:
parent
0374157cad
commit
8c17a8d4f0
48 changed files with 3490 additions and 3417 deletions
4
app/src/main/java/com/tangem/presentation/Screen.java
Normal file
4
app/src/main/java/com/tangem/presentation/Screen.java
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
package com.tangem.presentation;
|
||||
|
||||
public class Screen {
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -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");
|
||||
// }
|
||||
// });
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -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) {
|
||||
//
|
||||
// }
|
||||
// });
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue