Updated on 2026-08-14
This commit is contained in:
parent
5ac02ecf5b
commit
bc0a1a8b48
30 changed files with 604 additions and 1825 deletions
|
|
@ -1,90 +0,0 @@
|
|||
package com.tangem.presentation.activity
|
||||
|
||||
import android.content.res.Configuration
|
||||
import android.os.Bundle
|
||||
import android.preference.PreferenceActivity
|
||||
import android.support.annotation.LayoutRes
|
||||
import android.support.v7.app.ActionBar
|
||||
import android.support.v7.app.AppCompatDelegate
|
||||
import android.support.v7.widget.Toolbar
|
||||
import android.view.MenuInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
|
||||
/**
|
||||
* A [android.preference.PreferenceActivity] which implements and proxies the necessary calls
|
||||
* to be used with AppCompat.
|
||||
*/
|
||||
abstract class AppCompatPreferenceActivity : PreferenceActivity() {
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
delegate.installViewFactory()
|
||||
delegate.onCreate(savedInstanceState)
|
||||
super.onCreate(savedInstanceState)
|
||||
}
|
||||
|
||||
override fun onPostCreate(savedInstanceState: Bundle?) {
|
||||
super.onPostCreate(savedInstanceState)
|
||||
delegate.onPostCreate(savedInstanceState)
|
||||
}
|
||||
|
||||
val supportActionBar: ActionBar?
|
||||
get() = delegate.supportActionBar
|
||||
|
||||
fun setSupportActionBar(toolbar: Toolbar?) {
|
||||
delegate.setSupportActionBar(toolbar)
|
||||
}
|
||||
|
||||
override fun getMenuInflater(): MenuInflater {
|
||||
return delegate.menuInflater
|
||||
}
|
||||
|
||||
override fun setContentView(@LayoutRes layoutResID: Int) {
|
||||
delegate.setContentView(layoutResID)
|
||||
}
|
||||
|
||||
override fun setContentView(view: View) {
|
||||
delegate.setContentView(view)
|
||||
}
|
||||
|
||||
override fun setContentView(view: View, params: ViewGroup.LayoutParams) {
|
||||
delegate.setContentView(view, params)
|
||||
}
|
||||
|
||||
override fun addContentView(view: View, params: ViewGroup.LayoutParams) {
|
||||
delegate.addContentView(view, params)
|
||||
}
|
||||
|
||||
override fun onPostResume() {
|
||||
super.onPostResume()
|
||||
delegate.onPostResume()
|
||||
}
|
||||
|
||||
override fun onTitleChanged(title: CharSequence, color: Int) {
|
||||
super.onTitleChanged(title, color)
|
||||
delegate.setTitle(title)
|
||||
}
|
||||
|
||||
override fun onConfigurationChanged(newConfig: Configuration) {
|
||||
super.onConfigurationChanged(newConfig)
|
||||
delegate.onConfigurationChanged(newConfig)
|
||||
}
|
||||
|
||||
override fun onStop() {
|
||||
super.onStop()
|
||||
delegate.onStop()
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
delegate.onDestroy()
|
||||
}
|
||||
|
||||
override fun invalidateOptionsMenu() {
|
||||
delegate.invalidateOptionsMenu()
|
||||
}
|
||||
|
||||
private val delegate: AppCompatDelegate by lazy {
|
||||
AppCompatDelegate.create(this, null)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,126 +0,0 @@
|
|||
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.domain.wallet.TangemCard;
|
||||
import com.tangem.presentation.fragment.WalletInfo;
|
||||
import com.tangem.wallet.R;
|
||||
|
||||
public class CardInfoActivity extends AppCompatActivity implements WalletInfo.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 TangemCard 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 = findViewById(R.id.container);
|
||||
mViewPager.setAdapter(mSectionsPagerAdapter);
|
||||
|
||||
TabLayout tabLayout = findViewById(R.id.tabs);
|
||||
tabLayout.setupWithViewPager(mViewPager);
|
||||
|
||||
String UID = getIntent().getStringExtra("UID");
|
||||
mCard = new TangemCard(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 WalletInfo.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -23,24 +23,24 @@ import android.widget.TextView;
|
|||
import android.widget.Toast;
|
||||
|
||||
import com.tangem.data.network.request.ElectrumRequest;
|
||||
import com.tangem.data.network.request.FeeRequest;
|
||||
import com.tangem.data.network.request.InfuraRequest;
|
||||
import com.tangem.data.network.task.ElectrumTask;
|
||||
import com.tangem.data.network.task.FeeTask;
|
||||
import com.tangem.data.network.task.InfuraTask;
|
||||
import com.tangem.domain.cardReader.NfcManager;
|
||||
import com.tangem.domain.wallet.TangemCard;
|
||||
import com.tangem.util.Util;
|
||||
import com.tangem.util.BTCUtils;
|
||||
import com.tangem.domain.wallet.Blockchain;
|
||||
import com.tangem.domain.wallet.CoinEngine;
|
||||
import com.tangem.domain.wallet.CoinEngineFactory;
|
||||
import com.tangem.util.DerEncodingUtil;
|
||||
import com.tangem.data.network.task.ElectrumTask;
|
||||
import com.tangem.data.network.request.FeeRequest;
|
||||
import com.tangem.data.network.task.FeeTask;
|
||||
import com.tangem.util.FormatUtil;
|
||||
import com.tangem.data.network.request.InfuraRequest;
|
||||
import com.tangem.data.network.task.InfuraTask;
|
||||
import com.tangem.wallet.R;
|
||||
import com.tangem.domain.wallet.SharedData;
|
||||
import com.tangem.domain.wallet.TangemCard;
|
||||
import com.tangem.domain.wallet.Transaction;
|
||||
import com.tangem.domain.wallet.UnspentOutputInfo;
|
||||
import com.tangem.util.BTCUtils;
|
||||
import com.tangem.util.DerEncodingUtil;
|
||||
import com.tangem.util.FormatUtil;
|
||||
import com.tangem.util.Util;
|
||||
import com.tangem.wallet.R;
|
||||
|
||||
import org.json.JSONException;
|
||||
|
||||
|
|
@ -57,392 +57,31 @@ public class ConfirmPaymentActivity extends AppCompatActivity implements NfcAdap
|
|||
|
||||
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;
|
||||
TangemCard mCard;
|
||||
RadioGroup rgFee;
|
||||
String minFee = null, maxFee = null, normalFee = null;
|
||||
Long minFeeInInternalUnits = 0L;
|
||||
private Button btnSend;
|
||||
private boolean feeRequestSuccess = false;
|
||||
private boolean balanceRequestSuccess = false;
|
||||
private EditText etWallet;
|
||||
private TextView tvAmountEquivalent;
|
||||
private TextView tvFeeEquivalent;
|
||||
private EditText etAmount;
|
||||
private EditText etFee;
|
||||
private ImageView ivCamera;
|
||||
private TangemCard mCard;
|
||||
private RadioGroup rgFee;
|
||||
private String minFee = null, maxFee = null, normalFee = null;
|
||||
private 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 TangemCard(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());
|
||||
InfuraRequest req = InfuraRequest.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(ElectrumRequest.CheckBalance(mCard.getWallet()));
|
||||
connectTaskEx.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, ElectrumRequest.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(/*ElectrumRequest.CheckBalance(mCard.getWallet()), */ElectrumRequest.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(FeeRequest.GetFee(mCard.getWallet(), calcSize, FeeRequest.NORMAL),
|
||||
FeeRequest.GetFee(mCard.getWallet(), calcSize, FeeRequest.MINIMAL),
|
||||
FeeRequest.GetFee(mCard.getWallet(), calcSize, FeeRequest.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")) {
|
||||
TangemCard updatedCard = new TangemCard(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<TangemCard.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 int requestPIN2Count = 0;
|
||||
private ProgressBar progressBar;
|
||||
private boolean nodeCheck = false;
|
||||
private Date dtVerifyed = null;
|
||||
|
||||
private class ConnectTask extends ElectrumTask {
|
||||
public ConnectTask(String host, int port) {
|
||||
super(host, port);
|
||||
}
|
||||
|
||||
public ConnectTask(String host, int port, SharedData sharedData) {
|
||||
ConnectTask(String host, int port, SharedData sharedData) {
|
||||
super(host, port, sharedData);
|
||||
}
|
||||
|
||||
|
|
@ -588,7 +227,7 @@ public class ConfirmPaymentActivity extends AppCompatActivity implements NfcAdap
|
|||
}
|
||||
|
||||
private class ConnectFeeTask extends FeeTask {
|
||||
public ConnectFeeTask(SharedData sharedData) {
|
||||
ConnectFeeTask(SharedData sharedData) {
|
||||
super(sharedData);
|
||||
}
|
||||
|
||||
|
|
@ -598,9 +237,7 @@ public class ConfirmPaymentActivity extends AppCompatActivity implements NfcAdap
|
|||
for (FeeRequest request : requests) {
|
||||
if (request.error == null) {
|
||||
long minFeeRate = 0;
|
||||
|
||||
try {
|
||||
|
||||
try {
|
||||
String tmpAnswer = request.getAsString();
|
||||
BigDecimal minFeeBD = new BigDecimal(tmpAnswer);
|
||||
|
|
@ -696,20 +333,219 @@ public class ConfirmPaymentActivity extends AppCompatActivity implements NfcAdap
|
|||
}
|
||||
}
|
||||
|
||||
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
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_confirm_payment);
|
||||
|
||||
MainActivity.commonInit(getApplicationContext());
|
||||
mNfcManager = new NfcManager(this, this);
|
||||
|
||||
mCard = new TangemCard(getIntent().getStringExtra("UID"));
|
||||
mCard.LoadFromBundle(getIntent().getExtras().getBundle("Card"));
|
||||
|
||||
progressBar = findViewById(R.id.progressBar);
|
||||
btnSend = findViewById(R.id.btnSend);
|
||||
etWallet = findViewById(R.id.etWallet);
|
||||
TextView tvCardID = findViewById(R.id.tvCardID);
|
||||
TextView tvBalance = findViewById(R.id.tvBalance);
|
||||
TextView tvCurrency = findViewById(R.id.tvCurrency);
|
||||
TextView tvCurrency2 = findViewById(R.id.tvCurrency2);
|
||||
etAmount = findViewById(R.id.etAmount);
|
||||
etFee = findViewById(R.id.etFee);
|
||||
TextView 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((group, 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(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());
|
||||
InfuraRequest req = InfuraRequest.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(ElectrumRequest.CheckBalance(mCard.getWallet()));
|
||||
connectTaskEx.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, ElectrumRequest.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(/*ElectrumRequest.CheckBalance(mCard.getWallet()), */ElectrumRequest.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(FeeRequest.GetFee(mCard.getWallet(), calcSize, FeeRequest.NORMAL),
|
||||
FeeRequest.GetFee(mCard.getWallet(), calcSize, FeeRequest.MINIMAL),
|
||||
FeeRequest.GetFee(mCard.getWallet(), calcSize, FeeRequest.PRIORITY));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -730,4 +566,155 @@ public class ConfirmPaymentActivity extends AppCompatActivity implements NfcAdap
|
|||
super.onStop();
|
||||
mNfcManager.onStop();
|
||||
}
|
||||
}
|
||||
|
||||
@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")) {
|
||||
TangemCard updatedCard = new TangemCard(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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
|
||||
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<TangemCard.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 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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -25,100 +25,20 @@ import com.tangem.wallet.R;
|
|||
import com.tangem.presentation.dialog.WaitSecurityDelayDialog;
|
||||
|
||||
public class CreateNewWalletActivity extends AppCompatActivity implements NfcAdapter.ReaderCallback, CardProtocol.Notifications {
|
||||
public static final String TAG = CreateNewWalletActivity.class.getSimpleName();
|
||||
|
||||
public static final int RESULT_INVALID_PIN = Activity.RESULT_FIRST_USER;
|
||||
private TangemCard 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 TangemCard(getIntent().getStringExtra("UID"));
|
||||
mCard.LoadFromBundle(getIntent().getExtras().getBundle("Card"));
|
||||
|
||||
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())) {
|
||||
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 IsoDep mIsoDep;
|
||||
private CardProtocol.Notifications mNotifications;
|
||||
private boolean isCancelled = false;
|
||||
|
||||
public CreateNewWalletTask(IsoDep isoDep, CardProtocol.Notifications notifications) {
|
||||
|
|
@ -198,13 +118,85 @@ public class CreateNewWalletActivity extends AppCompatActivity implements NfcAda
|
|||
|
||||
}
|
||||
|
||||
public void OnReadStart(CardProtocol cardProtocol) {
|
||||
progressBar.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
progressBar.setVisibility(View.VISIBLE);
|
||||
progressBar.setProgress(5);
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_create_new_wallet);
|
||||
|
||||
MainActivity.commonInit(getApplicationContext());
|
||||
mCard = new TangemCard(getIntent().getStringExtra("UID"));
|
||||
mCard.LoadFromBundle(getIntent().getExtras().getBundle("Card"));
|
||||
|
||||
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(TAG, "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(TAG, "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();
|
||||
}
|
||||
|
||||
public void OnReadStart(CardProtocol cardProtocol) {
|
||||
progressBar.post(() -> {
|
||||
progressBar.setVisibility(View.VISIBLE);
|
||||
progressBar.setProgress(5);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -214,104 +206,80 @@ public class CreateNewWalletActivity extends AppCompatActivity implements NfcAda
|
|||
|
||||
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();
|
||||
}
|
||||
progressBar.post(() -> {
|
||||
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.post(() -> {
|
||||
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();
|
||||
}
|
||||
progressBar.postDelayed(() -> {
|
||||
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.post(() -> {
|
||||
if (cardProtocol.getError() instanceof CardProtocol.TangemException_ExtendedLengthNotSupported) {
|
||||
if (!NoExtendedLengthSupportDialog.allreadyShowed) {
|
||||
new NoExtendedLengthSupportDialog().show(getFragmentManager(), NoExtendedLengthSupportDialog.TAG);
|
||||
}
|
||||
progressBar.setProgress(100);
|
||||
progressBar.setProgressTintList(ColorStateList.valueOf(Color.RED));
|
||||
} else {
|
||||
Toast.makeText(getBaseContext(), R.string.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();
|
||||
}
|
||||
progressBar.postDelayed(() -> {
|
||||
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);
|
||||
}
|
||||
});
|
||||
progressBar.post(() -> 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();
|
||||
}
|
||||
progressBar.postDelayed(() -> {
|
||||
try {
|
||||
progressBar.setProgress(0);
|
||||
progressBar.setProgressTintList(ColorStateList.valueOf(Color.DKGRAY));
|
||||
progressBar.setVisibility(View.INVISIBLE);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}, 500);
|
||||
}
|
||||
|
|
@ -330,5 +298,5 @@ public class CreateNewWalletActivity extends AppCompatActivity implements NfcAda
|
|||
public void OnReadAfterRequest() {
|
||||
WaitSecurityDelayDialog.onReadAfterRequest(this);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
package com.tangem.presentation.activity;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.nfc.NfcAdapter;
|
||||
|
|
@ -11,7 +12,6 @@ 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;
|
||||
|
|
@ -21,26 +21,25 @@ import com.tangem.domain.cardReader.NfcManager;
|
|||
import com.tangem.domain.wallet.Blockchain;
|
||||
import com.tangem.domain.wallet.CoinEngine;
|
||||
import com.tangem.domain.wallet.CoinEngineFactory;
|
||||
import com.tangem.util.FormatUtil;
|
||||
import com.tangem.domain.wallet.TangemCard;
|
||||
import com.tangem.util.FormatUtil;
|
||||
import com.tangem.wallet.R;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Objects;
|
||||
|
||||
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;
|
||||
private Button btnVerify;
|
||||
private EditText etWallet;
|
||||
private EditText etAmount;
|
||||
private TextView tvCurrency;
|
||||
private TextView tvCardId, tvBalance, tvBalanceEquivalent, tvAmountEquivalent;
|
||||
private ImageView ivCamera;
|
||||
private TextView tvAmountEquivalent;
|
||||
boolean use_mCurrency;
|
||||
private TangemCard mCard;
|
||||
private NfcManager mNfcManager;
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
|
@ -53,14 +52,14 @@ public class PreparePaymentActivity extends AppCompatActivity implements NfcAdap
|
|||
mCard = new TangemCard(getIntent().getStringExtra("UID"));
|
||||
mCard.LoadFromBundle(getIntent().getExtras().getBundle("Card"));
|
||||
|
||||
btnVerify = findViewById(R.id.btnVerify);
|
||||
Button btnVerify = findViewById(R.id.btnVerify);
|
||||
etWallet = findViewById(R.id.etWallet);
|
||||
etAmount = findViewById(R.id.etAmount);
|
||||
ivCamera = findViewById(R.id.ivCamera);
|
||||
tvCurrency = findViewById(R.id.tvCurrency);
|
||||
tvCardId = findViewById(R.id.tvCardID);
|
||||
tvBalance = findViewById(R.id.tvBalance);
|
||||
tvBalanceEquivalent = findViewById(R.id.tvBalanceEquivalent);
|
||||
ImageView ivCamera = findViewById(R.id.ivCamera);
|
||||
TextView tvCurrency = findViewById(R.id.tvCurrency);
|
||||
TextView tvCardId = findViewById(R.id.tvCardID);
|
||||
TextView tvBalance = findViewById(R.id.tvBalance);
|
||||
TextView tvBalanceEquivalent = findViewById(R.id.tvBalanceEquivalent);
|
||||
tvAmountEquivalent = findViewById(R.id.tvAmountEquivalent);
|
||||
|
||||
tvCardId.setText(mCard.getCIDDescription());
|
||||
|
|
@ -135,49 +134,60 @@ public class PreparePaymentActivity extends AppCompatActivity implements NfcAdap
|
|||
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;
|
||||
btnVerify.setOnClickListener(v -> {
|
||||
String strAmount;
|
||||
strAmount = etAmount.getText().toString();
|
||||
CoinEngine engine1 = CoinEngineFactory.Create(mCard.getBlockchain());
|
||||
try {
|
||||
if (!Objects.requireNonNull(engine1).CheckAmount(mCard, etAmount.getText().toString())) {
|
||||
etAmount.setError(getString(R.string.not_enough_funds_on_your_card));
|
||||
}
|
||||
|
||||
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);
|
||||
} catch (Exception e) {
|
||||
etAmount.setError(getString(R.string.unknown_amount_format));
|
||||
return;
|
||||
}
|
||||
|
||||
boolean checkAddress = engine1.ValdateAddress(etWallet.getText().toString(), mCard);
|
||||
if (!checkAddress) {
|
||||
etWallet.setError(getString(R.string.incorrect_destination_wallet_address));
|
||||
return;
|
||||
}
|
||||
|
||||
if (etWallet.getText().toString().equals(mCard.getWallet())) {
|
||||
etWallet.setError(getString(R.string.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);
|
||||
}
|
||||
ivCamera.setOnClickListener(v -> {
|
||||
Intent intent = new Intent(getBaseContext(), QRScanActivity.class);
|
||||
startActivityForResult(intent, REQUEST_CODE_SCAN_QR);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
mNfcManager.onResume();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
mNfcManager.onPause();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStop() {
|
||||
super.onStop();
|
||||
mNfcManager.onStop();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -207,21 +217,4 @@ public class PreparePaymentActivity extends AppCompatActivity implements NfcAdap
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
mNfcManager.onResume();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
mNfcManager.onPause();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStop() {
|
||||
super.onStop();
|
||||
mNfcManager.onStop();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
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) {
|
||||
//
|
||||
// }
|
||||
// });
|
||||
}
|
||||
}
|
||||
|
|
@ -1,232 +0,0 @@
|
|||
package com.tangem.presentation.activity
|
||||
|
||||
import android.annotation.TargetApi
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.res.Configuration
|
||||
import android.media.RingtoneManager
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.preference.*
|
||||
import android.text.TextUtils
|
||||
import android.view.MenuItem
|
||||
import com.tangem.wallet.R
|
||||
|
||||
/**
|
||||
* A [PreferenceActivity] that presents a set of application settings. On
|
||||
* handset devices, settings are presented as a single list. On tablets,
|
||||
* settings are split by category, with category headers shown to the left of
|
||||
* the list of settings.
|
||||
*
|
||||
* See [Android Design: Settings](http://developer.android.com/design/patterns/settings.html)
|
||||
* for design guidelines and the [Settings API Guide](http://developer.android.com/guide/topics/ui/settings.html)
|
||||
* for more information on developing a Settings UI.
|
||||
*/
|
||||
class SettingsFeatureActivity : AppCompatPreferenceActivity() {
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setupActionBar()
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up the [android.app.ActionBar], if the API is available.
|
||||
*/
|
||||
private fun setupActionBar() {
|
||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
override fun onIsMultiPane(): Boolean {
|
||||
return isXLargeTablet(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
|
||||
override fun onBuildHeaders(target: List<PreferenceActivity.Header>) {
|
||||
loadHeadersFromResource(R.xml.pref_headers, target)
|
||||
}
|
||||
|
||||
/**
|
||||
* This method stops fragment injection in malicious applications.
|
||||
* Make sure to deny any unknown fragments here.
|
||||
*/
|
||||
override fun isValidFragment(fragmentName: String): Boolean {
|
||||
return PreferenceFragment::class.java.name == fragmentName
|
||||
|| GeneralPreferenceFragment::class.java.name == fragmentName
|
||||
|| DataSyncPreferenceFragment::class.java.name == fragmentName
|
||||
|| NotificationPreferenceFragment::class.java.name == fragmentName
|
||||
}
|
||||
|
||||
/**
|
||||
* This fragment shows general preferences only. It is used when the
|
||||
* activity is showing a two-pane settings UI.
|
||||
*/
|
||||
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
|
||||
class GeneralPreferenceFragment : PreferenceFragment() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
addPreferencesFromResource(R.xml.pref_general)
|
||||
setHasOptionsMenu(true)
|
||||
|
||||
// Bind the summaries of EditText/List/Dialog/Ringtone preferences
|
||||
// to their values. When their values change, their summaries are
|
||||
// updated to reflect the new value, per the Android Design
|
||||
// guidelines.
|
||||
bindPreferenceSummaryToValue(findPreference("example_text"))
|
||||
bindPreferenceSummaryToValue(findPreference("example_list"))
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
val id = item.itemId
|
||||
if (id == android.R.id.home) {
|
||||
startActivity(Intent(activity, SettingsFeatureActivity::class.java))
|
||||
return true
|
||||
}
|
||||
return super.onOptionsItemSelected(item)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This fragment shows notification preferences only. It is used when the
|
||||
* activity is showing a two-pane settings UI.
|
||||
*/
|
||||
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
|
||||
class NotificationPreferenceFragment : PreferenceFragment() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
addPreferencesFromResource(R.xml.pref_notification)
|
||||
setHasOptionsMenu(true)
|
||||
|
||||
// Bind the summaries of EditText/List/Dialog/Ringtone preferences
|
||||
// to their values. When their values change, their summaries are
|
||||
// updated to reflect the new value, per the Android Design
|
||||
// guidelines.
|
||||
bindPreferenceSummaryToValue(findPreference("notifications_new_message_ringtone"))
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
val id = item.itemId
|
||||
if (id == android.R.id.home) {
|
||||
startActivity(Intent(activity, SettingsFeatureActivity::class.java))
|
||||
return true
|
||||
}
|
||||
return super.onOptionsItemSelected(item)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This fragment shows data and sync preferences only. It is used when the
|
||||
* activity is showing a two-pane settings UI.
|
||||
*/
|
||||
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
|
||||
class DataSyncPreferenceFragment : PreferenceFragment() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
addPreferencesFromResource(R.xml.pref_data_sync)
|
||||
setHasOptionsMenu(true)
|
||||
|
||||
// Bind the summaries of EditText/List/Dialog/Ringtone preferences
|
||||
// to their values. When their values change, their summaries are
|
||||
// updated to reflect the new value, per the Android Design
|
||||
// guidelines.
|
||||
bindPreferenceSummaryToValue(findPreference("sync_frequency"))
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
val id = item.itemId
|
||||
if (id == android.R.id.home) {
|
||||
startActivity(Intent(activity, SettingsFeatureActivity::class.java))
|
||||
return true
|
||||
}
|
||||
return super.onOptionsItemSelected(item)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
/**
|
||||
* A preference value change listener that updates the preference's summary
|
||||
* to reflect its new value.
|
||||
*/
|
||||
private val sBindPreferenceSummaryToValueListener = Preference.OnPreferenceChangeListener { preference, value ->
|
||||
val stringValue = value.toString()
|
||||
|
||||
if (preference is ListPreference) {
|
||||
// For list preferences, look up the correct display value in
|
||||
// the preference's 'entries' list.
|
||||
val listPreference = preference
|
||||
val index = listPreference.findIndexOfValue(stringValue)
|
||||
|
||||
// Set the summary to reflect the new value.
|
||||
preference.setSummary(
|
||||
if (index >= 0)
|
||||
listPreference.entries[index]
|
||||
else
|
||||
null)
|
||||
|
||||
} else if (preference is RingtonePreference) {
|
||||
// For ringtone preferences, look up the correct display value
|
||||
// using RingtoneManager.
|
||||
if (TextUtils.isEmpty(stringValue)) {
|
||||
// Empty values correspond to 'silent' (no ringtone).
|
||||
preference.setSummary(R.string.pref_ringtone_silent)
|
||||
|
||||
} else {
|
||||
val ringtone = RingtoneManager.getRingtone(
|
||||
preference.getContext(), Uri.parse(stringValue))
|
||||
|
||||
if (ringtone == null) {
|
||||
// Clear the summary if there was a lookup error.
|
||||
preference.setSummary(null)
|
||||
} else {
|
||||
// Set the summary to reflect the new ringtone display
|
||||
// name.
|
||||
val name = ringtone.getTitle(preference.getContext())
|
||||
preference.setSummary(name)
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
// For all other preferences, set the summary to the value's
|
||||
// simple string representation.
|
||||
preference.summary = stringValue
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to determine if the device has an extra-large screen. For
|
||||
* example, 10" tablets are extra-large.
|
||||
*/
|
||||
private fun isXLargeTablet(context: Context): Boolean {
|
||||
return context.resources.configuration.screenLayout and Configuration.SCREENLAYOUT_SIZE_MASK >= Configuration.SCREENLAYOUT_SIZE_XLARGE
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds a preference's summary to its value. More specifically, when the
|
||||
* preference's value is changed, its summary (line of text below the
|
||||
* preference title) is updated to reflect the value. The summary is also
|
||||
* immediately updated upon calling this method. The exact display format is
|
||||
* dependent on the type of preference.
|
||||
|
||||
* @see .sBindPreferenceSummaryToValueListener
|
||||
*/
|
||||
private fun bindPreferenceSummaryToValue(preference: Preference) {
|
||||
// Set the listener to watch for value changes.
|
||||
preference.onPreferenceChangeListener = sBindPreferenceSummaryToValueListener
|
||||
|
||||
// Trigger the listener immediately with the preference's
|
||||
// current value.
|
||||
sBindPreferenceSummaryToValueListener.onPreferenceChange(preference,
|
||||
PreferenceManager
|
||||
.getDefaultSharedPreferences(preference.context)
|
||||
.getString(preference.key, ""))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,87 +0,0 @@
|
|||
package com.tangem.presentation.adapter;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.text.Html;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.BaseAdapter;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.tangem.domain.wallet.TangemCard;
|
||||
import com.tangem.wallet.R;
|
||||
|
||||
import static android.text.Html.FROM_HTML_MODE_COMPACT;
|
||||
|
||||
/**
|
||||
* Created by dvol on 17.07.2017.
|
||||
*/
|
||||
|
||||
public class CardUnspentListAdapter extends BaseAdapter {
|
||||
private LayoutInflater mLayoutInflater;
|
||||
private Context mContext;
|
||||
private TangemCard mCard;
|
||||
|
||||
public CardUnspentListAdapter(LayoutInflater layoutInflater, TangemCard card) {
|
||||
mLayoutInflater = layoutInflater;
|
||||
mContext = layoutInflater.getContext();
|
||||
mCard = card;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
if (mCard != null && mCard.getUnspentTransactions() != null)
|
||||
return mCard.getUnspentTransactions().size();
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getItem(int i) {
|
||||
if (mCard != null && mCard.getUnspentTransactions() != null)
|
||||
return mCard.getUnspentTransactions().get(i);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getItemId(int i) {
|
||||
if (mCard != null && mCard.getUnspentTransactions() != null)
|
||||
return mCard.getUnspentTransactions().get(i).txID.hashCode();
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(int position, View convertView, ViewGroup viewGroup) {
|
||||
if (convertView == null) {
|
||||
convertView = mLayoutInflater.inflate(R.layout.item_list_card_unspent, viewGroup,
|
||||
false);
|
||||
}
|
||||
TextView tvItem = (TextView) convertView.findViewById(R.id.tvItem);
|
||||
|
||||
TangemCard.UnspentTransaction unspentTransaction = (TangemCard.UnspentTransaction) getItem(position);
|
||||
|
||||
String html = String.format("<b>%d mBTC</b><br>%s", unspentTransaction.Amount, unspentTransaction.txID);
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
tvItem.setText(Html.fromHtml(html, FROM_HTML_MODE_COMPACT));
|
||||
} else {
|
||||
tvItem.setText(Html.fromHtml(html.toString()));
|
||||
}
|
||||
return convertView;
|
||||
}
|
||||
|
||||
public void Clear() {
|
||||
mCard.getUnspentTransactions().clear();
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
public void UpdateUnspent(String tx_hash, int value, int height) {
|
||||
TangemCard.UnspentTransaction newUT = new TangemCard.UnspentTransaction();
|
||||
newUT.txID = tx_hash;
|
||||
newUT.Amount = value;
|
||||
newUT.Height = height;
|
||||
mCard.getUnspentTransactions().add(newUT);
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,152 +0,0 @@
|
|||
package com.tangem.presentation.fragment;
|
||||
|
||||
import android.content.ClipData;
|
||||
import android.content.ClipboardManager;
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Color;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.google.zxing.BarcodeFormat;
|
||||
import com.google.zxing.EncodeHintType;
|
||||
import com.google.zxing.WriterException;
|
||||
import com.google.zxing.common.BitMatrix;
|
||||
import com.google.zxing.qrcode.QRCodeWriter;
|
||||
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
|
||||
import com.tangem.wallet.R;
|
||||
import com.tangem.domain.wallet.TangemCard;
|
||||
|
||||
import java.util.Hashtable;
|
||||
|
||||
import static android.content.Context.CLIPBOARD_SERVICE;
|
||||
|
||||
/**
|
||||
* A simple {@link Fragment} subclass.
|
||||
* Activities that contain this fragment must implement the
|
||||
* {@link OnFragmentInteractionListener} interface
|
||||
* to handle interaction events.
|
||||
* Use the {@link WalletInfo#newInstance} factory method to
|
||||
* create an instance of this fragment.
|
||||
*/
|
||||
public class WalletInfo extends Fragment {
|
||||
|
||||
// TODO: Rename and change types of parameters
|
||||
private TangemCard mCard;
|
||||
|
||||
private OnFragmentInteractionListener mListener;
|
||||
|
||||
public WalletInfo() {
|
||||
// Required empty public constructor
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this factory method to create a new instance of
|
||||
* this fragment using the provided parameters.
|
||||
*
|
||||
* @return A new instance of fragment WalletInfo.
|
||||
*/
|
||||
// TODO: Rename and change types and number of parameters
|
||||
public static WalletInfo newInstance(TangemCard card) {
|
||||
WalletInfo fragment = new WalletInfo();
|
||||
Bundle args = new Bundle();
|
||||
args.putString("UID",card.getUID());
|
||||
card.SaveToBundle(args);
|
||||
fragment.setArguments(args);
|
||||
return fragment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
if (getArguments() != null) {
|
||||
mCard = new TangemCard(getArguments().getString("UID"));
|
||||
mCard.LoadFromBundle(getArguments());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
// Inflate the layout for this fragment
|
||||
View result=inflater.inflate(R.layout.fr_wallet_info, container, false);
|
||||
|
||||
ImageView mImage= (ImageView)result.findViewById(R.id.qrWallet);
|
||||
try {
|
||||
mImage.setImageBitmap(generateQrCode(mCard.getWallet()));
|
||||
} catch (WriterException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
TextView mText=(TextView)result.findViewById(R.id.strWallet);
|
||||
mText.setText(mCard.getWallet());
|
||||
|
||||
mText.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
TextView mText = (TextView) view;
|
||||
ClipboardManager clipboard = (ClipboardManager)getActivity().getSystemService(CLIPBOARD_SERVICE);
|
||||
clipboard.setPrimaryClip(ClipData.newPlainText(mText.getText(), mText.getText()));
|
||||
Toast.makeText(getContext(),"Copied to clipboard",Toast.LENGTH_LONG).show();
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
public static Bitmap generateQrCode(String myCodeText) throws WriterException {
|
||||
Hashtable<EncodeHintType, ErrorCorrectionLevel> hintMap = new Hashtable<EncodeHintType, ErrorCorrectionLevel>();
|
||||
hintMap.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H); // H = 30% damage
|
||||
|
||||
QRCodeWriter qrCodeWriter = new QRCodeWriter();
|
||||
|
||||
int size = 256;
|
||||
|
||||
BitMatrix bitMatrix = qrCodeWriter.encode(myCodeText, BarcodeFormat.QR_CODE, size, size, hintMap);
|
||||
int width = bitMatrix.getWidth();
|
||||
Bitmap bmp = Bitmap.createBitmap(width, width, Bitmap.Config.RGB_565);
|
||||
for (int x = 0; x < width; x++) {
|
||||
for (int y = 0; y < width; y++) {
|
||||
bmp.setPixel(y, x, bitMatrix.get(x, y) ? Color.BLACK : Color.WHITE);
|
||||
}
|
||||
}
|
||||
return bmp;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onAttach(Context context) {
|
||||
super.onAttach(context);
|
||||
if (context instanceof OnFragmentInteractionListener) {
|
||||
mListener = (OnFragmentInteractionListener) context;
|
||||
} else {
|
||||
throw new RuntimeException(context.toString()
|
||||
+ " must implement OnFragmentInteractionListener");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDetach() {
|
||||
super.onDetach();
|
||||
mListener = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* This interface must be implemented by activities that contain this
|
||||
* fragment to allow an interaction in this fragment to be communicated
|
||||
* to the activity and potentially other fragments contained in that
|
||||
* activity.
|
||||
* <p>
|
||||
* See the Android Training lesson <a href=
|
||||
* "http://developer.android.com/training/basics/fragments/communicating.html"
|
||||
* >Communicating with Other Fragments</a> for more information.
|
||||
*/
|
||||
public interface OnFragmentInteractionListener {
|
||||
// TODO: Update argument type and name
|
||||
// void onFragmentInteraction(Uri uri);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue