mirror of
https://github.com/m2049r/xmrwallet
synced 2025-09-08 20:40:51 +02:00
Compare commits
18 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
b978396a38 | ||
![]() |
c6aa04e986 | ||
![]() |
6c17b8bd87 | ||
![]() |
cac32f660c | ||
![]() |
8e70004bf2 | ||
![]() |
c3a466c392 | ||
![]() |
e076c19e3e | ||
![]() |
35b717756d | ||
![]() |
c14486306e | ||
![]() |
c2ef25c377 | ||
![]() |
b7164ef200 | ||
![]() |
f94a366d51 | ||
![]() |
286a04b5ef | ||
![]() |
1209295a8c | ||
![]() |
037b019d4d | ||
![]() |
7a1d788f2a | ||
![]() |
87d9a8cd95 | ||
![]() |
f637d7f617 |
@@ -7,8 +7,8 @@ android {
|
|||||||
applicationId "com.m2049r.xmrwallet"
|
applicationId "com.m2049r.xmrwallet"
|
||||||
minSdkVersion 21
|
minSdkVersion 21
|
||||||
targetSdkVersion 28
|
targetSdkVersion 28
|
||||||
versionCode 193
|
versionCode 202
|
||||||
versionName "1.12.3 'Caerbannog'"
|
versionName "1.12.12 'Caerbannog'"
|
||||||
|
|
||||||
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
||||||
externalNativeBuild {
|
externalNativeBuild {
|
||||||
|
@@ -79,6 +79,23 @@ public class GenerateFragment extends Fragment {
|
|||||||
|
|
||||||
private String type = null;
|
private String type = null;
|
||||||
|
|
||||||
|
private void clearErrorOnTextEntry(final TextInputLayout textInputLayout) {
|
||||||
|
textInputLayout.getEditText().addTextChangedListener(new TextWatcher() {
|
||||||
|
@Override
|
||||||
|
public void afterTextChanged(Editable editable) {
|
||||||
|
textInputLayout.setError(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||||
Bundle savedInstanceState) {
|
Bundle savedInstanceState) {
|
||||||
@@ -110,6 +127,23 @@ public class GenerateFragment extends Fragment {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
clearErrorOnTextEntry(etWalletName);
|
||||||
|
|
||||||
|
etWalletPassword.getEditText().addTextChangedListener(new TextWatcher() {
|
||||||
|
@Override
|
||||||
|
public void afterTextChanged(Editable editable) {
|
||||||
|
checkPassword();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
etWalletMnemonic.getEditText().setOnFocusChangeListener(new View.OnFocusChangeListener() {
|
etWalletMnemonic.getEditText().setOnFocusChangeListener(new View.OnFocusChangeListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onFocusChange(View v, boolean hasFocus) {
|
public void onFocusChange(View v, boolean hasFocus) {
|
||||||
@@ -118,6 +152,8 @@ public class GenerateFragment extends Fragment {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
clearErrorOnTextEntry(etWalletMnemonic);
|
||||||
|
|
||||||
etWalletAddress.getEditText().setOnFocusChangeListener(new View.OnFocusChangeListener() {
|
etWalletAddress.getEditText().setOnFocusChangeListener(new View.OnFocusChangeListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onFocusChange(View v, boolean hasFocus) {
|
public void onFocusChange(View v, boolean hasFocus) {
|
||||||
@@ -126,6 +162,8 @@ public class GenerateFragment extends Fragment {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
clearErrorOnTextEntry(etWalletAddress);
|
||||||
|
|
||||||
etWalletViewKey.getEditText().setOnFocusChangeListener(new View.OnFocusChangeListener() {
|
etWalletViewKey.getEditText().setOnFocusChangeListener(new View.OnFocusChangeListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onFocusChange(View v, boolean hasFocus) {
|
public void onFocusChange(View v, boolean hasFocus) {
|
||||||
@@ -134,6 +172,8 @@ public class GenerateFragment extends Fragment {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
clearErrorOnTextEntry(etWalletViewKey);
|
||||||
|
|
||||||
etWalletSpendKey.getEditText().setOnFocusChangeListener(new View.OnFocusChangeListener() {
|
etWalletSpendKey.getEditText().setOnFocusChangeListener(new View.OnFocusChangeListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onFocusChange(View v, boolean hasFocus) {
|
public void onFocusChange(View v, boolean hasFocus) {
|
||||||
@@ -142,6 +182,7 @@ public class GenerateFragment extends Fragment {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
clearErrorOnTextEntry(etWalletSpendKey);
|
||||||
|
|
||||||
Helper.showKeyboard(getActivity());
|
Helper.showKeyboard(getActivity());
|
||||||
|
|
||||||
@@ -310,21 +351,6 @@ public class GenerateFragment extends Fragment {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
etWalletPassword.getEditText().addTextChangedListener(new TextWatcher() {
|
|
||||||
@Override
|
|
||||||
public void afterTextChanged(Editable editable) {
|
|
||||||
checkPassword();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
etWalletName.requestFocus();
|
etWalletName.requestFocus();
|
||||||
initZxcvbn();
|
initZxcvbn();
|
||||||
|
|
||||||
@@ -412,7 +438,7 @@ public class GenerateFragment extends Fragment {
|
|||||||
height = RestoreHeight.getInstance().getHeight(parser.parse(restoreHeight));
|
height = RestoreHeight.getInstance().getHeight(parser.parse(restoreHeight));
|
||||||
} catch (ParseException ex) {
|
} catch (ParseException ex) {
|
||||||
}
|
}
|
||||||
if (height <= 0)
|
if ((height <= 0) && (restoreHeight.length() == 8))
|
||||||
try {
|
try {
|
||||||
// is it a date without dashes?
|
// is it a date without dashes?
|
||||||
SimpleDateFormat parser = new SimpleDateFormat("yyyyMMdd");
|
SimpleDateFormat parser = new SimpleDateFormat("yyyyMMdd");
|
||||||
|
@@ -50,9 +50,8 @@ import com.m2049r.xmrwallet.widget.Toolbar;
|
|||||||
|
|
||||||
import java.text.NumberFormat;
|
import java.text.NumberFormat;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Date;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
import timber.log.Timber;
|
import timber.log.Timber;
|
||||||
|
|
||||||
@@ -114,9 +113,12 @@ public class WalletFragment extends Fragment
|
|||||||
ivSynced = view.findViewById(R.id.ivSynced);
|
ivSynced = view.findViewById(R.id.ivSynced);
|
||||||
|
|
||||||
sCurrency = view.findViewById(R.id.sCurrency);
|
sCurrency = view.findViewById(R.id.sCurrency);
|
||||||
ArrayAdapter currencyAdapter = ArrayAdapter.createFromResource(getContext(), R.array.currency, R.layout.item_spinner_balance);
|
List<String> currencies = new ArrayList<>();
|
||||||
currencyAdapter.setDropDownViewResource(R.layout.item_spinner_dropdown_item);
|
currencies.add(Helper.BASE_CRYPTO);
|
||||||
sCurrency.setAdapter(currencyAdapter);
|
currencies.addAll(Arrays.asList(getResources().getStringArray(R.array.currency)));
|
||||||
|
ArrayAdapter<String> spinnerAdapter = new ArrayAdapter<>(getContext(), R.layout.item_spinner_balance, currencies);
|
||||||
|
spinnerAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
|
||||||
|
sCurrency.setAdapter(spinnerAdapter);
|
||||||
|
|
||||||
bSend = view.findViewById(R.id.bSend);
|
bSend = view.findViewById(R.id.bSend);
|
||||||
bReceive = view.findViewById(R.id.bReceive);
|
bReceive = view.findViewById(R.id.bReceive);
|
||||||
@@ -220,7 +222,7 @@ public class WalletFragment extends Fragment
|
|||||||
// at this point selection is XMR in case of error
|
// at this point selection is XMR in case of error
|
||||||
String displayB;
|
String displayB;
|
||||||
double amountA = Helper.getDecimalAmount(unlockedBalance).doubleValue();
|
double amountA = Helper.getDecimalAmount(unlockedBalance).doubleValue();
|
||||||
if (!Helper.CRYPTO.equals(balanceCurrency)) { // not XMR
|
if (!Helper.BASE_CRYPTO.equals(balanceCurrency)) { // not XMR
|
||||||
double amountB = amountA * balanceRate;
|
double amountB = amountA * balanceRate;
|
||||||
displayB = Helper.getFormattedAmount(amountB, false);
|
displayB = Helper.getFormattedAmount(amountB, false);
|
||||||
} else { // XMR
|
} else { // XMR
|
||||||
@@ -229,7 +231,7 @@ public class WalletFragment extends Fragment
|
|||||||
showBalance(displayB);
|
showBalance(displayB);
|
||||||
}
|
}
|
||||||
|
|
||||||
String balanceCurrency = Helper.CRYPTO;
|
String balanceCurrency = Helper.BASE_CRYPTO;
|
||||||
double balanceRate = 1.0;
|
double balanceRate = 1.0;
|
||||||
|
|
||||||
private final ExchangeApi exchangeApi = Helper.getExchangeApi();
|
private final ExchangeApi exchangeApi = Helper.getExchangeApi();
|
||||||
@@ -245,7 +247,7 @@ public class WalletFragment extends Fragment
|
|||||||
Timber.d(currency);
|
Timber.d(currency);
|
||||||
if (!currency.equals(balanceCurrency) || (balanceRate <= 0)) {
|
if (!currency.equals(balanceCurrency) || (balanceRate <= 0)) {
|
||||||
showExchanging();
|
showExchanging();
|
||||||
exchangeApi.queryExchangeRate(Helper.CRYPTO, currency,
|
exchangeApi.queryExchangeRate(Helper.BASE_CRYPTO, currency,
|
||||||
new ExchangeCallback() {
|
new ExchangeCallback() {
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(final ExchangeRate exchangeRate) {
|
public void onSuccess(final ExchangeRate exchangeRate) {
|
||||||
@@ -301,10 +303,10 @@ public class WalletFragment extends Fragment
|
|||||||
|
|
||||||
public void exchange(final ExchangeRate exchangeRate) {
|
public void exchange(final ExchangeRate exchangeRate) {
|
||||||
hideExchanging();
|
hideExchanging();
|
||||||
if (!Helper.CRYPTO.equals(exchangeRate.getBaseCurrency())) {
|
if (!Helper.BASE_CRYPTO.equals(exchangeRate.getBaseCurrency())) {
|
||||||
Timber.e("Not XMR");
|
Timber.e("Not XMR");
|
||||||
sCurrency.setSelection(0, true);
|
sCurrency.setSelection(0, true);
|
||||||
balanceCurrency = Helper.CRYPTO;
|
balanceCurrency = Helper.BASE_CRYPTO;
|
||||||
balanceRate = 1.0;
|
balanceRate = 1.0;
|
||||||
} else {
|
} else {
|
||||||
int spinnerPosition = ((ArrayAdapter) sCurrency.getAdapter()).getPosition(exchangeRate.getQuoteCurrency());
|
int spinnerPosition = ((ArrayAdapter) sCurrency.getAdapter()).getPosition(exchangeRate.getQuoteCurrency());
|
||||||
|
@@ -59,42 +59,36 @@ public class BarcodeData {
|
|||||||
final public Asset asset;
|
final public Asset asset;
|
||||||
final public String address;
|
final public String address;
|
||||||
final public String addressName;
|
final public String addressName;
|
||||||
final public String paymentId;
|
|
||||||
final public String amount;
|
final public String amount;
|
||||||
final public String description;
|
final public String description;
|
||||||
final public Security security;
|
final public Security security;
|
||||||
final public String bip70;
|
final public String bip70;
|
||||||
|
|
||||||
public BarcodeData(Asset asset, String address) {
|
public BarcodeData(Asset asset, String address) {
|
||||||
this(asset, address, null, null, null, null, Security.NORMAL);
|
this(asset, address, null, null, null, Security.NORMAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
public BarcodeData(Asset asset, String address, String amount) {
|
public BarcodeData(Asset asset, String address, String amount) {
|
||||||
this(asset, address, null, null, null, amount, Security.NORMAL);
|
this(asset, address, null, null, amount, Security.NORMAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
public BarcodeData(Asset asset, String address, String amount, String description, Security security) {
|
public BarcodeData(Asset asset, String address, String amount, String description, Security security) {
|
||||||
this(asset, address, null, null, description, amount, security);
|
this(asset, address, null, description, amount, security);
|
||||||
}
|
|
||||||
|
|
||||||
public BarcodeData(Asset asset, String address, String paymentId, String amount) {
|
|
||||||
this(asset, address, null, paymentId, null, amount, Security.NORMAL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public BarcodeData(Asset asset, String address, String paymentId, String description, String amount) {
|
public BarcodeData(Asset asset, String address, String paymentId, String description, String amount) {
|
||||||
this(asset, address, null, paymentId, description, amount, Security.NORMAL);
|
this(asset, address, null, description, amount, Security.NORMAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
public BarcodeData(Asset asset, String address, String addressName, String paymentId, String description, String amount, Security security) {
|
public BarcodeData(Asset asset, String address, String addressName, String description, String amount, Security security) {
|
||||||
this(asset, address, addressName, null, paymentId, description, amount, security);
|
this(asset, address, addressName, null, description, amount, security);
|
||||||
}
|
}
|
||||||
|
|
||||||
public BarcodeData(Asset asset, String address, String addressName, String bip70, String paymentId, String description, String amount, Security security) {
|
public BarcodeData(Asset asset, String address, String addressName, String bip70, String description, String amount, Security security) {
|
||||||
this.asset = asset;
|
this.asset = asset;
|
||||||
this.address = address;
|
this.address = address;
|
||||||
this.bip70 = bip70;
|
this.bip70 = bip70;
|
||||||
this.addressName = addressName;
|
this.addressName = addressName;
|
||||||
this.paymentId = paymentId;
|
|
||||||
this.description = description;
|
this.description = description;
|
||||||
this.amount = amount;
|
this.amount = amount;
|
||||||
this.security = security;
|
this.security = security;
|
||||||
@@ -110,11 +104,6 @@ public class BarcodeData {
|
|||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
sb.append(BarcodeData.XMR_SCHEME).append(address);
|
sb.append(BarcodeData.XMR_SCHEME).append(address);
|
||||||
boolean first = true;
|
boolean first = true;
|
||||||
if ((paymentId != null) && !paymentId.isEmpty()) {
|
|
||||||
sb.append("?");
|
|
||||||
first = false;
|
|
||||||
sb.append(BarcodeData.XMR_PAYMENTID).append('=').append(paymentId);
|
|
||||||
}
|
|
||||||
if ((description != null) && !description.isEmpty()) {
|
if ((description != null) && !description.isEmpty()) {
|
||||||
sb.append(first ? "?" : "&");
|
sb.append(first ? "?" : "&");
|
||||||
first = false;
|
first = false;
|
||||||
@@ -185,8 +174,11 @@ public class BarcodeData {
|
|||||||
String address = monero.getPath();
|
String address = monero.getPath();
|
||||||
|
|
||||||
String paymentId = parms.get(XMR_PAYMENTID);
|
String paymentId = parms.get(XMR_PAYMENTID);
|
||||||
// deal with empty payment_id created by non-spec-conforming apps
|
// no support for payment ids!
|
||||||
if ((paymentId != null) && paymentId.isEmpty()) paymentId = null;
|
if (paymentId != null) {
|
||||||
|
Timber.e("no support for payment ids!");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
String description = parms.get(XMR_DESCRIPTION);
|
String description = parms.get(XMR_DESCRIPTION);
|
||||||
String amount = parms.get(XMR_AMOUNT);
|
String amount = parms.get(XMR_AMOUNT);
|
||||||
@@ -198,10 +190,6 @@ public class BarcodeData {
|
|||||||
return null; // we have an amount but its not a number!
|
return null; // we have an amount but its not a number!
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ((paymentId != null) && !Wallet.isPaymentIdValid(paymentId)) {
|
|
||||||
Timber.d("paymentId invalid");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!Wallet.isAddressValid(address)) {
|
if (!Wallet.isAddressValid(address)) {
|
||||||
Timber.d("address invalid");
|
Timber.d("address invalid");
|
||||||
@@ -267,7 +255,7 @@ public class BarcodeData {
|
|||||||
Timber.d("[%s] is not http url", bip70);
|
Timber.d("[%s] is not http url", bip70);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return new BarcodeData(BarcodeData.Asset.BTC, null, null, bip70, null, description, null, Security.NORMAL);
|
return new BarcodeData(BarcodeData.Asset.BTC, null, null, bip70, description, null, Security.NORMAL);
|
||||||
}
|
}
|
||||||
if (!BitcoinAddressValidator.validate(address)) {
|
if (!BitcoinAddressValidator.validate(address)) {
|
||||||
Timber.d("BTC address (%s) invalid", address);
|
Timber.d("BTC address (%s) invalid", address);
|
||||||
|
@@ -29,19 +29,16 @@ public class TxData implements Parcelable {
|
|||||||
|
|
||||||
public TxData(TxData txData) {
|
public TxData(TxData txData) {
|
||||||
this.dstAddr = txData.dstAddr;
|
this.dstAddr = txData.dstAddr;
|
||||||
this.paymentId = txData.paymentId;
|
|
||||||
this.amount = txData.amount;
|
this.amount = txData.amount;
|
||||||
this.mixin = txData.mixin;
|
this.mixin = txData.mixin;
|
||||||
this.priority = txData.priority;
|
this.priority = txData.priority;
|
||||||
}
|
}
|
||||||
|
|
||||||
public TxData(String dstAddr,
|
public TxData(String dstAddr,
|
||||||
String paymentId,
|
|
||||||
long amount,
|
long amount,
|
||||||
int mixin,
|
int mixin,
|
||||||
PendingTransaction.Priority priority) {
|
PendingTransaction.Priority priority) {
|
||||||
this.dstAddr = dstAddr;
|
this.dstAddr = dstAddr;
|
||||||
this.paymentId = paymentId;
|
|
||||||
this.amount = amount;
|
this.amount = amount;
|
||||||
this.mixin = mixin;
|
this.mixin = mixin;
|
||||||
this.priority = priority;
|
this.priority = priority;
|
||||||
@@ -51,10 +48,6 @@ public class TxData implements Parcelable {
|
|||||||
return dstAddr;
|
return dstAddr;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getPaymentId() {
|
|
||||||
return paymentId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getAmount() {
|
public long getAmount() {
|
||||||
return amount;
|
return amount;
|
||||||
}
|
}
|
||||||
@@ -71,10 +64,6 @@ public class TxData implements Parcelable {
|
|||||||
this.dstAddr = dstAddr;
|
this.dstAddr = dstAddr;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setPaymentId(String paymentId) {
|
|
||||||
this.paymentId = paymentId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setAmount(long amount) {
|
public void setAmount(long amount) {
|
||||||
this.amount = amount;
|
this.amount = amount;
|
||||||
}
|
}
|
||||||
@@ -96,7 +85,6 @@ public class TxData implements Parcelable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private String dstAddr;
|
private String dstAddr;
|
||||||
private String paymentId;
|
|
||||||
private long amount;
|
private long amount;
|
||||||
private int mixin;
|
private int mixin;
|
||||||
private PendingTransaction.Priority priority;
|
private PendingTransaction.Priority priority;
|
||||||
@@ -106,7 +94,6 @@ public class TxData implements Parcelable {
|
|||||||
@Override
|
@Override
|
||||||
public void writeToParcel(Parcel out, int flags) {
|
public void writeToParcel(Parcel out, int flags) {
|
||||||
out.writeString(dstAddr);
|
out.writeString(dstAddr);
|
||||||
out.writeString(paymentId);
|
|
||||||
out.writeLong(amount);
|
out.writeLong(amount);
|
||||||
out.writeInt(mixin);
|
out.writeInt(mixin);
|
||||||
out.writeInt(priority.getValue());
|
out.writeInt(priority.getValue());
|
||||||
@@ -125,7 +112,6 @@ public class TxData implements Parcelable {
|
|||||||
|
|
||||||
protected TxData(Parcel in) {
|
protected TxData(Parcel in) {
|
||||||
dstAddr = in.readString();
|
dstAddr = in.readString();
|
||||||
paymentId = in.readString();
|
|
||||||
amount = in.readLong();
|
amount = in.readLong();
|
||||||
mixin = in.readInt();
|
mixin = in.readInt();
|
||||||
priority = PendingTransaction.Priority.fromInteger(in.readInt());
|
priority = PendingTransaction.Priority.fromInteger(in.readInt());
|
||||||
@@ -142,14 +128,12 @@ public class TxData implements Parcelable {
|
|||||||
StringBuffer sb = new StringBuffer();
|
StringBuffer sb = new StringBuffer();
|
||||||
sb.append("dstAddr:");
|
sb.append("dstAddr:");
|
||||||
sb.append(dstAddr);
|
sb.append(dstAddr);
|
||||||
sb.append(",paymentId:");
|
|
||||||
sb.append(paymentId);
|
|
||||||
sb.append(",amount:");
|
sb.append(",amount:");
|
||||||
sb.append(amount);
|
sb.append(amount);
|
||||||
sb.append(",mixin:");
|
sb.append(",mixin:");
|
||||||
sb.append(mixin);
|
sb.append(mixin);
|
||||||
sb.append(",priority:");
|
sb.append(",priority:");
|
||||||
sb.append(String.valueOf(priority));
|
sb.append(priority);
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -31,7 +31,6 @@ import android.view.LayoutInflater;
|
|||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.view.inputmethod.EditorInfo;
|
import android.view.inputmethod.EditorInfo;
|
||||||
import android.widget.Button;
|
|
||||||
import android.widget.EditText;
|
import android.widget.EditText;
|
||||||
import android.widget.ImageButton;
|
import android.widget.ImageButton;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
@@ -86,12 +85,9 @@ public class SendAddressWizardFragment extends SendWizardFragment {
|
|||||||
|
|
||||||
private EditText etDummy;
|
private EditText etDummy;
|
||||||
private TextInputLayout etAddress;
|
private TextInputLayout etAddress;
|
||||||
private TextInputLayout etPaymentId;
|
|
||||||
private TextInputLayout etNotes;
|
private TextInputLayout etNotes;
|
||||||
private Button bPaymentId;
|
|
||||||
private CardView cvScan;
|
private CardView cvScan;
|
||||||
private View tvPaymentIdIntegrated;
|
private View tvPaymentIdIntegrated;
|
||||||
private View llPaymentId;
|
|
||||||
private TextView tvXmrTo;
|
private TextView tvXmrTo;
|
||||||
private View llXmrTo;
|
private View llXmrTo;
|
||||||
private ImageButton bPasteAddress;
|
private ImageButton bPasteAddress;
|
||||||
@@ -114,7 +110,6 @@ public class SendAddressWizardFragment extends SendWizardFragment {
|
|||||||
View view = inflater.inflate(R.layout.fragment_send_address, container, false);
|
View view = inflater.inflate(R.layout.fragment_send_address, container, false);
|
||||||
|
|
||||||
tvPaymentIdIntegrated = view.findViewById(R.id.tvPaymentIdIntegrated);
|
tvPaymentIdIntegrated = view.findViewById(R.id.tvPaymentIdIntegrated);
|
||||||
llPaymentId = view.findViewById(R.id.llPaymentId);
|
|
||||||
llXmrTo = view.findViewById(R.id.llXmrTo);
|
llXmrTo = view.findViewById(R.id.llXmrTo);
|
||||||
tvXmrTo = view.findViewById(R.id.tvXmrTo);
|
tvXmrTo = view.findViewById(R.id.tvXmrTo);
|
||||||
tvXmrTo.setText(Html.fromHtml(getString(R.string.info_xmrto)));
|
tvXmrTo.setText(Html.fromHtml(getString(R.string.info_xmrto)));
|
||||||
@@ -158,8 +153,6 @@ public class SendAddressWizardFragment extends SendWizardFragment {
|
|||||||
etAddress.setError(null);
|
etAddress.setError(null);
|
||||||
if (isIntegratedAddress()) {
|
if (isIntegratedAddress()) {
|
||||||
Timber.d("isIntegratedAddress");
|
Timber.d("isIntegratedAddress");
|
||||||
etPaymentId.getEditText().getText().clear();
|
|
||||||
llPaymentId.setVisibility(View.INVISIBLE);
|
|
||||||
etAddress.setError(getString(R.string.info_paymentid_integrated));
|
etAddress.setError(getString(R.string.info_paymentid_integrated));
|
||||||
tvPaymentIdIntegrated.setVisibility(View.VISIBLE);
|
tvPaymentIdIntegrated.setVisibility(View.VISIBLE);
|
||||||
llXmrTo.setVisibility(View.INVISIBLE);
|
llXmrTo.setVisibility(View.INVISIBLE);
|
||||||
@@ -169,7 +162,6 @@ public class SendAddressWizardFragment extends SendWizardFragment {
|
|||||||
setBtcMode();
|
setBtcMode();
|
||||||
} else {
|
} else {
|
||||||
Timber.d("isStandardAddress or other");
|
Timber.d("isStandardAddress or other");
|
||||||
llPaymentId.setVisibility(View.VISIBLE);
|
|
||||||
tvPaymentIdIntegrated.setVisibility(View.INVISIBLE);
|
tvPaymentIdIntegrated.setVisibility(View.INVISIBLE);
|
||||||
llXmrTo.setVisibility(View.INVISIBLE);
|
llXmrTo.setVisibility(View.INVISIBLE);
|
||||||
sendListener.setMode(SendFragment.Mode.XMR);
|
sendListener.setMode(SendFragment.Mode.XMR);
|
||||||
@@ -211,46 +203,6 @@ public class SendAddressWizardFragment extends SendWizardFragment {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
etPaymentId = view.findViewById(R.id.etPaymentId);
|
|
||||||
etPaymentId.getEditText().setRawInputType(InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
|
|
||||||
etPaymentId.getEditText().setOnEditorActionListener(new TextView.OnEditorActionListener() {
|
|
||||||
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
|
|
||||||
if ((event != null && (event.getKeyCode() == KeyEvent.KEYCODE_ENTER) && (event.getAction() == KeyEvent.ACTION_DOWN))
|
|
||||||
|| (actionId == EditorInfo.IME_ACTION_NEXT)) {
|
|
||||||
if (checkPaymentId()) {
|
|
||||||
etNotes.requestFocus();
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
etPaymentId.getEditText().addTextChangedListener(new TextWatcher() {
|
|
||||||
@Override
|
|
||||||
public void afterTextChanged(Editable editable) {
|
|
||||||
etPaymentId.setError(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
bPaymentId = view.findViewById(R.id.bPaymentId);
|
|
||||||
bPaymentId.setOnClickListener(new View.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(View v) {
|
|
||||||
final EditText et = etPaymentId.getEditText();
|
|
||||||
et.setText((Wallet.generatePaymentId()));
|
|
||||||
et.setSelection(et.getText().length());
|
|
||||||
etPaymentId.requestFocus();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
etNotes = view.findViewById(R.id.etNotes);
|
etNotes = view.findViewById(R.id.etNotes);
|
||||||
etNotes.getEditText().setRawInputType(InputType.TYPE_CLASS_TEXT);
|
etNotes.getEditText().setRawInputType(InputType.TYPE_CLASS_TEXT);
|
||||||
etNotes.getEditText().setOnEditorActionListener(new TextView.OnEditorActionListener() {
|
etNotes.getEditText().setOnEditorActionListener(new TextView.OnEditorActionListener() {
|
||||||
@@ -286,8 +238,6 @@ public class SendAddressWizardFragment extends SendWizardFragment {
|
|||||||
|
|
||||||
private void setBtcMode() {
|
private void setBtcMode() {
|
||||||
Timber.d("setBtcMode");
|
Timber.d("setBtcMode");
|
||||||
etPaymentId.getEditText().getText().clear();
|
|
||||||
llPaymentId.setVisibility(View.INVISIBLE);
|
|
||||||
tvPaymentIdIntegrated.setVisibility(View.INVISIBLE);
|
tvPaymentIdIntegrated.setVisibility(View.INVISIBLE);
|
||||||
llXmrTo.setVisibility(View.VISIBLE);
|
llXmrTo.setVisibility(View.VISIBLE);
|
||||||
sendListener.setMode(SendFragment.Mode.BTC);
|
sendListener.setMode(SendFragment.Mode.BTC);
|
||||||
@@ -342,7 +292,7 @@ public class SendAddressWizardFragment extends SendWizardFragment {
|
|||||||
|
|
||||||
final BarcodeData barcodeData =
|
final BarcodeData barcodeData =
|
||||||
new BarcodeData(BarcodeData.Asset.BTC, address, null,
|
new BarcodeData(BarcodeData.Asset.BTC, address, null,
|
||||||
resolvedBip70, null, null, String.valueOf(amount),
|
resolvedBip70, null, String.valueOf(amount),
|
||||||
BarcodeData.Security.BIP70);
|
BarcodeData.Security.BIP70);
|
||||||
etNotes.post(new Runnable() {
|
etNotes.post(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
@@ -403,22 +353,6 @@ public class SendAddressWizardFragment extends SendWizardFragment {
|
|||||||
return BitcoinAddressValidator.validate(address);
|
return BitcoinAddressValidator.validate(address);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean checkPaymentId() {
|
|
||||||
String paymentId = etPaymentId.getEditText().getText().toString();
|
|
||||||
boolean ok = paymentId.isEmpty() || Wallet.isPaymentIdValid(paymentId);
|
|
||||||
if (!ok) {
|
|
||||||
etPaymentId.setError(getString(R.string.receive_paymentid_invalid));
|
|
||||||
} else {
|
|
||||||
if (!paymentId.isEmpty() && isIntegratedAddress()) {
|
|
||||||
ok = false;
|
|
||||||
etPaymentId.setError(getString(R.string.receive_integrated_paymentid_invalid));
|
|
||||||
} else {
|
|
||||||
etPaymentId.setError(null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ok;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void shakeAddress() {
|
private void shakeAddress() {
|
||||||
etAddress.startAnimation(Helper.getShakeAnimation(getContext()));
|
etAddress.startAnimation(Helper.getShakeAnimation(getContext()));
|
||||||
}
|
}
|
||||||
@@ -441,11 +375,6 @@ public class SendAddressWizardFragment extends SendWizardFragment {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!checkPaymentId()) {
|
|
||||||
etPaymentId.startAnimation(Helper.getShakeAnimation(getContext()));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sendListener != null) {
|
if (sendListener != null) {
|
||||||
TxData txData = sendListener.getTxData();
|
TxData txData = sendListener.getTxData();
|
||||||
if (txData instanceof TxDataBtc) {
|
if (txData instanceof TxDataBtc) {
|
||||||
@@ -459,10 +388,8 @@ public class SendAddressWizardFragment extends SendWizardFragment {
|
|||||||
((TxDataBtc) txData).setBip70(null);
|
((TxDataBtc) txData).setBip70(null);
|
||||||
}
|
}
|
||||||
txData.setDestinationAddress(null);
|
txData.setDestinationAddress(null);
|
||||||
txData.setPaymentId("");
|
|
||||||
} else {
|
} else {
|
||||||
txData.setDestinationAddress(etAddress.getEditText().getText().toString());
|
txData.setDestinationAddress(etAddress.getEditText().getText().toString());
|
||||||
txData.setPaymentId(etPaymentId.getEditText().getText().toString());
|
|
||||||
}
|
}
|
||||||
txData.setUserNotes(new UserNotes(etNotes.getEditText().getText().toString()));
|
txData.setUserNotes(new UserNotes(etNotes.getEditText().getText().toString()));
|
||||||
txData.setPriority(PendingTransaction.Priority.Priority_Default);
|
txData.setPriority(PendingTransaction.Priority.Priority_Default);
|
||||||
@@ -525,14 +452,6 @@ public class SendAddressWizardFragment extends SendWizardFragment {
|
|||||||
etAddress.setError(null);
|
etAddress.setError(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
String scannedPaymentId = barcodeData.paymentId;
|
|
||||||
if (scannedPaymentId != null) {
|
|
||||||
etPaymentId.getEditText().setText(scannedPaymentId);
|
|
||||||
checkPaymentId();
|
|
||||||
} else {
|
|
||||||
etPaymentId.getEditText().getText().clear();
|
|
||||||
etPaymentId.setError(null);
|
|
||||||
}
|
|
||||||
String scannedNotes = barcodeData.description;
|
String scannedNotes = barcodeData.description;
|
||||||
if (scannedNotes != null) {
|
if (scannedNotes != null) {
|
||||||
etNotes.getEditText().setText(scannedNotes);
|
etNotes.getEditText().setText(scannedNotes);
|
||||||
|
@@ -116,12 +116,12 @@ public class SendAmountWizardFragment extends SendWizardFragment {
|
|||||||
sendListener.getTxData().setAmount(Wallet.SWEEP_ALL);
|
sendListener.getTxData().setAmount(Wallet.SWEEP_ALL);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (!etAmount.validate(maxFunds)) {
|
if (!etAmount.validate(maxFunds, 0)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sendListener != null) {
|
if (sendListener != null) {
|
||||||
String xmr = etAmount.getAmount();
|
String xmr = etAmount.getNativeAmount();
|
||||||
if (xmr != null) {
|
if (xmr != null) {
|
||||||
sendListener.getTxData().setAmount(Wallet.getAmountFromString(xmr));
|
sendListener.getTxData().setAmount(Wallet.getAmountFromString(xmr));
|
||||||
} else {
|
} else {
|
||||||
@@ -148,8 +148,8 @@ public class SendAmountWizardFragment extends SendWizardFragment {
|
|||||||
tvFunds.setText(getString(R.string.send_available,
|
tvFunds.setText(getString(R.string.send_available,
|
||||||
getString(R.string.unknown_amount)));
|
getString(R.string.unknown_amount)));
|
||||||
}
|
}
|
||||||
// getAmount is null if exchange is in progress
|
// getNativeAmount is null if exchange is in progress
|
||||||
if ((etAmount.getAmount() != null) && etAmount.getAmount().isEmpty()) {
|
if ((etAmount.getNativeAmount() != null) && etAmount.getNativeAmount().isEmpty()) {
|
||||||
final BarcodeData data = sendListener.popBarcodeData();
|
final BarcodeData data = sendListener.popBarcodeData();
|
||||||
if ((data != null) && (data.amount != null)) {
|
if ((data != null) && (data.amount != null)) {
|
||||||
etAmount.setAmount(data.amount);
|
etAmount.setAmount(data.amount);
|
||||||
|
@@ -31,7 +31,8 @@ import com.m2049r.xmrwallet.data.TxDataBtc;
|
|||||||
import com.m2049r.xmrwallet.model.Wallet;
|
import com.m2049r.xmrwallet.model.Wallet;
|
||||||
import com.m2049r.xmrwallet.util.Helper;
|
import com.m2049r.xmrwallet.util.Helper;
|
||||||
import com.m2049r.xmrwallet.util.OkHttpHelper;
|
import com.m2049r.xmrwallet.util.OkHttpHelper;
|
||||||
import com.m2049r.xmrwallet.widget.ExchangeBtcEditText;
|
import com.m2049r.xmrwallet.widget.ExchangeEditText;
|
||||||
|
import com.m2049r.xmrwallet.widget.ExchangeOtherEditText;
|
||||||
import com.m2049r.xmrwallet.widget.SendProgressView;
|
import com.m2049r.xmrwallet.widget.SendProgressView;
|
||||||
import com.m2049r.xmrwallet.xmrto.XmrToError;
|
import com.m2049r.xmrwallet.xmrto.XmrToError;
|
||||||
import com.m2049r.xmrwallet.xmrto.XmrToException;
|
import com.m2049r.xmrwallet.xmrto.XmrToException;
|
||||||
@@ -61,7 +62,7 @@ public class SendBtcAmountWizardFragment extends SendWizardFragment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private TextView tvFunds;
|
private TextView tvFunds;
|
||||||
private ExchangeBtcEditText etAmount;
|
private ExchangeOtherEditText etAmount;
|
||||||
|
|
||||||
private TextView tvXmrToParms;
|
private TextView tvXmrToParms;
|
||||||
private SendProgressView evParams;
|
private SendProgressView evParams;
|
||||||
@@ -97,7 +98,7 @@ public class SendBtcAmountWizardFragment extends SendWizardFragment {
|
|||||||
}
|
}
|
||||||
if (sendListener != null) {
|
if (sendListener != null) {
|
||||||
TxDataBtc txDataBtc = (TxDataBtc) sendListener.getTxData();
|
TxDataBtc txDataBtc = (TxDataBtc) sendListener.getTxData();
|
||||||
String btcString = etAmount.getAmount();
|
String btcString = etAmount.getNativeAmount();
|
||||||
if (btcString != null) {
|
if (btcString != null) {
|
||||||
try {
|
try {
|
||||||
double btc = Double.parseDouble(btcString);
|
double btc = Double.parseDouble(btcString);
|
||||||
@@ -166,7 +167,7 @@ public class SendBtcAmountWizardFragment extends SendWizardFragment {
|
|||||||
getView().post(new Runnable() {
|
getView().post(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
etAmount.setRate(1.0d / orderParameters.getPrice());
|
etAmount.setExchangeRate(1.0d / orderParameters.getPrice());
|
||||||
NumberFormat df = NumberFormat.getInstance(Locale.US);
|
NumberFormat df = NumberFormat.getInstance(Locale.US);
|
||||||
df.setMaximumFractionDigits(6);
|
df.setMaximumFractionDigits(6);
|
||||||
String min = df.format(orderParameters.getLowerLimit());
|
String min = df.format(orderParameters.getLowerLimit());
|
||||||
@@ -206,7 +207,7 @@ public class SendBtcAmountWizardFragment extends SendWizardFragment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void processOrderParmsError(final Exception ex) {
|
private void processOrderParmsError(final Exception ex) {
|
||||||
etAmount.setRate(0);
|
etAmount.setExchangeRate(0);
|
||||||
orderParameters = null;
|
orderParameters = null;
|
||||||
maxBtc = 0;
|
maxBtc = 0;
|
||||||
minBtc = 0;
|
minBtc = 0;
|
||||||
|
@@ -457,8 +457,7 @@ public class SendBtcConfirmWizardFragment extends SendWizardFragment implements
|
|||||||
}
|
}
|
||||||
showProgress(3, getString(R.string.label_send_progress_create_tx));
|
showProgress(3, getString(R.string.label_send_progress_create_tx));
|
||||||
TxData txData = sendListener.getTxData();
|
TxData txData = sendListener.getTxData();
|
||||||
txData.setDestinationAddress(xmrtoStatus.getXmrReceivingAddress());
|
txData.setDestinationAddress(xmrtoStatus.getXmrReceivingSubaddress());
|
||||||
txData.setPaymentId(xmrtoStatus.getXmrRequiredPaymentIdShort());
|
|
||||||
txData.setAmount(Wallet.getAmountFromDouble(xmrtoStatus.getXmrAmountTotal()));
|
txData.setAmount(Wallet.getAmountFromDouble(xmrtoStatus.getXmrAmountTotal()));
|
||||||
getActivityCallback().onPrepareSend(xmrtoStatus.getUuid(), txData);
|
getActivityCallback().onPrepareSend(xmrtoStatus.getUuid(), txData);
|
||||||
}
|
}
|
||||||
|
@@ -138,12 +138,6 @@ public class SendBtcSuccessWizardFragment extends SendWizardFragment {
|
|||||||
|
|
||||||
btcData = (TxDataBtc) sendListener.getTxData();
|
btcData = (TxDataBtc) sendListener.getTxData();
|
||||||
tvTxAddress.setText(btcData.getDestinationAddress());
|
tvTxAddress.setText(btcData.getDestinationAddress());
|
||||||
String paymentId = btcData.getPaymentId();
|
|
||||||
if ((paymentId != null) && (!paymentId.isEmpty())) {
|
|
||||||
tvTxPaymentId.setText(btcData.getPaymentId());
|
|
||||||
} else {
|
|
||||||
tvTxPaymentId.setText("-");
|
|
||||||
}
|
|
||||||
|
|
||||||
final PendingTx committedTx = sendListener.getCommittedTx();
|
final PendingTx committedTx = sendListener.getCommittedTx();
|
||||||
if (committedTx != null) {
|
if (committedTx != null) {
|
||||||
|
@@ -70,7 +70,6 @@ public class SendConfirmWizardFragment extends SendWizardFragment implements Sen
|
|||||||
}
|
}
|
||||||
|
|
||||||
private TextView tvTxAddress;
|
private TextView tvTxAddress;
|
||||||
private TextView tvTxPaymentId;
|
|
||||||
private TextView tvTxNotes;
|
private TextView tvTxNotes;
|
||||||
private TextView tvTxAmount;
|
private TextView tvTxAmount;
|
||||||
private TextView tvTxFee;
|
private TextView tvTxFee;
|
||||||
@@ -90,7 +89,6 @@ public class SendConfirmWizardFragment extends SendWizardFragment implements Sen
|
|||||||
R.layout.fragment_send_confirm, container, false);
|
R.layout.fragment_send_confirm, container, false);
|
||||||
|
|
||||||
tvTxAddress = view.findViewById(R.id.tvTxAddress);
|
tvTxAddress = view.findViewById(R.id.tvTxAddress);
|
||||||
tvTxPaymentId = view.findViewById(R.id.tvTxPaymentId);
|
|
||||||
tvTxNotes = view.findViewById(R.id.tvTxNotes);
|
tvTxNotes = view.findViewById(R.id.tvTxNotes);
|
||||||
tvTxAmount = view.findViewById(R.id.tvTxAmount);
|
tvTxAmount = view.findViewById(R.id.tvTxAmount);
|
||||||
tvTxFee = view.findViewById(R.id.tvTxFee);
|
tvTxFee = view.findViewById(R.id.tvTxFee);
|
||||||
@@ -192,12 +190,6 @@ public class SendConfirmWizardFragment extends SendWizardFragment implements Sen
|
|||||||
|
|
||||||
final TxData txData = sendListener.getTxData();
|
final TxData txData = sendListener.getTxData();
|
||||||
tvTxAddress.setText(txData.getDestinationAddress());
|
tvTxAddress.setText(txData.getDestinationAddress());
|
||||||
String paymentId = txData.getPaymentId();
|
|
||||||
if ((paymentId != null) && (!paymentId.isEmpty())) {
|
|
||||||
tvTxPaymentId.setText(txData.getPaymentId());
|
|
||||||
} else {
|
|
||||||
tvTxPaymentId.setText("-");
|
|
||||||
}
|
|
||||||
UserNotes notes = sendListener.getTxData().getUserNotes();
|
UserNotes notes = sendListener.getTxData().getUserNotes();
|
||||||
if ((notes != null) && (!notes.note.isEmpty())) {
|
if ((notes != null) && (!notes.note.isEmpty())) {
|
||||||
tvTxNotes.setText(notes.note);
|
tvTxNotes.setText(notes.note);
|
||||||
|
@@ -111,12 +111,6 @@ public class SendSuccessWizardFragment extends SendWizardFragment {
|
|||||||
|
|
||||||
final TxData txData = sendListener.getTxData();
|
final TxData txData = sendListener.getTxData();
|
||||||
tvTxAddress.setText(txData.getDestinationAddress());
|
tvTxAddress.setText(txData.getDestinationAddress());
|
||||||
String paymentId = txData.getPaymentId();
|
|
||||||
if ((paymentId != null) && (!paymentId.isEmpty())) {
|
|
||||||
tvTxPaymentId.setText(txData.getPaymentId());
|
|
||||||
} else {
|
|
||||||
tvTxPaymentId.setText("-");
|
|
||||||
}
|
|
||||||
|
|
||||||
final PendingTx committedTx = sendListener.getCommittedTx();
|
final PendingTx committedTx = sendListener.getCommittedTx();
|
||||||
if (committedTx != null) {
|
if (committedTx != null) {
|
||||||
|
@@ -298,22 +298,21 @@ public class Wallet {
|
|||||||
public PendingTransaction createTransaction(TxData txData) {
|
public PendingTransaction createTransaction(TxData txData) {
|
||||||
return createTransaction(
|
return createTransaction(
|
||||||
txData.getDestinationAddress(),
|
txData.getDestinationAddress(),
|
||||||
txData.getPaymentId(),
|
|
||||||
txData.getAmount(),
|
txData.getAmount(),
|
||||||
txData.getMixin(),
|
txData.getMixin(),
|
||||||
txData.getPriority());
|
txData.getPriority());
|
||||||
}
|
}
|
||||||
|
|
||||||
public PendingTransaction createTransaction(String dst_addr, String payment_id,
|
public PendingTransaction createTransaction(String dst_addr,
|
||||||
long amount, int mixin_count,
|
long amount, int mixin_count,
|
||||||
PendingTransaction.Priority priority) {
|
PendingTransaction.Priority priority) {
|
||||||
disposePendingTransaction();
|
disposePendingTransaction();
|
||||||
int _priority = priority.getValue();
|
int _priority = priority.getValue();
|
||||||
long txHandle =
|
long txHandle =
|
||||||
(amount == SWEEP_ALL ?
|
(amount == SWEEP_ALL ?
|
||||||
createSweepTransaction(dst_addr, payment_id, mixin_count, _priority,
|
createSweepTransaction(dst_addr, "", mixin_count, _priority,
|
||||||
accountIndex) :
|
accountIndex) :
|
||||||
createTransactionJ(dst_addr, payment_id, amount, mixin_count, _priority,
|
createTransactionJ(dst_addr, "", amount, mixin_count, _priority,
|
||||||
accountIndex));
|
accountIndex));
|
||||||
pendingTransaction = new PendingTransaction(txHandle);
|
pendingTransaction = new PendingTransaction(txHandle);
|
||||||
return pendingTransaction;
|
return pendingTransaction;
|
||||||
|
@@ -0,0 +1,215 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2019 m2049r@monerujo.io
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// https://developer.android.com/training/basics/network-ops/xml
|
||||||
|
|
||||||
|
package com.m2049r.xmrwallet.service.exchange.ecb;
|
||||||
|
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.annotation.VisibleForTesting;
|
||||||
|
|
||||||
|
import com.m2049r.xmrwallet.service.exchange.api.ExchangeApi;
|
||||||
|
import com.m2049r.xmrwallet.service.exchange.api.ExchangeCallback;
|
||||||
|
import com.m2049r.xmrwallet.service.exchange.api.ExchangeException;
|
||||||
|
import com.m2049r.xmrwallet.service.exchange.api.ExchangeRate;
|
||||||
|
|
||||||
|
import org.w3c.dom.Document;
|
||||||
|
import org.w3c.dom.Element;
|
||||||
|
import org.w3c.dom.Node;
|
||||||
|
import org.w3c.dom.NodeList;
|
||||||
|
import org.xml.sax.SAXException;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.text.ParseException;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.Calendar;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.TimeZone;
|
||||||
|
|
||||||
|
import javax.xml.parsers.DocumentBuilder;
|
||||||
|
import javax.xml.parsers.DocumentBuilderFactory;
|
||||||
|
import javax.xml.parsers.ParserConfigurationException;
|
||||||
|
|
||||||
|
import okhttp3.Call;
|
||||||
|
import okhttp3.HttpUrl;
|
||||||
|
import okhttp3.OkHttpClient;
|
||||||
|
import okhttp3.Request;
|
||||||
|
import okhttp3.Response;
|
||||||
|
import timber.log.Timber;
|
||||||
|
|
||||||
|
public class ExchangeApiImpl implements ExchangeApi {
|
||||||
|
@NonNull
|
||||||
|
private final OkHttpClient okHttpClient;
|
||||||
|
@NonNull
|
||||||
|
private final HttpUrl baseUrl;
|
||||||
|
|
||||||
|
//so we can inject the mockserver url
|
||||||
|
@VisibleForTesting
|
||||||
|
public ExchangeApiImpl(@NonNull final OkHttpClient okHttpClient, @NonNull final HttpUrl baseUrl) {
|
||||||
|
this.okHttpClient = okHttpClient;
|
||||||
|
this.baseUrl = baseUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ExchangeApiImpl(@NonNull final OkHttpClient okHttpClient) {
|
||||||
|
this(okHttpClient, HttpUrl.parse("https://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml"));
|
||||||
|
// data is daily and is refreshed around 16:00 CET every working day
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isSameDay(Calendar calendar, Calendar anotherCalendar) {
|
||||||
|
return (calendar.get(Calendar.YEAR) == anotherCalendar.get(Calendar.YEAR)) &&
|
||||||
|
(calendar.get(Calendar.DAY_OF_YEAR) == anotherCalendar.get(Calendar.DAY_OF_YEAR));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void queryExchangeRate(@NonNull final String baseCurrency, @NonNull final String quoteCurrency,
|
||||||
|
@NonNull final ExchangeCallback callback) {
|
||||||
|
if (!baseCurrency.equals("EUR")) {
|
||||||
|
callback.onError(new IllegalArgumentException("Only EUR supported as base"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (baseCurrency.equals(quoteCurrency)) {
|
||||||
|
callback.onSuccess(new ExchangeRateImpl(quoteCurrency, 1.0, new Date()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fetchDate != null) { // we have data
|
||||||
|
boolean useCache = false;
|
||||||
|
// figure out if we can use the cached values
|
||||||
|
// data is daily and is refreshed around 16:00 CET every working day
|
||||||
|
Calendar now = Calendar.getInstance(TimeZone.getTimeZone("CET"));
|
||||||
|
|
||||||
|
int fetchWeekday = fetchDate.get(Calendar.DAY_OF_WEEK);
|
||||||
|
int fetchDay = fetchDate.get(Calendar.DAY_OF_YEAR);
|
||||||
|
int fetchHour = fetchDate.get(Calendar.HOUR_OF_DAY);
|
||||||
|
|
||||||
|
int today = now.get(Calendar.DAY_OF_YEAR);
|
||||||
|
int nowHour = now.get(Calendar.HOUR_OF_DAY);
|
||||||
|
|
||||||
|
if (
|
||||||
|
// was it fetched today before 16:00? assume no new data iff now < 16:00 as well
|
||||||
|
((today == fetchDay) && (fetchHour < 16) && (nowHour < 16))
|
||||||
|
// was it fetched after, 17:00? we can assume there is no newer data
|
||||||
|
|| ((today == fetchDay) && (fetchHour > 17))
|
||||||
|
|| ((today == fetchDay + 1) && (fetchHour > 17) && (nowHour < 16))
|
||||||
|
// is the data itself from today? there can be no newer data
|
||||||
|
|| (fxDate.get(Calendar.DAY_OF_YEAR) == today)
|
||||||
|
// was it fetched Sat/Sun? we can assume there is no newer data
|
||||||
|
|| ((fetchWeekday == Calendar.SATURDAY) || (fetchWeekday == Calendar.SUNDAY))
|
||||||
|
) { // return cached rate
|
||||||
|
try {
|
||||||
|
callback.onSuccess(getRate(quoteCurrency));
|
||||||
|
} catch (ExchangeException ex) {
|
||||||
|
callback.onError(ex);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final Request httpRequest = createHttpRequest(baseUrl);
|
||||||
|
|
||||||
|
okHttpClient.newCall(httpRequest).enqueue(new okhttp3.Callback() {
|
||||||
|
@Override
|
||||||
|
public void onFailure(final Call call, final IOException ex) {
|
||||||
|
callback.onError(ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onResponse(final Call call, final Response response) throws IOException {
|
||||||
|
if (response.isSuccessful()) {
|
||||||
|
try {
|
||||||
|
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
|
||||||
|
DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
|
||||||
|
Document doc = dBuilder.parse(response.body().byteStream());
|
||||||
|
doc.getDocumentElement().normalize();
|
||||||
|
parse(doc);
|
||||||
|
try {
|
||||||
|
callback.onSuccess(getRate(quoteCurrency));
|
||||||
|
} catch (ExchangeException ex) {
|
||||||
|
callback.onError(ex);
|
||||||
|
}
|
||||||
|
} catch (ParserConfigurationException | SAXException ex) {
|
||||||
|
Timber.w(ex);
|
||||||
|
callback.onError(new ExchangeException(ex.getLocalizedMessage()));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
callback.onError(new ExchangeException(response.code(), response.message()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private Request createHttpRequest(final HttpUrl url) {
|
||||||
|
return new Request.Builder()
|
||||||
|
.url(url)
|
||||||
|
.get()
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
final private Map<String, Double> fxEntries = new HashMap<>();
|
||||||
|
private Calendar fxDate = null;
|
||||||
|
private Calendar fetchDate = null;
|
||||||
|
|
||||||
|
synchronized private ExchangeRate getRate(String currency) throws ExchangeException {
|
||||||
|
Timber.e("Getting %s", currency);
|
||||||
|
final Double rate = fxEntries.get(currency);
|
||||||
|
if (rate == null) throw new ExchangeException(404, "Currency not supported: " + currency);
|
||||||
|
return new ExchangeRateImpl(currency, rate, fxDate.getTime());
|
||||||
|
}
|
||||||
|
|
||||||
|
private final static SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd", Locale.US);
|
||||||
|
|
||||||
|
{
|
||||||
|
DATE_FORMAT.setTimeZone(TimeZone.getTimeZone("UTC"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void parse(final Document xmlRootDoc) {
|
||||||
|
final Map<String, Double> entries = new HashMap<>();
|
||||||
|
Calendar date = Calendar.getInstance(TimeZone.getTimeZone("CET"));
|
||||||
|
try {
|
||||||
|
NodeList cubes = xmlRootDoc.getElementsByTagName("Cube");
|
||||||
|
for (int i = 0; i < cubes.getLength(); i++) {
|
||||||
|
Node node = cubes.item(i);
|
||||||
|
if (node.getNodeType() == Node.ELEMENT_NODE) {
|
||||||
|
Element cube = (Element) node;
|
||||||
|
if (cube.hasAttribute("time")) { // a time Cube
|
||||||
|
final Date time = DATE_FORMAT.parse(cube.getAttribute("time"));
|
||||||
|
date.setTime(time);
|
||||||
|
} else if (cube.hasAttribute("currency")
|
||||||
|
&& cube.hasAttribute("rate")) { // a rate Cube
|
||||||
|
String currency = cube.getAttribute("currency");
|
||||||
|
double rate = Double.valueOf(cube.getAttribute("rate"));
|
||||||
|
entries.put(currency, rate);
|
||||||
|
} // else an empty Cube - ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (ParseException ex) {
|
||||||
|
Timber.d(ex);
|
||||||
|
}
|
||||||
|
synchronized (this) {
|
||||||
|
if (date != null) {
|
||||||
|
fetchDate = Calendar.getInstance(TimeZone.getTimeZone("CET"));
|
||||||
|
fxDate = date;
|
||||||
|
fxEntries.clear();
|
||||||
|
fxEntries.putAll(entries);
|
||||||
|
}
|
||||||
|
// else don't change what we have
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,57 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2019 m2049r et al.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.m2049r.xmrwallet.service.exchange.ecb;
|
||||||
|
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
|
||||||
|
import com.m2049r.xmrwallet.service.exchange.api.ExchangeRate;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
class ExchangeRateImpl implements ExchangeRate {
|
||||||
|
private final Date date;
|
||||||
|
private final String baseCurrency = "EUR";
|
||||||
|
private final String quoteCurrency;
|
||||||
|
private final double rate;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getServiceName() {
|
||||||
|
return "ecb.europa.eu";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getBaseCurrency() {
|
||||||
|
return baseCurrency;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getQuoteCurrency() {
|
||||||
|
return quoteCurrency;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double getRate() {
|
||||||
|
return rate;
|
||||||
|
}
|
||||||
|
|
||||||
|
ExchangeRateImpl(@NonNull final String quoteCurrency, double rate, @NonNull final Date date) {
|
||||||
|
super();
|
||||||
|
this.quoteCurrency = quoteCurrency;
|
||||||
|
this.rate = rate;
|
||||||
|
this.date = date;
|
||||||
|
}
|
||||||
|
}
|
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2017-2018 m2049r et al.
|
* Copyright (c) 2017-2019 m2049r et al.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.m2049r.xmrwallet.service.exchange.coinmarketcap;
|
package com.m2049r.xmrwallet.service.exchange.kraken;
|
||||||
|
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
import android.support.annotation.VisibleForTesting;
|
import android.support.annotation.VisibleForTesting;
|
||||||
@@ -36,9 +36,9 @@ import okhttp3.HttpUrl;
|
|||||||
import okhttp3.OkHttpClient;
|
import okhttp3.OkHttpClient;
|
||||||
import okhttp3.Request;
|
import okhttp3.Request;
|
||||||
import okhttp3.Response;
|
import okhttp3.Response;
|
||||||
|
import timber.log.Timber;
|
||||||
|
|
||||||
public class ExchangeApiImpl implements ExchangeApi {
|
public class ExchangeApiImpl implements ExchangeApi {
|
||||||
static final String CRYPTO_ID = "328";
|
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
private final OkHttpClient okHttpClient;
|
private final OkHttpClient okHttpClient;
|
||||||
@@ -47,14 +47,13 @@ public class ExchangeApiImpl implements ExchangeApi {
|
|||||||
|
|
||||||
//so we can inject the mockserver url
|
//so we can inject the mockserver url
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
ExchangeApiImpl(@NonNull final OkHttpClient okHttpClient, final HttpUrl baseUrl) {
|
public ExchangeApiImpl(@NonNull final OkHttpClient okHttpClient, final HttpUrl baseUrl) {
|
||||||
|
|
||||||
this.okHttpClient = okHttpClient;
|
this.okHttpClient = okHttpClient;
|
||||||
this.baseUrl = baseUrl;
|
this.baseUrl = baseUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ExchangeApiImpl(@NonNull final OkHttpClient okHttpClient) {
|
public ExchangeApiImpl(@NonNull final OkHttpClient okHttpClient) {
|
||||||
this(okHttpClient, HttpUrl.parse("https://api.coinmarketcap.com/v2/ticker/"));
|
this(okHttpClient, HttpUrl.parse("https://api.kraken.com/0/public/Ticker"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -66,29 +65,25 @@ public class ExchangeApiImpl implements ExchangeApi {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean inverse = false;
|
boolean invertQuery;
|
||||||
String fiat = null;
|
|
||||||
|
|
||||||
if (baseCurrency.equals(Helper.CRYPTO)) {
|
|
||||||
fiat = quoteCurrency;
|
|
||||||
inverse = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (quoteCurrency.equals(Helper.CRYPTO)) {
|
if (Helper.BASE_CRYPTO.equals(baseCurrency)) {
|
||||||
fiat = baseCurrency;
|
invertQuery = false;
|
||||||
inverse = true;
|
} else if (Helper.BASE_CRYPTO.equals(quoteCurrency)) {
|
||||||
}
|
invertQuery = true;
|
||||||
|
} else {
|
||||||
if (fiat == null) {
|
callback.onError(new IllegalArgumentException("no crypto specified"));
|
||||||
callback.onError(new IllegalArgumentException("no fiat specified"));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final boolean swapAssets = inverse;
|
Timber.d("queryExchangeRate: i %b, b %s, q %s", invertQuery, baseCurrency, quoteCurrency);
|
||||||
|
final boolean invert = invertQuery;
|
||||||
|
final String base = invert ? quoteCurrency : baseCurrency;
|
||||||
|
final String quote = invert ? baseCurrency : quoteCurrency;
|
||||||
|
|
||||||
final HttpUrl url = baseUrl.newBuilder()
|
final HttpUrl url = baseUrl.newBuilder()
|
||||||
.addEncodedPathSegments(CRYPTO_ID + "/")
|
.addQueryParameter("pair", base + (quote.equals("BTC") ? "XBT" : quote))
|
||||||
.addQueryParameter("convert", fiat)
|
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
final Request httpRequest = createHttpRequest(url);
|
final Request httpRequest = createHttpRequest(url);
|
||||||
@@ -104,13 +99,13 @@ public class ExchangeApiImpl implements ExchangeApi {
|
|||||||
if (response.isSuccessful()) {
|
if (response.isSuccessful()) {
|
||||||
try {
|
try {
|
||||||
final JSONObject json = new JSONObject(response.body().string());
|
final JSONObject json = new JSONObject(response.body().string());
|
||||||
final JSONObject metadata = json.getJSONObject("metadata");
|
final JSONArray jsonError = json.getJSONArray("error");
|
||||||
if (!metadata.isNull("error")) {
|
if (jsonError.length() > 0) {
|
||||||
final String errorMsg = metadata.getString("error");
|
final String errorMsg = jsonError.getString(0);
|
||||||
callback.onError(new ExchangeException(response.code(), errorMsg));
|
callback.onError(new ExchangeException(response.code(), errorMsg));
|
||||||
} else {
|
} else {
|
||||||
final JSONObject jsonResult = json.getJSONObject("data");
|
final JSONObject jsonResult = json.getJSONObject("result");
|
||||||
reportSuccess(jsonResult, swapAssets, callback);
|
reportSuccess(jsonResult, invert, callback);
|
||||||
}
|
}
|
||||||
} catch (JSONException ex) {
|
} catch (JSONException ex) {
|
||||||
callback.onError(new ExchangeException(ex.getLocalizedMessage()));
|
callback.onError(new ExchangeException(ex.getLocalizedMessage()));
|
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2017-2018 m2049r et al.
|
* Copyright (c) 2017 m2049r et al.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.m2049r.xmrwallet.service.exchange.coinmarketcap;
|
package com.m2049r.xmrwallet.service.exchange.kraken;
|
||||||
|
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
|
|
||||||
@@ -25,7 +25,6 @@ import org.json.JSONArray;
|
|||||||
import org.json.JSONException;
|
import org.json.JSONException;
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.NoSuchElementException;
|
import java.util.NoSuchElementException;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
@@ -38,7 +37,7 @@ class ExchangeRateImpl implements ExchangeRate {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getServiceName() {
|
public String getServiceName() {
|
||||||
return "coinmarketcap.com";
|
return "kraken.com";
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -65,21 +64,29 @@ class ExchangeRateImpl implements ExchangeRate {
|
|||||||
|
|
||||||
ExchangeRateImpl(final JSONObject jsonObject, final boolean swapAssets) throws JSONException, ExchangeException {
|
ExchangeRateImpl(final JSONObject jsonObject, final boolean swapAssets) throws JSONException, ExchangeException {
|
||||||
try {
|
try {
|
||||||
final String baseC = jsonObject.getString("symbol");
|
final String key = jsonObject.keys().next(); // we expect only one
|
||||||
final JSONObject quotes = jsonObject.getJSONObject("quotes");
|
Pattern pattern = Pattern.compile("^X(.*?)Z(.*?)$");
|
||||||
final Iterator<String> keys = quotes.keys();
|
Matcher matcher = pattern.matcher(key);
|
||||||
String key = null;
|
if (matcher.find()) {
|
||||||
// get key which is not USD unless it is the only one
|
baseCurrency = swapAssets ? matcher.group(2) : matcher.group(1);
|
||||||
while (keys.hasNext()) {
|
quoteCurrency = swapAssets ? matcher.group(1) : matcher.group(2);
|
||||||
key = keys.next();
|
} else {
|
||||||
if (!key.equals("USD")) break;
|
throw new ExchangeException("no pair returned!");
|
||||||
|
}
|
||||||
|
|
||||||
|
JSONObject pair = jsonObject.getJSONObject(key);
|
||||||
|
JSONArray close = pair.getJSONArray("c");
|
||||||
|
String closePrice = close.getString(0);
|
||||||
|
if (closePrice != null) {
|
||||||
|
try {
|
||||||
|
double rate = Double.parseDouble(closePrice);
|
||||||
|
this.rate = swapAssets ? (1 / rate) : rate;
|
||||||
|
} catch (NumberFormatException ex) {
|
||||||
|
throw new ExchangeException(ex.getLocalizedMessage());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new ExchangeException("no close price returned!");
|
||||||
}
|
}
|
||||||
final String quoteC = key;
|
|
||||||
baseCurrency = swapAssets ? quoteC : baseC;
|
|
||||||
quoteCurrency = swapAssets ? baseC : quoteC;
|
|
||||||
JSONObject quote = quotes.getJSONObject(key);
|
|
||||||
double price = quote.getDouble("price");
|
|
||||||
this.rate = swapAssets ? (1d / price) : price;
|
|
||||||
} catch (NoSuchElementException ex) {
|
} catch (NoSuchElementException ex) {
|
||||||
throw new ExchangeException(ex.getLocalizedMessage());
|
throw new ExchangeException(ex.getLocalizedMessage());
|
||||||
}
|
}
|
@@ -0,0 +1,99 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2019 m2049r@monerujo.io
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// https://developer.android.com/training/basics/network-ops/xml
|
||||||
|
|
||||||
|
package com.m2049r.xmrwallet.service.exchange.krakenEcb;
|
||||||
|
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
|
||||||
|
import com.m2049r.xmrwallet.service.exchange.api.ExchangeApi;
|
||||||
|
import com.m2049r.xmrwallet.service.exchange.api.ExchangeCallback;
|
||||||
|
import com.m2049r.xmrwallet.service.exchange.api.ExchangeRate;
|
||||||
|
import com.m2049r.xmrwallet.util.Helper;
|
||||||
|
|
||||||
|
import okhttp3.OkHttpClient;
|
||||||
|
import timber.log.Timber;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Gets the XMR/EUR rate from kraken and then gets the EUR/fiat rate from the ECB
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class ExchangeApiImpl implements ExchangeApi {
|
||||||
|
static public final String BASE_FIAT = "EUR";
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
private final OkHttpClient okHttpClient;
|
||||||
|
|
||||||
|
public ExchangeApiImpl(@NonNull final OkHttpClient okHttpClient) {
|
||||||
|
this.okHttpClient = okHttpClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void queryExchangeRate(@NonNull final String baseCurrency, @NonNull final String quoteCurrency,
|
||||||
|
@NonNull final ExchangeCallback callback) {
|
||||||
|
Timber.d("B=%s Q=%s", baseCurrency, quoteCurrency);
|
||||||
|
if (baseCurrency.equals(quoteCurrency)) {
|
||||||
|
Timber.d("BASE=QUOTE=1");
|
||||||
|
callback.onSuccess(new ExchangeRateImpl(baseCurrency, quoteCurrency, 1.0));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Helper.BASE_CRYPTO.equals(baseCurrency)
|
||||||
|
&& !Helper.BASE_CRYPTO.equals(quoteCurrency)) {
|
||||||
|
callback.onError(new IllegalArgumentException("no " + Helper.BASE_CRYPTO + " specified"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final String quote = Helper.BASE_CRYPTO.equals(baseCurrency) ? quoteCurrency : baseCurrency;
|
||||||
|
|
||||||
|
final ExchangeApi krakenApi =
|
||||||
|
new com.m2049r.xmrwallet.service.exchange.kraken.ExchangeApiImpl(okHttpClient);
|
||||||
|
krakenApi.queryExchangeRate(Helper.BASE_CRYPTO, BASE_FIAT, new ExchangeCallback() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(final ExchangeRate krakenRate) {
|
||||||
|
Timber.d("kraken = %f", krakenRate.getRate());
|
||||||
|
final ExchangeApi ecbApi =
|
||||||
|
new com.m2049r.xmrwallet.service.exchange.ecb.ExchangeApiImpl(okHttpClient);
|
||||||
|
ecbApi.queryExchangeRate(BASE_FIAT, quote, new ExchangeCallback() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(final ExchangeRate ecbRate) {
|
||||||
|
Timber.d("ECB = %f", ecbRate.getRate());
|
||||||
|
double rate = ecbRate.getRate() * krakenRate.getRate();
|
||||||
|
Timber.d("Q=%s QC=%s", quote, quoteCurrency);
|
||||||
|
if (!quote.equals(quoteCurrency)) rate = 1.0d / rate;
|
||||||
|
Timber.d("rate = %f", rate);
|
||||||
|
final ExchangeRate exchangeRate =
|
||||||
|
new ExchangeRateImpl(baseCurrency, quoteCurrency, rate);
|
||||||
|
callback.onSuccess(exchangeRate);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(Exception ex) {
|
||||||
|
Timber.d(ex);
|
||||||
|
callback.onError(ex);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(Exception ex) {
|
||||||
|
Timber.d(ex);
|
||||||
|
callback.onError(ex);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2019 m2049r et al.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.m2049r.xmrwallet.service.exchange.krakenEcb;
|
||||||
|
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
|
||||||
|
import com.m2049r.xmrwallet.service.exchange.api.ExchangeRate;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
class ExchangeRateImpl implements ExchangeRate {
|
||||||
|
private final String baseCurrency;
|
||||||
|
private final String quoteCurrency;
|
||||||
|
private final double rate;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getServiceName() {
|
||||||
|
return "kraken+ecb";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getBaseCurrency() {
|
||||||
|
return baseCurrency;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getQuoteCurrency() {
|
||||||
|
return quoteCurrency;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double getRate() {
|
||||||
|
return rate;
|
||||||
|
}
|
||||||
|
|
||||||
|
ExchangeRateImpl(@NonNull final String baseCurrency, @NonNull final String quoteCurrency, double rate) {
|
||||||
|
super();
|
||||||
|
this.baseCurrency = baseCurrency;
|
||||||
|
this.quoteCurrency = quoteCurrency;
|
||||||
|
this.rate = rate;
|
||||||
|
}
|
||||||
|
}
|
@@ -58,7 +58,6 @@ import android.widget.TextView;
|
|||||||
import com.m2049r.xmrwallet.BuildConfig;
|
import com.m2049r.xmrwallet.BuildConfig;
|
||||||
import com.m2049r.xmrwallet.R;
|
import com.m2049r.xmrwallet.R;
|
||||||
import com.m2049r.xmrwallet.model.NetworkType;
|
import com.m2049r.xmrwallet.model.NetworkType;
|
||||||
import com.m2049r.xmrwallet.model.Wallet;
|
|
||||||
import com.m2049r.xmrwallet.model.WalletManager;
|
import com.m2049r.xmrwallet.model.WalletManager;
|
||||||
import com.m2049r.xmrwallet.service.exchange.api.ExchangeApi;
|
import com.m2049r.xmrwallet.service.exchange.api.ExchangeApi;
|
||||||
|
|
||||||
@@ -85,7 +84,7 @@ public class Helper {
|
|||||||
|
|
||||||
static public final String NOCRAZYPASS_FLAGFILE = ".nocrazypass";
|
static public final String NOCRAZYPASS_FLAGFILE = ".nocrazypass";
|
||||||
|
|
||||||
static public final String CRYPTO = "XMR";
|
static public final String BASE_CRYPTO = "XMR";
|
||||||
|
|
||||||
static private final String WALLET_DIR = "monerujo" + FLAVOR_SUFFIX;
|
static private final String WALLET_DIR = "monerujo" + FLAVOR_SUFFIX;
|
||||||
static private final String HOME_DIR = "monero" + FLAVOR_SUFFIX;
|
static private final String HOME_DIR = "monero" + FLAVOR_SUFFIX;
|
||||||
@@ -207,22 +206,33 @@ public class Helper {
|
|||||||
return d.toPlainString();
|
return d.toPlainString();
|
||||||
}
|
}
|
||||||
|
|
||||||
static public String getFormattedAmount(double amount, boolean isXmr) {
|
static public String getFormattedAmount(double amount, boolean isCrypto) {
|
||||||
// at this point selection is XMR in case of error
|
// at this point selection is XMR in case of error
|
||||||
String displayB;
|
String displayB;
|
||||||
if (isXmr) { // XMR
|
if (isCrypto) {
|
||||||
long xmr = Wallet.getAmountFromDouble(amount);
|
if ((amount >= 0) || (amount == 0)) {
|
||||||
if ((xmr > 0) || (amount == 0)) {
|
|
||||||
displayB = String.format(Locale.US, "%,.5f", amount);
|
displayB = String.format(Locale.US, "%,.5f", amount);
|
||||||
} else {
|
} else {
|
||||||
displayB = null;
|
displayB = null;
|
||||||
}
|
}
|
||||||
} else { // not XMR
|
} else { // not crypto
|
||||||
displayB = String.format(Locale.US, "%,.2f", amount);
|
displayB = String.format(Locale.US, "%,.2f", amount);
|
||||||
}
|
}
|
||||||
return displayB;
|
return displayB;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// min 2 significant digits after decimal point
|
||||||
|
static public String getFormattedAmount(double amount) {
|
||||||
|
if ((amount >= 1.0d) || (amount == 0))
|
||||||
|
return String.format(Locale.US, "%,.2f", amount);
|
||||||
|
else { // amount < 1
|
||||||
|
int decimals = 1 - (int) Math.floor(Math.log10(amount));
|
||||||
|
if (decimals < 2) decimals = 2;
|
||||||
|
if (decimals > 12) decimals = 12;
|
||||||
|
return String.format(Locale.US, "%,." + decimals + "f", amount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static public Bitmap getBitmap(Context context, int drawableId) {
|
static public Bitmap getBitmap(Context context, int drawableId) {
|
||||||
Drawable drawable = ContextCompat.getDrawable(context, drawableId);
|
Drawable drawable = ContextCompat.getDrawable(context, drawableId);
|
||||||
if (drawable instanceof BitmapDrawable) {
|
if (drawable instanceof BitmapDrawable) {
|
||||||
@@ -624,7 +634,7 @@ public class Helper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static public ExchangeApi getExchangeApi() {
|
static public ExchangeApi getExchangeApi() {
|
||||||
return new com.m2049r.xmrwallet.service.exchange.coinmarketcap.ExchangeApiImpl(OkHttpHelper.getOkHttpClient());
|
return new com.m2049r.xmrwallet.service.exchange.krakenEcb.ExchangeApiImpl(OkHttpHelper.getOkHttpClient());
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface Action {
|
public interface Action {
|
||||||
|
@@ -105,6 +105,14 @@ public class RestoreHeight {
|
|||||||
blockheight.put("2019-05-01", 1824671L);
|
blockheight.put("2019-05-01", 1824671L);
|
||||||
blockheight.put("2019-06-01", 1847005L);
|
blockheight.put("2019-06-01", 1847005L);
|
||||||
blockheight.put("2019-07-01", 1868590L);
|
blockheight.put("2019-07-01", 1868590L);
|
||||||
|
blockheight.put("2019-08-01", 1890878L);
|
||||||
|
blockheight.put("2019-09-01", 1913201L);
|
||||||
|
blockheight.put("2019-10-01", 1934732L);
|
||||||
|
blockheight.put("2019-11-01", 1957051L);
|
||||||
|
blockheight.put("2019-12-01", 1978433L);
|
||||||
|
blockheight.put("2020-01-01", 2001315L);
|
||||||
|
blockheight.put("2020-02-01", 2023656L);
|
||||||
|
blockheight.put("2020-03-01", 2044552L);
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getHeight(String date) {
|
public long getHeight(String date) {
|
||||||
|
@@ -1,187 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2017 m2049r
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
// based on https://code.tutsplus.com/tutorials/creating-compound-views-on-android--cms-22889
|
|
||||||
|
|
||||||
package com.m2049r.xmrwallet.widget;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.text.Editable;
|
|
||||||
import android.text.TextWatcher;
|
|
||||||
import android.util.AttributeSet;
|
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.widget.ArrayAdapter;
|
|
||||||
import android.widget.EditText;
|
|
||||||
import android.widget.LinearLayout;
|
|
||||||
import android.widget.Spinner;
|
|
||||||
import android.widget.TextView;
|
|
||||||
|
|
||||||
import com.m2049r.xmrwallet.R;
|
|
||||||
import com.m2049r.xmrwallet.util.Helper;
|
|
||||||
|
|
||||||
import timber.log.Timber;
|
|
||||||
|
|
||||||
public class ExchangeBtcEditText extends LinearLayout {
|
|
||||||
|
|
||||||
String btcAmount = null;
|
|
||||||
String xmrAmount = null;
|
|
||||||
|
|
||||||
private boolean validate(String amount, double max, double min) {
|
|
||||||
boolean ok = true;
|
|
||||||
if (amount != null) {
|
|
||||||
try {
|
|
||||||
double x = Double.parseDouble(amount);
|
|
||||||
if ((x < min) || (x > max)) {
|
|
||||||
ok = false;
|
|
||||||
}
|
|
||||||
} catch (NumberFormatException ex) {
|
|
||||||
Timber.e(ex.getLocalizedMessage());
|
|
||||||
ok = false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ok = false;
|
|
||||||
}
|
|
||||||
return ok;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean validate(double maxBtc, double minBtc) {
|
|
||||||
Timber.d("validate(maxBtc=%f,minBtc=%f)", maxBtc, minBtc);
|
|
||||||
boolean ok = true;
|
|
||||||
if (!validate(btcAmount, maxBtc, minBtc)) {
|
|
||||||
Timber.d("btcAmount invalid %s", btcAmount);
|
|
||||||
shakeAmountField();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void shakeAmountField() {
|
|
||||||
etAmountA.startAnimation(Helper.getShakeAnimation(getContext()));
|
|
||||||
}
|
|
||||||
|
|
||||||
void shakeExchangeField() {
|
|
||||||
tvAmountB.startAnimation(Helper.getShakeAnimation(getContext()));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setRate(double xmrBtcRate) {
|
|
||||||
this.xmrBtcRate = xmrBtcRate;
|
|
||||||
post(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
exchange();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setAmount(String btcAmount) {
|
|
||||||
this.btcAmount = btcAmount;
|
|
||||||
etAmountA.setText(btcAmount);
|
|
||||||
xmrAmount = null;
|
|
||||||
exchange();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setEditable(boolean editable) {
|
|
||||||
etAmountA.setEnabled(editable);
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getAmount() {
|
|
||||||
return btcAmount;
|
|
||||||
}
|
|
||||||
|
|
||||||
EditText etAmountA;
|
|
||||||
TextView tvAmountB;
|
|
||||||
Spinner sCurrencyA;
|
|
||||||
Spinner sCurrencyB;
|
|
||||||
|
|
||||||
public ExchangeBtcEditText(Context context) {
|
|
||||||
super(context);
|
|
||||||
initializeViews(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ExchangeBtcEditText(Context context, AttributeSet attrs) {
|
|
||||||
super(context, attrs);
|
|
||||||
initializeViews(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ExchangeBtcEditText(Context context,
|
|
||||||
AttributeSet attrs,
|
|
||||||
int defStyle) {
|
|
||||||
super(context, attrs, defStyle);
|
|
||||||
initializeViews(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Inflates the views in the layout.
|
|
||||||
*
|
|
||||||
* @param context the current context for the view.
|
|
||||||
*/
|
|
||||||
private void initializeViews(Context context) {
|
|
||||||
LayoutInflater inflater = (LayoutInflater) context
|
|
||||||
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
|
||||||
inflater.inflate(R.layout.view_exchange_btc_edit, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onFinishInflate() {
|
|
||||||
super.onFinishInflate();
|
|
||||||
etAmountA = findViewById(R.id.etAmountA);
|
|
||||||
etAmountA.addTextChangedListener(new TextWatcher() {
|
|
||||||
@Override
|
|
||||||
public void afterTextChanged(Editable s) {
|
|
||||||
exchange();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
tvAmountB = findViewById(R.id.tvAmountB);
|
|
||||||
sCurrencyA = findViewById(R.id.sCurrencyA);
|
|
||||||
sCurrencyB = findViewById(R.id.sCurrencyB);
|
|
||||||
|
|
||||||
ArrayAdapter<String> btcAdapter = new ArrayAdapter<String>(getContext(),
|
|
||||||
android.R.layout.simple_spinner_item,
|
|
||||||
new String[]{"BTC"});
|
|
||||||
sCurrencyA.setAdapter(btcAdapter);
|
|
||||||
sCurrencyA.setEnabled(false);
|
|
||||||
ArrayAdapter<String> xmrAdapter = new ArrayAdapter<String>(getContext(),
|
|
||||||
android.R.layout.simple_spinner_item,
|
|
||||||
new String[]{"XMR"});
|
|
||||||
sCurrencyB.setAdapter(xmrAdapter);
|
|
||||||
sCurrencyB.setEnabled(false);
|
|
||||||
etAmountA.setFocusable(true);
|
|
||||||
etAmountA.setFocusableInTouchMode(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
double xmrBtcRate = 0;
|
|
||||||
|
|
||||||
public void exchange() {
|
|
||||||
btcAmount = etAmountA.getText().toString();
|
|
||||||
if (!btcAmount.isEmpty() && (xmrBtcRate > 0)) {
|
|
||||||
double xmr = xmrBtcRate * Double.parseDouble(btcAmount);
|
|
||||||
xmrAmount = Helper.getFormattedAmount(xmr, true);
|
|
||||||
} else {
|
|
||||||
xmrAmount = "";
|
|
||||||
}
|
|
||||||
tvAmountB.setText(getResources().getString(R.string.send_amount_btc_xmr, xmrAmount));
|
|
||||||
Timber.d("%s BTC =%f> %s XMR", btcAmount, xmrBtcRate, xmrAmount);
|
|
||||||
}
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,196 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2017-2019 m2049r
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// based on https://code.tutsplus.com/tutorials/creating-compound-views-on-android--cms-22889
|
||||||
|
|
||||||
|
package com.m2049r.xmrwallet.widget;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.res.TypedArray;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.Looper;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.widget.Spinner;
|
||||||
|
|
||||||
|
import com.m2049r.xmrwallet.R;
|
||||||
|
import com.m2049r.xmrwallet.service.exchange.api.ExchangeCallback;
|
||||||
|
import com.m2049r.xmrwallet.service.exchange.api.ExchangeRate;
|
||||||
|
import com.m2049r.xmrwallet.util.Helper;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import timber.log.Timber;
|
||||||
|
|
||||||
|
public class ExchangeOtherEditText extends ExchangeEditText {
|
||||||
|
/*
|
||||||
|
all exchanges are done through XMR
|
||||||
|
baseCurrency is the native currency
|
||||||
|
*/
|
||||||
|
|
||||||
|
String baseCurrency = null; // not XMR
|
||||||
|
private double exchangeRate = 0; // baseCurrency to XMR
|
||||||
|
|
||||||
|
public void setExchangeRate(double rate) {
|
||||||
|
exchangeRate = rate;
|
||||||
|
post(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
startExchange();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setBaseCurrency(Context context, AttributeSet attrs) {
|
||||||
|
TypedArray ta = context.getTheme().obtainStyledAttributes(attrs, R.styleable.ExchangeEditText, 0, 0);
|
||||||
|
try {
|
||||||
|
baseCurrency = ta.getString(R.styleable.ExchangeEditText_baseSymbol);
|
||||||
|
if (baseCurrency == null)
|
||||||
|
throw new IllegalArgumentException("base currency must be set");
|
||||||
|
} finally {
|
||||||
|
ta.recycle();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ExchangeOtherEditText(Context context, AttributeSet attrs) {
|
||||||
|
super(context, attrs);
|
||||||
|
setBaseCurrency(context, attrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ExchangeOtherEditText(Context context,
|
||||||
|
AttributeSet attrs,
|
||||||
|
int defStyle) {
|
||||||
|
super(context, attrs, defStyle);
|
||||||
|
setBaseCurrency(context, attrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void setCurrencyAdapter(Spinner spinner) {
|
||||||
|
List<String> currencies = new ArrayList<>();
|
||||||
|
if (!baseCurrency.equals(Helper.BASE_CRYPTO)) currencies.add(baseCurrency);
|
||||||
|
currencies.add(Helper.BASE_CRYPTO);
|
||||||
|
setCurrencyAdapter(spinner, currencies);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void setInitialSpinnerSelections(Spinner baseSpinner, Spinner quoteSpinner) {
|
||||||
|
baseSpinner.setSelection(0, true);
|
||||||
|
quoteSpinner.setSelection(1, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void localExchange(final String base, final String quote, final double rate) {
|
||||||
|
exchange(new ExchangeRate() {
|
||||||
|
@Override
|
||||||
|
public String getServiceName() {
|
||||||
|
return "Local";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getBaseCurrency() {
|
||||||
|
return base;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getQuoteCurrency() {
|
||||||
|
return quote;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double getRate() {
|
||||||
|
return rate;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void execExchange(String currencyA, String currencyB) {
|
||||||
|
if (!currencyA.equals(baseCurrency) && !currencyB.equals(baseCurrency)) {
|
||||||
|
throw new IllegalStateException("I can only exchange " + baseCurrency);
|
||||||
|
}
|
||||||
|
|
||||||
|
showProgress();
|
||||||
|
|
||||||
|
Timber.d("execExchange(%s, %s)", currencyA, currencyB);
|
||||||
|
|
||||||
|
// first deal with XMR/baseCurrency & baseCurrency/XMR
|
||||||
|
|
||||||
|
if (currencyA.equals(Helper.BASE_CRYPTO) && (currencyB.equals(baseCurrency))) {
|
||||||
|
localExchange(currencyA, currencyB, 1.0d / exchangeRate);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (currencyA.equals(baseCurrency) && (currencyB.equals(Helper.BASE_CRYPTO))) {
|
||||||
|
localExchange(currencyA, currencyB, exchangeRate);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// next, deal with XMR/baseCurrency
|
||||||
|
|
||||||
|
if (currencyA.equals(baseCurrency)) {
|
||||||
|
queryExchangeRate(Helper.BASE_CRYPTO, currencyB, exchangeRate, true);
|
||||||
|
} else {
|
||||||
|
queryExchangeRate(currencyA, Helper.BASE_CRYPTO, 1.0d / exchangeRate, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void queryExchangeRate(final String base, final String quote, final double factor,
|
||||||
|
final boolean baseIsBaseCrypto) {
|
||||||
|
queryExchangeRate(base, quote,
|
||||||
|
new ExchangeCallback() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(final ExchangeRate exchangeRate) {
|
||||||
|
if (isAttachedToWindow())
|
||||||
|
new Handler(Looper.getMainLooper()).post(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
ExchangeRate xchange = new ExchangeRate() {
|
||||||
|
@Override
|
||||||
|
public String getServiceName() {
|
||||||
|
return exchangeRate.getServiceName() + "+" + baseCurrency;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getBaseCurrency() {
|
||||||
|
return baseIsBaseCrypto ? baseCurrency : base;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getQuoteCurrency() {
|
||||||
|
return baseIsBaseCrypto ? quote : baseCurrency;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double getRate() {
|
||||||
|
return exchangeRate.getRate() * factor;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
exchange(xchange);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(final Exception e) {
|
||||||
|
Timber.e(e.getLocalizedMessage());
|
||||||
|
new Handler(Looper.getMainLooper()).post(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
exchangeFailed();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@@ -44,6 +44,9 @@ import com.m2049r.xmrwallet.service.exchange.api.ExchangeCallback;
|
|||||||
import com.m2049r.xmrwallet.service.exchange.api.ExchangeRate;
|
import com.m2049r.xmrwallet.service.exchange.api.ExchangeRate;
|
||||||
import com.m2049r.xmrwallet.util.Helper;
|
import com.m2049r.xmrwallet.util.Helper;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
import timber.log.Timber;
|
import timber.log.Timber;
|
||||||
@@ -147,6 +150,15 @@ public class ExchangeView extends LinearLayout {
|
|||||||
inflater.inflate(R.layout.view_exchange, this);
|
inflater.inflate(R.layout.view_exchange, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setCurrencyAdapter(Spinner spinner) {
|
||||||
|
List<String> currencies = new ArrayList<>();
|
||||||
|
currencies.add(Helper.BASE_CRYPTO);
|
||||||
|
currencies.addAll(Arrays.asList(getResources().getStringArray(R.array.currency)));
|
||||||
|
ArrayAdapter<String> spinnerAdapter = new ArrayAdapter<>(getContext(), android.R.layout.simple_spinner_item, currencies);
|
||||||
|
spinnerAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
|
||||||
|
spinner.setAdapter(spinnerAdapter);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onFinishInflate() {
|
protected void onFinishInflate() {
|
||||||
super.onFinishInflate();
|
super.onFinishInflate();
|
||||||
@@ -161,6 +173,9 @@ public class ExchangeView extends LinearLayout {
|
|||||||
evExchange = findViewById(R.id.evExchange);
|
evExchange = findViewById(R.id.evExchange);
|
||||||
pbExchange = findViewById(R.id.pbExchange);
|
pbExchange = findViewById(R.id.pbExchange);
|
||||||
|
|
||||||
|
setCurrencyAdapter(sCurrencyA);
|
||||||
|
setCurrencyAdapter(sCurrencyB);
|
||||||
|
|
||||||
// make progress circle gray
|
// make progress circle gray
|
||||||
pbExchange.getIndeterminateDrawable().
|
pbExchange.getIndeterminateDrawable().
|
||||||
setColorFilter(getResources().getColor(R.color.trafficGray),
|
setColorFilter(getResources().getColor(R.color.trafficGray),
|
||||||
|
@@ -73,6 +73,8 @@ public interface QueryOrderStatus {
|
|||||||
|
|
||||||
String getXmrReceivingAddress(); // "xmr_receiving_address": "xmr_old_style_address_user_can_send_funds_to_as_string",
|
String getXmrReceivingAddress(); // "xmr_receiving_address": "xmr_old_style_address_user_can_send_funds_to_as_string",
|
||||||
|
|
||||||
|
String getXmrReceivingSubaddress(); // <xmr_subaddress_user_needs_to_send_funds_to_as_string>,
|
||||||
|
|
||||||
String getXmrReceivingIntegratedAddress(); // "xmr_receiving_integrated_address": "xmr_integrated_address_user_needs_to_send_funds_to_as_string",
|
String getXmrReceivingIntegratedAddress(); // "xmr_receiving_integrated_address": "xmr_integrated_address_user_needs_to_send_funds_to_as_string",
|
||||||
|
|
||||||
int getXmrRecommendedMixin(); // "xmr_recommended_mixin": <xmr_recommended_mixin_as_integer>,
|
int getXmrRecommendedMixin(); // "xmr_recommended_mixin": <xmr_recommended_mixin_as_integer>,
|
||||||
|
@@ -52,6 +52,7 @@ class QueryOrderStatusImpl implements QueryOrderStatus {
|
|||||||
private double xmrAmountRemaining; // "xmr_amount_remaining": <amount_in_xmr_that_the_user_must_still_send_as_float>,
|
private double xmrAmountRemaining; // "xmr_amount_remaining": <amount_in_xmr_that_the_user_must_still_send_as_float>,
|
||||||
private int xmrNumConfirmationsRemaining; // "xmr_num_confirmations_remaining": <num_xmr_confirmations_remaining_before_bitcoins_will_be_sent_as_integer>,
|
private int xmrNumConfirmationsRemaining; // "xmr_num_confirmations_remaining": <num_xmr_confirmations_remaining_before_bitcoins_will_be_sent_as_integer>,
|
||||||
private double xmrPriceBtc; // "xmr_price_btc": <price_of_1_btc_in_xmr_as_offered_by_service_as_float>,
|
private double xmrPriceBtc; // "xmr_price_btc": <price_of_1_btc_in_xmr_as_offered_by_service_as_float>,
|
||||||
|
private String xmrReceivingSubaddress; // <xmr_subaddress_user_needs_to_send_funds_to_as_string>,
|
||||||
private String xmrReceivingAddress; // "xmr_receiving_address": "xmr_old_style_address_user_can_send_funds_to_as_string",
|
private String xmrReceivingAddress; // "xmr_receiving_address": "xmr_old_style_address_user_can_send_funds_to_as_string",
|
||||||
private String xmrReceivingIntegratedAddress; // "xmr_receiving_integrated_address": "xmr_integrated_address_user_needs_to_send_funds_to_as_string",
|
private String xmrReceivingIntegratedAddress; // "xmr_receiving_integrated_address": "xmr_integrated_address_user_needs_to_send_funds_to_as_string",
|
||||||
private int xmrRecommendedMixin; // "xmr_recommended_mixin": <xmr_recommended_mixin_as_integer>,
|
private int xmrRecommendedMixin; // "xmr_recommended_mixin": <xmr_recommended_mixin_as_integer>,
|
||||||
@@ -115,6 +116,10 @@ class QueryOrderStatusImpl implements QueryOrderStatus {
|
|||||||
return xmrPriceBtc;
|
return xmrPriceBtc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getXmrReceivingSubaddress() {
|
||||||
|
return xmrReceivingSubaddress;
|
||||||
|
}
|
||||||
|
|
||||||
public String getXmrReceivingAddress() {
|
public String getXmrReceivingAddress() {
|
||||||
return xmrReceivingAddress;
|
return xmrReceivingAddress;
|
||||||
}
|
}
|
||||||
@@ -208,6 +213,7 @@ class QueryOrderStatusImpl implements QueryOrderStatus {
|
|||||||
xmrAmountRemaining = jsonObject.getDouble("xmr_amount_remaining"); // "xmr_amount_remaining": <amount_in_xmr_that_the_user_must_still_send_as_float>,
|
xmrAmountRemaining = jsonObject.getDouble("xmr_amount_remaining"); // "xmr_amount_remaining": <amount_in_xmr_that_the_user_must_still_send_as_float>,
|
||||||
xmrNumConfirmationsRemaining = jsonObject.getInt("xmr_num_confirmations_remaining"); // "xmr_num_confirmations_remaining": <num_xmr_confirmations_remaining_before_bitcoins_will_be_sent_as_integer>,
|
xmrNumConfirmationsRemaining = jsonObject.getInt("xmr_num_confirmations_remaining"); // "xmr_num_confirmations_remaining": <num_xmr_confirmations_remaining_before_bitcoins_will_be_sent_as_integer>,
|
||||||
xmrPriceBtc = jsonObject.getDouble("xmr_price_btc"); // "xmr_price_btc": <price_of_1_btc_in_xmr_as_offered_by_service_as_float>,
|
xmrPriceBtc = jsonObject.getDouble("xmr_price_btc"); // "xmr_price_btc": <price_of_1_btc_in_xmr_as_offered_by_service_as_float>,
|
||||||
|
xmrReceivingSubaddress = jsonObject.getString("xmr_receiving_subaddress"); // <xmr_subaddress_user_needs_to_send_funds_to_as_string>,
|
||||||
xmrReceivingAddress = jsonObject.getString("xmr_receiving_address"); // "xmr_receiving_address": "xmr_old_style_address_user_can_send_funds_to_as_string",
|
xmrReceivingAddress = jsonObject.getString("xmr_receiving_address"); // "xmr_receiving_address": "xmr_old_style_address_user_can_send_funds_to_as_string",
|
||||||
xmrReceivingIntegratedAddress = jsonObject.getString("xmr_receiving_integrated_address"); // "xmr_receiving_integrated_address": "xmr_integrated_address_user_needs_to_send_funds_to_as_string",
|
xmrReceivingIntegratedAddress = jsonObject.getString("xmr_receiving_integrated_address"); // "xmr_receiving_integrated_address": "xmr_integrated_address_user_needs_to_send_funds_to_as_string",
|
||||||
xmrRecommendedMixin = jsonObject.getInt("xmr_recommended_mixin"); // "xmr_recommended_mixin": <xmr_recommended_mixin_as_integer>,
|
xmrRecommendedMixin = jsonObject.getInt("xmr_recommended_mixin"); // "xmr_recommended_mixin": <xmr_recommended_mixin_as_integer>,
|
||||||
|
@@ -10,54 +10,43 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
|
||||||
<LinearLayout
|
<android.support.design.widget.TextInputLayout
|
||||||
|
android:id="@+id/etWalletName"
|
||||||
|
style="@style/Widget.MaterialComponents.TextInputLayout.FilledBox"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="horizontal"
|
android:layout_marginBottom="@dimen/header_top_first"
|
||||||
android:weightSum="2">
|
app:errorEnabled="true">
|
||||||
|
|
||||||
<android.support.design.widget.TextInputLayout
|
<android.support.design.widget.TextInputEditText
|
||||||
android:id="@+id/etWalletName"
|
style="@style/MoneroEdit"
|
||||||
style="@style/Widget.MaterialComponents.TextInputLayout.FilledBox"
|
android:layout_width="match_parent"
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_weight="1"
|
android:hint="@string/generate_name_hint"
|
||||||
app:counterEnabled="true"
|
android:imeOptions="actionNext"
|
||||||
app:counterMaxLength="20"
|
android:inputType="text"
|
||||||
android:layout_marginEnd="4dp"
|
android:maxLines="1"
|
||||||
app:errorEnabled="true">
|
android:textAlignment="textStart" />
|
||||||
|
</android.support.design.widget.TextInputLayout>
|
||||||
|
|
||||||
<android.support.design.widget.TextInputEditText
|
<android.support.design.widget.TextInputLayout
|
||||||
style="@style/MoneroEdit"
|
android:id="@+id/etWalletPassword"
|
||||||
android:layout_width="match_parent"
|
style="@style/Widget.MaterialComponents.TextInputLayout.FilledBox"
|
||||||
android:layout_height="wrap_content"
|
android:layout_width="match_parent"
|
||||||
android:hint="@string/generate_name_hint"
|
android:layout_height="wrap_content"
|
||||||
android:imeOptions="actionNext"
|
android:layout_marginBottom="@dimen/header_top_first"
|
||||||
android:inputType="text"
|
app:errorEnabled="true">
|
||||||
android:maxLines="1"
|
|
||||||
android:textAlignment="textStart" />
|
|
||||||
|
|
||||||
</android.support.design.widget.TextInputLayout>
|
<android.support.design.widget.TextInputEditText
|
||||||
|
style="@style/MoneroEdit"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:hint="@string/generate_password_hint"
|
||||||
|
android:imeOptions="actionNext"
|
||||||
|
android:inputType="textVisiblePassword"
|
||||||
|
android:textAlignment="textStart" />
|
||||||
|
|
||||||
<android.support.design.widget.TextInputLayout
|
</android.support.design.widget.TextInputLayout>
|
||||||
android:id="@+id/etWalletPassword"
|
|
||||||
style="@style/Widget.MaterialComponents.TextInputLayout.FilledBox"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_marginStart="4dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_weight="1">
|
|
||||||
|
|
||||||
<android.support.design.widget.TextInputEditText
|
|
||||||
style="@style/MoneroEdit"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:hint="@string/generate_password_hint"
|
|
||||||
android:imeOptions="actionNext"
|
|
||||||
android:inputType="textVisiblePassword"
|
|
||||||
android:textAlignment="textStart" />
|
|
||||||
|
|
||||||
</android.support.design.widget.TextInputLayout>
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/llFingerprintAuth"
|
android:id="@+id/llFingerprintAuth"
|
||||||
@@ -85,6 +74,7 @@
|
|||||||
style="@style/Widget.MaterialComponents.TextInputLayout.FilledBox"
|
style="@style/Widget.MaterialComponents.TextInputLayout.FilledBox"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="@dimen/header_top_first"
|
||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
app:errorEnabled="true">
|
app:errorEnabled="true">
|
||||||
|
|
||||||
@@ -103,8 +93,8 @@
|
|||||||
style="@style/Widget.MaterialComponents.TextInputLayout.FilledBox"
|
style="@style/Widget.MaterialComponents.TextInputLayout.FilledBox"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="@dimen/header_top_first"
|
||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
|
|
||||||
app:counterEnabled="true"
|
app:counterEnabled="true"
|
||||||
app:counterMaxLength="95"
|
app:counterMaxLength="95"
|
||||||
app:errorEnabled="true">
|
app:errorEnabled="true">
|
||||||
@@ -124,6 +114,7 @@
|
|||||||
style="@style/Widget.MaterialComponents.TextInputLayout.FilledBox"
|
style="@style/Widget.MaterialComponents.TextInputLayout.FilledBox"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="@dimen/header_top_first"
|
||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
app:counterEnabled="true"
|
app:counterEnabled="true"
|
||||||
app:counterMaxLength="64"
|
app:counterMaxLength="64"
|
||||||
@@ -144,6 +135,7 @@
|
|||||||
style="@style/Widget.MaterialComponents.TextInputLayout.FilledBox"
|
style="@style/Widget.MaterialComponents.TextInputLayout.FilledBox"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="@dimen/header_top_first"
|
||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
app:counterEnabled="true"
|
app:counterEnabled="true"
|
||||||
app:counterMaxLength="64"
|
app:counterMaxLength="64"
|
||||||
@@ -164,6 +156,7 @@
|
|||||||
style="@style/Widget.MaterialComponents.TextInputLayout.FilledBox"
|
style="@style/Widget.MaterialComponents.TextInputLayout.FilledBox"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginBottom="@dimen/header_top_first"
|
||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
app:errorEnabled="true">
|
app:errorEnabled="true">
|
||||||
|
|
||||||
|
@@ -108,50 +108,6 @@
|
|||||||
android:textSize="18sp"
|
android:textSize="18sp"
|
||||||
tools:text="@string/info_xmrto" />
|
tools:text="@string/info_xmrto" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<RelativeLayout
|
|
||||||
android:id="@+id/llPaymentId"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_gravity="center"
|
|
||||||
android:layout_marginBottom="4dp"
|
|
||||||
android:orientation="horizontal"
|
|
||||||
android:visibility="visible">
|
|
||||||
|
|
||||||
<android.support.design.widget.TextInputLayout
|
|
||||||
style="@style/Widget.MaterialComponents.TextInputLayout.FilledBox"
|
|
||||||
android:id="@+id/etPaymentId"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_alignParentStart="true"
|
|
||||||
android:layout_toStartOf="@+id/bPaymentId"
|
|
||||||
app:counterEnabled="true"
|
|
||||||
app:counterMaxLength="16"
|
|
||||||
app:errorEnabled="true">
|
|
||||||
|
|
||||||
<android.support.design.widget.TextInputEditText
|
|
||||||
style="@style/MoneroEdit"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_weight="10"
|
|
||||||
android:hint="@string/send_paymentid_hint"
|
|
||||||
android:imeOptions="actionNext"
|
|
||||||
android:inputType="textMultiLine"
|
|
||||||
android:textAlignment="textStart" />
|
|
||||||
</android.support.design.widget.TextInputLayout>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/bPaymentId"
|
|
||||||
style="@style/MoneroText.Button.Small"
|
|
||||||
android:layout_width="56dp"
|
|
||||||
android:layout_height="56dp"
|
|
||||||
android:layout_alignParentEnd="true"
|
|
||||||
android:layout_gravity="center"
|
|
||||||
android:layout_marginStart="8dp"
|
|
||||||
android:background="?android:selectableItemBackgroundBorderless"
|
|
||||||
android:drawableTop="@drawable/ic_settings_orange_24dp"
|
|
||||||
android:text="@string/send_generate_paymentid_hint" />
|
|
||||||
</RelativeLayout>
|
|
||||||
</FrameLayout>
|
</FrameLayout>
|
||||||
|
|
||||||
<android.support.design.widget.TextInputLayout
|
<android.support.design.widget.TextInputLayout
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
@@ -53,11 +54,12 @@
|
|||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</FrameLayout>
|
</FrameLayout>
|
||||||
|
|
||||||
<com.m2049r.xmrwallet.widget.ExchangeBtcEditText
|
<com.m2049r.xmrwallet.widget.ExchangeOtherEditText
|
||||||
android:id="@+id/etAmount"
|
android:id="@+id/etAmount"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginBottom="16dp"
|
|
||||||
android:layout_marginTop="16dp"
|
android:layout_marginTop="16dp"
|
||||||
android:orientation="vertical" />
|
android:layout_marginBottom="16dp"
|
||||||
|
android:orientation="vertical"
|
||||||
|
app:baseSymbol="BTC" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
@@ -107,27 +107,6 @@
|
|||||||
android:textAlignment="textStart"
|
android:textAlignment="textStart"
|
||||||
tools:text="4AdkPJoxn7JCvAby9szgnt93MSEwdnxdhaASxbTBm6x5dCwmsDep2UYN4FhStDn5i11nsJbpU7oj59ahg8gXb1Mg3viqCuk" />
|
tools:text="4AdkPJoxn7JCvAby9szgnt93MSEwdnxdhaASxbTBm6x5dCwmsDep2UYN4FhStDn5i11nsJbpU7oj59ahg8gXb1Mg3viqCuk" />
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="8dp"
|
|
||||||
android:orientation="horizontal">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
style="@style/MoneroText.Confirm.Label"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="@string/label_send_payment_id" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/tvTxPaymentId"
|
|
||||||
style="@style/MoneroText.Confirm"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="8dp"
|
|
||||||
tools:text="d666a38d4a28fb38" />
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<android.support.v7.widget.CardView xmlns:card_view="http://schemas.android.com/apk/res-auto"
|
<android.support.v7.widget.CardView xmlns:card_view="http://schemas.android.com/apk/res-auto"
|
||||||
android:id="@+id/cvXmrTo"
|
android:id="@+id/cvXmrTo"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
@@ -26,29 +26,6 @@
|
|||||||
android:textAlignment="textStart"
|
android:textAlignment="textStart"
|
||||||
tools:text="4AdkPJoxn7JCvAby9szgnt93MSEwdnxdhaASxbTBm6x5dCwmsDep2UYN4FhStDn5i11nsJbpU7oj59ahg8gXb1Mg3viqCuk" />
|
tools:text="4AdkPJoxn7JCvAby9szgnt93MSEwdnxdhaASxbTBm6x5dCwmsDep2UYN4FhStDn5i11nsJbpU7oj59ahg8gXb1Mg3viqCuk" />
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="4dp"
|
|
||||||
android:orientation="horizontal">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
style="@style/MoneroText.Confirm.Label"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="@string/label_send_payment_id"
|
|
||||||
android:textAlignment="textStart" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/tvTxPaymentId"
|
|
||||||
style="@style/MoneroText.Confirm"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="8dp"
|
|
||||||
android:textAlignment="textStart"
|
|
||||||
tools:text="d666a38d4a28fb38" />
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user