1
mirror of https://github.com/m2049r/xmrwallet synced 2025-09-03 08:23:04 +02:00

Compare commits

...

6 Commits

Author SHA1 Message Date
m2049r
e56369b91a fix tests (#717) 2021-02-19 17:25:11 +01:00
m2049r
b04aa24269 bump version 2021-02-19 16:35:02 +01:00
m2049r
dda86bd5de fix URI generation (#716) 2021-02-19 16:32:15 +01:00
m2049r
a19ad7fd52 change build to docker (#715) 2021-02-19 16:31:35 +01:00
m2049r
0443fd808c bump version (consolidating with play release) 2021-02-17 00:16:24 +01:00
m2049r
faf57c96fc add more output currencies (#714) 2021-02-15 21:38:26 +01:00
122 changed files with 2547 additions and 2005 deletions

View File

@@ -13,7 +13,7 @@ set(EXTERNAL_LIBS_DIR ${CMAKE_SOURCE_DIR}/../external-libs)
add_library(sodium STATIC IMPORTED)
set_target_properties(sodium PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/libsodium/lib/${ANDROID_ABI}/libsodium.a)
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/libsodium.a)
############
# OpenSSL
@@ -21,11 +21,11 @@ set_target_properties(sodium PROPERTIES IMPORTED_LOCATION
add_library(crypto STATIC IMPORTED)
set_target_properties(crypto PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/openssl/lib/${ANDROID_ABI}/libcrypto.a)
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/libcrypto.a)
add_library(ssl STATIC IMPORTED)
set_target_properties(ssl PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/openssl/lib/${ANDROID_ABI}/libssl.a)
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/libssl.a)
############
# Boost
@@ -33,39 +33,39 @@ set_target_properties(ssl PROPERTIES IMPORTED_LOCATION
add_library(boost_chrono STATIC IMPORTED)
set_target_properties(boost_chrono PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/boost/lib/${ANDROID_ABI}/libboost_chrono.a)
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/libboost_chrono.a)
add_library(boost_date_time STATIC IMPORTED)
set_target_properties(boost_date_time PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/boost/lib/${ANDROID_ABI}/libboost_date_time.a)
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/libboost_date_time.a)
add_library(boost_filesystem STATIC IMPORTED)
set_target_properties(boost_filesystem PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/boost/lib/${ANDROID_ABI}/libboost_filesystem.a)
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/libboost_filesystem.a)
add_library(boost_program_options STATIC IMPORTED)
set_target_properties(boost_program_options PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/boost/lib/${ANDROID_ABI}/libboost_program_options.a)
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/libboost_program_options.a)
add_library(boost_regex STATIC IMPORTED)
set_target_properties(boost_regex PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/boost/lib/${ANDROID_ABI}/libboost_regex.a)
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/libboost_regex.a)
add_library(boost_serialization STATIC IMPORTED)
set_target_properties(boost_serialization PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/boost/lib/${ANDROID_ABI}/libboost_serialization.a)
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/libboost_serialization.a)
add_library(boost_system STATIC IMPORTED)
set_target_properties(boost_system PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/boost/lib/${ANDROID_ABI}/libboost_system.a)
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/libboost_system.a)
add_library(boost_thread STATIC IMPORTED)
set_target_properties(boost_thread PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/boost/lib/${ANDROID_ABI}/libboost_thread.a)
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/libboost_thread.a)
add_library(boost_wserialization STATIC IMPORTED)
set_target_properties(boost_wserialization PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/boost/lib/${ANDROID_ABI}/libboost_wserialization.a)
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/libboost_wserialization.a)
#############
# Monero
@@ -73,99 +73,103 @@ set_target_properties(boost_wserialization PROPERTIES IMPORTED_LOCATION
add_library(wallet_api STATIC IMPORTED)
set_target_properties(wallet_api PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libwallet_api.a)
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/monero/libwallet_api.a)
add_library(wallet STATIC IMPORTED)
set_target_properties(wallet PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libwallet.a)
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/monero/libwallet.a)
add_library(cryptonote_core STATIC IMPORTED)
set_target_properties(cryptonote_core PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libcryptonote_core.a)
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/monero/libcryptonote_core.a)
add_library(cryptonote_basic STATIC IMPORTED)
set_target_properties(cryptonote_basic PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libcryptonote_basic.a)
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/monero/libcryptonote_basic.a)
add_library(mnemonics STATIC IMPORTED)
set_target_properties(mnemonics PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libmnemonics.a)
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/monero/libmnemonics.a)
add_library(common STATIC IMPORTED)
set_target_properties(common PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libcommon.a)
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/monero/libcommon.a)
add_library(cncrypto STATIC IMPORTED)
set_target_properties(cncrypto PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libcncrypto.a)
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/monero/libcncrypto.a)
add_library(ringct STATIC IMPORTED)
set_target_properties(ringct PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libringct.a)
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/monero/libringct.a)
add_library(ringct_basic STATIC IMPORTED)
set_target_properties(ringct_basic PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libringct_basic.a)
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/monero/libringct_basic.a)
add_library(blockchain_db STATIC IMPORTED)
set_target_properties(blockchain_db PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libblockchain_db.a)
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/monero/libblockchain_db.a)
add_library(lmdb STATIC IMPORTED)
set_target_properties(lmdb PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/liblmdb.a)
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/monero/liblmdb.a)
add_library(easylogging STATIC IMPORTED)
set_target_properties(easylogging PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libeasylogging.a)
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/monero/libeasylogging.a)
add_library(unbound STATIC IMPORTED)
set_target_properties(unbound PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libunbound.a)
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/monero/libunbound.a)
add_library(epee STATIC IMPORTED)
set_target_properties(epee PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libepee.a)
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/monero/libepee.a)
add_library(blocks STATIC IMPORTED)
set_target_properties(blocks PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libblocks.a)
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/monero/libblocks.a)
add_library(checkpoints STATIC IMPORTED)
set_target_properties(checkpoints PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libcheckpoints.a)
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/monero/libcheckpoints.a)
add_library(device STATIC IMPORTED)
set_target_properties(device PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libdevice.a)
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/monero/libdevice.a)
add_library(device_trezor STATIC IMPORTED)
set_target_properties(device_trezor PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libdevice_trezor.a)
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/monero/libdevice_trezor.a)
add_library(multisig STATIC IMPORTED)
set_target_properties(multisig PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libmultisig.a)
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/monero/libmultisig.a)
add_library(version STATIC IMPORTED)
set_target_properties(version PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libversion.a)
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/monero/libversion.a)
add_library(net STATIC IMPORTED)
set_target_properties(net PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libnet.a)
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/monero/libnet.a)
add_library(hardforks STATIC IMPORTED)
set_target_properties(hardforks PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/libhardforks.a)
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/monero/libhardforks.a)
add_library(randomx STATIC IMPORTED)
set_target_properties(randomx PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/librandomx.a)
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/monero/librandomx.a)
add_library(rpc_base STATIC IMPORTED)
set_target_properties(rpc_base PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/monero/lib/${ANDROID_ABI}/librpc_base.a)
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/monero/librpc_base.a)
add_library(wallet-crypto STATIC IMPORTED)
set_target_properties(wallet-crypto PROPERTIES IMPORTED_LOCATION
${EXTERNAL_LIBS_DIR}/${ANDROID_ABI}/monero/libwallet-crypto.a)
#############
# System
@@ -173,10 +177,16 @@ set_target_properties(rpc_base PROPERTIES IMPORTED_LOCATION
find_library( log-lib log )
include_directories( ${EXTERNAL_LIBS_DIR}/monero/include )
include_directories( ${EXTERNAL_LIBS_DIR}/include )
message(STATUS EXTERNAL_LIBS_DIR : ${EXTERNAL_LIBS_DIR})
if(${ANDROID_ABI} STREQUAL "x86_64")
set(EXTRA_LIBS "wallet-crypto")
else()
set(EXTRA_LIBS "")
endif()
target_link_libraries( monerujo
wallet_api
@@ -203,6 +213,7 @@ target_link_libraries( monerujo
randomx
hardforks
rpc_base
${EXTRA_LIBS}
boost_chrono
boost_date_time

View File

@@ -7,8 +7,8 @@ android {
applicationId "com.m2049r.xmrwallet"
minSdkVersion 21
targetSdkVersion 29
versionCode 702
versionName "1.17.2 'Druk'"
versionCode 707
versionName "1.17.7 'Druk'"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
externalNativeBuild {
cmake {

View File

@@ -213,7 +213,7 @@ public class BaseActivity extends SecureActivity implements GenerateReviewFragme
if (uri == null) {
Toast.makeText(this, getString(R.string.nfc_tag_read_undef), Toast.LENGTH_LONG).show();
} else {
BarcodeData bc = BarcodeData.fromQrCode(uri.toString());
BarcodeData bc = BarcodeData.fromString(uri.toString());
if (bc == null)
Toast.makeText(this, getString(R.string.nfc_tag_read_undef), Toast.LENGTH_LONG).show();
else

View File

@@ -58,6 +58,7 @@ import com.google.zxing.qrcode.QRCodeWriter;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import com.m2049r.xmrwallet.BuildConfig;
import com.m2049r.xmrwallet.data.BarcodeData;
import com.m2049r.xmrwallet.data.Crypto;
import com.m2049r.xmrwallet.ledger.LedgerProgressDialog;
import com.m2049r.xmrwallet.model.Wallet;
import com.m2049r.xmrwallet.model.WalletManager;
@@ -469,7 +470,7 @@ public class ReceiveFragment extends Fragment {
Timber.d("CLEARQR");
return;
}
bcData = new BarcodeData(BarcodeData.Asset.XMR, address, notes, xmrAmount);
bcData = new BarcodeData(Crypto.XMR, address, notes, xmrAmount);
int size = Math.max(ivQrCode.getWidth(), ivQrCode.getHeight());
Bitmap qr = generate(bcData.getUriString(), size, size);
if (qr != null) {

View File

@@ -73,14 +73,7 @@ public class ScannerFragment extends Fragment implements ZXingScannerView.Result
// * On older devices continuously stopping and resuming camera preview can result in freezing the app.
// * I don't know why this is the case but I don't have the time to figure out.
Handler handler = new Handler();
handler.postDelayed(new
Runnable() {
@Override
public void run() {
mScannerView.resumeCameraPreview(ScannerFragment.this);
}
}, 2000);
handler.postDelayed(() -> mScannerView.resumeCameraPreview(ScannerFragment.this), 2000);
}
@Override

View File

@@ -300,7 +300,7 @@ public class TxFragment extends Fragment {
}
tvTxXmrToKey.setText(key);
tvDestinationBtc.setText(userNotes.xmrtoDestination);
tvTxAmountBtc.setText(userNotes.xmrtoAmount + " BTC");
tvTxAmountBtc.setText(userNotes.xmrtoAmount + " "+ userNotes.xmrtoCurrency);
switch (userNotes.xmrtoTag) {
case "xmrto":
tvXmrToSupport.setVisibility(View.GONE);

View File

@@ -921,7 +921,7 @@ public class WalletActivity extends BaseActivity implements WalletFragment.Liste
@Override
public boolean onScanned(String qrCode) {
// #gurke
BarcodeData bcData = BarcodeData.fromQrCode(qrCode);
BarcodeData bcData = BarcodeData.fromString(qrCode);
if (bcData != null) {
popFragmentStack(null);
Timber.d("AAA");

View File

@@ -49,6 +49,7 @@ 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 com.m2049r.xmrwallet.util.ServiceHelper;
import com.m2049r.xmrwallet.widget.Toolbar;
import java.text.NumberFormat;
@@ -242,7 +243,7 @@ public class WalletFragment extends Fragment
String balanceCurrency = Helper.BASE_CRYPTO;
double balanceRate = 1.0;
private final ExchangeApi exchangeApi = Helper.getExchangeApi();
private final ExchangeApi exchangeApi = ServiceHelper.getExchangeApi();
void refreshBalance() {
double unconfirmedXmr = Helper.getDecimalAmount(balance - unlockedBalance).doubleValue();

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,89 @@
package com.m2049r.xmrwallet.data;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.m2049r.xmrwallet.R;
import com.m2049r.xmrwallet.model.Wallet;
import com.m2049r.xmrwallet.util.validator.BitcoinAddressType;
import com.m2049r.xmrwallet.util.validator.BitcoinAddressValidator;
import com.m2049r.xmrwallet.util.validator.EthAddressValidator;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor
public enum Crypto {
XMR("XMR", true, "monero:tx_amount:recipient_name:tx_description", R.id.ibXMR, R.drawable.ic_monero, R.drawable.ic_monero_bw, Wallet::isAddressValid),
BTC("BTC", true, "bitcoin:amount:label:message", R.id.ibBTC, R.drawable.ic_xmrto_btc, R.drawable.ic_xmrto_btc_off, address -> {
return BitcoinAddressValidator.validate(address, BitcoinAddressType.BTC);
}),
DASH("DASH", true, "dash:amount:label:message", R.id.ibDASH, R.drawable.ic_xmrto_dash, R.drawable.ic_xmrto_dash_off, address -> {
return BitcoinAddressValidator.validate(address, BitcoinAddressType.DASH);
}),
DOGE("DOGE", true, "dogecoin:amount:label:message", R.id.ibDOGE, R.drawable.ic_xmrto_doge, R.drawable.ic_xmrto_doge_off, address -> {
return BitcoinAddressValidator.validate(address, BitcoinAddressType.DOGE);
}),
ETH("ETH", false, "ethereum:amount:label:message", R.id.ibETH, R.drawable.ic_xmrto_eth, R.drawable.ic_xmrto_eth_off, EthAddressValidator::validate),
LTC("LTC", true, "litecoin:amount:label:message", R.id.ibLTC, R.drawable.ic_xmrto_ltc, R.drawable.ic_xmrto_ltc_off, address -> {
return BitcoinAddressValidator.validate(address, BitcoinAddressType.LTC);
});
@Getter
@NonNull
private final String symbol;
@Getter
private final boolean casefull;
@NonNull
private final String uriSpec;
@Getter
private final int buttonId;
@Getter
private final int iconEnabledId;
@Getter
private final int iconDisabledId;
@NonNull
private final Validator validator;
@Nullable
public static Crypto withScheme(@NonNull String scheme) {
for (Crypto crypto : values()) {
if (crypto.getUriScheme().equals(scheme)) return crypto;
}
return null;
}
@Nullable
public static Crypto withSymbol(@NonNull String symbol) {
final String upperSymbol = symbol.toUpperCase();
for (Crypto crypto : values()) {
if (crypto.symbol.equals(upperSymbol)) return crypto;
}
return null;
}
interface Validator {
boolean validate(String address);
}
// TODO maybe cache these segments
String getUriScheme() {
return uriSpec.split(":")[0];
}
String getUriAmount() {
return uriSpec.split(":")[1];
}
String getUriLabel() {
return uriSpec.split(":")[2];
}
String getUriMessage() {
return uriSpec.split(":")[3];
}
boolean validate(String address) {
return validator.validate(address);
}
}

View File

@@ -24,6 +24,9 @@ import lombok.Getter;
import lombok.Setter;
public class TxDataBtc extends TxData {
@Getter
@Setter
private String btcSymbol; // the actual non-XMR thing we're sending
@Getter
@Setter
private String xmrtoOrderId; // shown in success screen
@@ -45,6 +48,7 @@ public class TxDataBtc extends TxData {
@Override
public void writeToParcel(Parcel out, int flags) {
super.writeToParcel(out, flags);
out.writeString(btcSymbol);
out.writeString(xmrtoOrderId);
out.writeString(btcAddress);
out.writeDouble(btcAmount);
@@ -63,6 +67,7 @@ public class TxDataBtc extends TxData {
protected TxDataBtc(Parcel in) {
super(in);
btcSymbol = in.readString();
xmrtoOrderId = in.readString();
btcAddress = in.readString();
btcAmount = in.readDouble();
@@ -74,10 +79,23 @@ public class TxDataBtc extends TxData {
StringBuilder sb = new StringBuilder();
sb.append("xmrtoOrderId:");
sb.append(xmrtoOrderId);
sb.append(",btcSymbol:");
sb.append(btcSymbol);
sb.append(",btcAddress:");
sb.append(btcAddress);
sb.append(",btcAmount:");
sb.append(btcAmount);
return sb.toString();
}
public boolean validateAddress(@NonNull String address) {
if ((btcSymbol == null) || (btcAddress == null)) return false;
final Crypto crypto = Crypto.withSymbol(btcSymbol);
if (crypto == null) return false;
if (crypto.isCasefull()) { // compare as-is
return address.equals(btcAddress);
} else { // normalize & compare (e.g. ETH with and without checksum capitals
return address.toLowerCase().equals(btcAddress.toLowerCase());
}
}
}

View File

@@ -28,6 +28,7 @@ public class UserNotes {
public String xmrtoTag = null;
public String xmrtoKey = null;
public String xmrtoAmount = null; // could be a double - but we are not doing any calculations
public String xmrtoCurrency = null;
public String xmrtoDestination = null;
public UserNotes(final String txNotes) {
@@ -35,14 +36,15 @@ public class UserNotes {
return;
}
this.txNotes = txNotes;
Pattern p = Pattern.compile("^\\{([a-z]+)-(\\w{6,}),([0-9.]*)BTC,(\\w*)\\} ?(.*)");
Pattern p = Pattern.compile("^\\{([a-z]+)-(\\w{6,}),([0-9.]*)([A-Z]+),(\\w*)\\} ?(.*)");
Matcher m = p.matcher(txNotes);
if (m.find()) {
xmrtoTag = m.group(1);
xmrtoKey = m.group(2);
xmrtoAmount = m.group(3);
xmrtoDestination = m.group(4);
note = m.group(5);
xmrtoCurrency = m.group(4);
xmrtoDestination = m.group(5);
note = m.group(6);
} else {
note = txNotes;
}
@@ -62,6 +64,7 @@ public class UserNotes {
xmrtoTag = order.TAG;
xmrtoKey = order.getOrderId();
xmrtoAmount = Helper.getDisplayAmount(order.getBtcAmount());
xmrtoCurrency = order.getBtcCurrency();
xmrtoDestination = order.getBtcAddress();
} else {
xmrtoTag = null;
@@ -83,7 +86,8 @@ public class UserNotes {
sb.append(xmrtoKey);
sb.append(",");
sb.append(xmrtoAmount);
sb.append("BTC,");
sb.append(xmrtoCurrency);
sb.append(",");
sb.append(xmrtoDestination);
sb.append("}");
if ((note != null) && (!note.isEmpty()))

View File

@@ -42,9 +42,8 @@ public class SendAmountWizardFragment extends SendWizardFragment {
Listener sendListener;
public SendAmountWizardFragment setSendListener(Listener listener) {
public void setSendListener(Listener listener) {
this.sendListener = listener;
return this;
}
interface Listener {

View File

@@ -28,16 +28,16 @@ import com.m2049r.xmrwallet.R;
import com.m2049r.xmrwallet.data.BarcodeData;
import com.m2049r.xmrwallet.data.TxDataBtc;
import com.m2049r.xmrwallet.model.Wallet;
import com.m2049r.xmrwallet.util.Helper;
import com.m2049r.xmrwallet.util.OkHttpHelper;
import com.m2049r.xmrwallet.widget.ExchangeOtherEditText;
import com.m2049r.xmrwallet.widget.SendProgressView;
import com.m2049r.xmrwallet.service.shift.ShiftCallback;
import com.m2049r.xmrwallet.service.shift.ShiftError;
import com.m2049r.xmrwallet.service.shift.ShiftException;
import com.m2049r.xmrwallet.service.shift.sideshift.api.QueryOrderParameters;
import com.m2049r.xmrwallet.service.shift.sideshift.api.SideShiftApi;
import com.m2049r.xmrwallet.service.shift.ShiftCallback;
import com.m2049r.xmrwallet.service.shift.sideshift.network.SideShiftApiImpl;
import com.m2049r.xmrwallet.util.OkHttpHelper;
import com.m2049r.xmrwallet.util.ServiceHelper;
import com.m2049r.xmrwallet.widget.ExchangeOtherEditText;
import com.m2049r.xmrwallet.widget.SendProgressView;
import java.text.NumberFormat;
import java.util.Locale;
@@ -85,6 +85,7 @@ public class SendBtcAmountWizardFragment extends SendWizardFragment {
etAmount = view.findViewById(R.id.etAmount);
etAmount.requestFocus();
return view;
}
@@ -130,20 +131,26 @@ public class SendBtcAmountWizardFragment extends SendWizardFragment {
public void onResumeFragment() {
super.onResumeFragment();
Timber.d("onResumeFragment()");
final String btcSymbol = ((TxDataBtc) sendListener.getTxData()).getBtcSymbol();
if (!btcSymbol.toLowerCase().equals(ServiceHelper.ASSET))
throw new IllegalStateException("Asset Symbol is wrong!");
final long funds = getTotalFunds();
if (!sendListener.getActivityCallback().isStreetMode()) {
tvFunds.setText(getString(R.string.send_available,
Wallet.getDisplayAmount(funds)));
//TODO
} else {
tvFunds.setText(getString(R.string.send_available,
getString(R.string.unknown_amount)));
}
etAmount.setAmount("");
final BarcodeData data = sendListener.popBarcodeData();
if (data != null) {
if (data.amount != null) {
etAmount.setAmount(data.amount);
}
}
etAmount.setBaseCurrency(btcSymbol);
callXmrTo();
}
@@ -166,7 +173,9 @@ public class SendBtcAmountWizardFragment extends SendWizardFragment {
String min = df.format(minBtc);
String max = df.format(maxBtc);
String rate = df.format(price);
Spanned xmrParmText = Html.fromHtml(getString(R.string.info_send_xmrto_parms, min, max, rate));
final TxDataBtc txDataBtc = (TxDataBtc) sendListener.getTxData();
Spanned xmrParmText = Html.fromHtml(getString(R.string.info_send_xmrto_parms,
min, max, rate, txDataBtc.getBtcSymbol()));
tvXmrToParms.setText(xmrParmText);
final long funds = getTotalFunds();
@@ -183,7 +192,8 @@ public class SendBtcAmountWizardFragment extends SendWizardFragment {
}
tvFunds.setText(getString(R.string.send_available_btc,
availXmrString,
availBtcString));
availBtcString,
((TxDataBtc) sendListener.getTxData()).getBtcSymbol()));
llXmrToParms.setVisibility(View.VISIBLE);
evParams.hideProgress();
});
@@ -246,7 +256,7 @@ public class SendBtcAmountWizardFragment extends SendWizardFragment {
synchronized (this) {
if (xmrToApi == null) {
xmrToApi = new SideShiftApiImpl(OkHttpHelper.getOkHttpClient(),
Helper.getXmrToBaseUrl());
ServiceHelper.getXmrToBaseUrl());
}
}
}

View File

@@ -29,16 +29,17 @@ import com.m2049r.xmrwallet.data.TxData;
import com.m2049r.xmrwallet.data.TxDataBtc;
import com.m2049r.xmrwallet.model.PendingTransaction;
import com.m2049r.xmrwallet.model.Wallet;
import com.m2049r.xmrwallet.util.Helper;
import com.m2049r.xmrwallet.util.OkHttpHelper;
import com.m2049r.xmrwallet.widget.SendProgressView;
import com.m2049r.xmrwallet.service.shift.ShiftCallback;
import com.m2049r.xmrwallet.service.shift.ShiftError;
import com.m2049r.xmrwallet.service.shift.ShiftException;
import com.m2049r.xmrwallet.service.shift.sideshift.api.CreateOrder;
import com.m2049r.xmrwallet.service.shift.sideshift.api.RequestQuote;
import com.m2049r.xmrwallet.service.shift.sideshift.api.SideShiftApi;
import com.m2049r.xmrwallet.service.shift.ShiftCallback;
import com.m2049r.xmrwallet.service.shift.sideshift.network.SideShiftApiImpl;
import com.m2049r.xmrwallet.util.Helper;
import com.m2049r.xmrwallet.util.OkHttpHelper;
import com.m2049r.xmrwallet.util.ServiceHelper;
import com.m2049r.xmrwallet.widget.SendProgressView;
import java.text.NumberFormat;
import java.util.Locale;
@@ -67,6 +68,7 @@ public class SendBtcConfirmWizardFragment extends SendWizardFragment implements
private TextView tvTxBtcAmount;
private TextView tvTxBtcRate;
private TextView tvTxBtcAddress;
private TextView tvTxBtcAddressLabel;
private TextView tvTxXmrToKey;
private TextView tvTxFee;
private TextView tvTxTotal;
@@ -84,6 +86,7 @@ public class SendBtcConfirmWizardFragment extends SendWizardFragment implements
R.layout.fragment_send_btc_confirm, container, false);
tvTxBtcAddress = view.findViewById(R.id.tvTxBtcAddress);
tvTxBtcAddressLabel = view.findViewById(R.id.tvTxBtcAddressLabel);
tvTxBtcAmount = view.findViewById(R.id.tvTxBtcAmount);
tvTxBtcRate = view.findViewById(R.id.tvTxBtcRate);
tvTxXmrToKey = view.findViewById(R.id.tvTxXmrToKey);
@@ -259,6 +262,8 @@ public class SendBtcConfirmWizardFragment extends SendWizardFragment implements
if (sendListener.getMode() != SendFragment.Mode.BTC) {
throw new IllegalStateException("Mode is not BTC!");
}
if (!((TxDataBtc) sendListener.getTxData()).getBtcSymbol().toLowerCase().equals(ServiceHelper.ASSET))
throw new IllegalStateException("Asset Symbol is wrong!");
Helper.hideKeyboard(getActivity());
llStageA.setVisibility(View.INVISIBLE);
evStageA.hideProgress();
@@ -392,9 +397,10 @@ public class SendBtcConfirmWizardFragment extends SendWizardFragment implements
df.setMaximumFractionDigits(12);
final String btcAmount = df.format(xmrtoQuote.getBtcAmount());
final String xmrAmountTotal = df.format(xmrtoQuote.getXmrAmount());
tvTxBtcAmount.setText(getString(R.string.text_send_btc_amount, btcAmount, xmrAmountTotal));
tvTxBtcAmount.setText(getString(R.string.text_send_btc_amount,
btcAmount, xmrAmountTotal, txDataBtc.getBtcSymbol()));
final String xmrPriceBtc = df.format(xmrtoQuote.getPrice());
tvTxBtcRate.setText(getString(R.string.text_send_btc_rate, xmrPriceBtc));
tvTxBtcRate.setText(getString(R.string.text_send_btc_rate, xmrPriceBtc, txDataBtc.getBtcSymbol()));
hideProgress();
});
stageB(requestQuote.getId());
@@ -477,13 +483,14 @@ public class SendBtcConfirmWizardFragment extends SendWizardFragment implements
TxDataBtc txDataBtc = (TxDataBtc) sendListener.getTxData();
// verify amount & destination
if ((order.getBtcAmount() != txDataBtc.getBtcAmount())
|| (!order.getBtcAddress().equals(txDataBtc.getBtcAddress()))) {
|| (!txDataBtc.validateAddress(order.getBtcAddress()))) {
throw new IllegalStateException("Order does not fulfill quote!"); // something is terribly wrong - die
}
xmrtoOrder = order;
getView().post(() -> {
tvTxXmrToKey.setText(order.getOrderId());
tvTxBtcAddress.setText(order.getBtcAddress());
tvTxBtcAddressLabel.setText(getString(R.string.label_send_btc_address, txDataBtc.getBtcSymbol()));
hideProgress();
Timber.d("Expires @ %s", order.getExpiresAt().toString());
final int timeout = (int) (order.getExpiresAt().getTime() - order.getCreatedAt().getTime()) / 1000 - 60; // -1 minute buffer
@@ -561,7 +568,7 @@ public class SendBtcConfirmWizardFragment extends SendWizardFragment implements
synchronized (this) {
if (xmrToApi == null) {
xmrToApi = new SideShiftApiImpl(OkHttpHelper.getOkHttpClient(),
Helper.getXmrToBaseUrl());
ServiceHelper.getXmrToBaseUrl());
}
}
}

View File

@@ -30,6 +30,7 @@ import android.widget.TextView;
import android.widget.Toast;
import com.m2049r.xmrwallet.R;
import com.m2049r.xmrwallet.data.Crypto;
import com.m2049r.xmrwallet.data.PendingTx;
import com.m2049r.xmrwallet.data.TxDataBtc;
import com.m2049r.xmrwallet.service.shift.ShiftCallback;
@@ -39,6 +40,7 @@ import com.m2049r.xmrwallet.service.shift.sideshift.api.SideShiftApi;
import com.m2049r.xmrwallet.service.shift.sideshift.network.SideShiftApiImpl;
import com.m2049r.xmrwallet.util.Helper;
import com.m2049r.xmrwallet.util.OkHttpHelper;
import com.m2049r.xmrwallet.util.ServiceHelper;
import java.text.NumberFormat;
import java.util.Locale;
@@ -62,10 +64,10 @@ public class SendBtcSuccessWizardFragment extends SendWizardFragment {
ImageButton bCopyTxId;
private TextView tvTxId;
private TextView tvTxAddress;
private TextView tvTxPaymentId;
private TextView tvTxAmount;
private TextView tvTxFee;
private TextView tvXmrToAmount;
private ImageView ivXmrToIcon;
private TextView tvXmrToStatus;
private ImageView ivXmrToStatus;
private ImageView ivXmrToStatusBig;
@@ -90,13 +92,13 @@ public class SendBtcSuccessWizardFragment extends SendWizardFragment {
});
tvXmrToAmount = view.findViewById(R.id.tvXmrToAmount);
ivXmrToIcon = view.findViewById(R.id.ivXmrToIcon);
tvXmrToStatus = view.findViewById(R.id.tvXmrToStatus);
ivXmrToStatus = view.findViewById(R.id.ivXmrToStatus);
ivXmrToStatusBig = view.findViewById(R.id.ivXmrToStatusBig);
tvTxId = view.findViewById(R.id.tvTxId);
tvTxAddress = view.findViewById(R.id.tvTxAddress);
tvTxPaymentId = view.findViewById(R.id.tvTxPaymentId);
tvTxAmount = view.findViewById(R.id.tvTxAmount);
tvTxFee = view.findViewById(R.id.tvTxFee);
@@ -150,9 +152,11 @@ public class SendBtcSuccessWizardFragment extends SendWizardFragment {
NumberFormat df = NumberFormat.getInstance(Locale.US);
df.setMaximumFractionDigits(12);
String btcAmount = df.format(btcData.getBtcAmount());
tvXmrToAmount.setText(getString(R.string.info_send_xmrto_success_btc, btcAmount));
tvXmrToAmount.setText(getString(R.string.info_send_xmrto_success_btc, btcAmount, btcData.getBtcSymbol()));
//TODO btcData.getBtcAddress();
tvTxXmrToKey.setText(btcData.getXmrtoOrderId());
final Crypto crypto = Crypto.withSymbol(btcData.getBtcSymbol());
ivXmrToIcon.setImageResource(crypto.getIconEnabledId());
tvXmrToSupport.setOnClickListener(v -> {
Uri orderUri = getXmrToApi().getQueryOrderUri(btcData.getXmrtoOrderId());
Intent intent = new Intent(Intent.ACTION_VIEW, orderUri);
@@ -211,7 +215,7 @@ public class SendBtcSuccessWizardFragment extends SendWizardFragment {
statusResource = R.drawable.ic_error_red_24dp;
pbXmrto.getIndeterminateDrawable().setColorFilter(0xff8b0000, android.graphics.PorterDuff.Mode.MULTIPLY);
} else if (status.isSent() || status.isPaid()) {
tvXmrToStatus.setText(getString(R.string.info_send_xmrto_sent));
tvXmrToStatus.setText(getString(R.string.info_send_xmrto_sent, btcData.getBtcSymbol()));
statusResource = R.drawable.ic_success_green_24dp;
pbXmrto.getIndeterminateDrawable().setColorFilter(0xFF417505, android.graphics.PorterDuff.Mode.MULTIPLY);
} else if (status.isWaiting()) {
@@ -228,6 +232,7 @@ public class SendBtcSuccessWizardFragment extends SendWizardFragment {
ivXmrToStatus.setImageResource(statusResource);
if (status.isTerminal()) {
pbXmrto.setVisibility(View.INVISIBLE);
ivXmrToIcon.setVisibility(View.GONE);
ivXmrToStatus.setVisibility(View.GONE);
ivXmrToStatusBig.setImageResource(statusResource);
ivXmrToStatusBig.setVisibility(View.VISIBLE);
@@ -241,7 +246,7 @@ public class SendBtcSuccessWizardFragment extends SendWizardFragment {
synchronized (this) {
if (xmrToApi == null) {
xmrToApi = new SideShiftApiImpl(OkHttpHelper.getOkHttpClient(),
Helper.getXmrToBaseUrl());
ServiceHelper.getXmrToBaseUrl());
}
}
}

View File

@@ -29,6 +29,7 @@ import android.view.ViewGroup;
import android.widget.Button;
import android.widget.EditText;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
@@ -92,8 +93,6 @@ public class SendFragment extends Fragment
void setOnUriScannedListener(OnUriScannedListener onUriScannedListener);
}
private EditText etDummy;
private View llNavBar;
private DotBar dotBar;
private Button bPrev;
@@ -101,7 +100,7 @@ public class SendFragment extends Fragment
private Button bDone;
static private int MAX_FALLBACK = Integer.MAX_VALUE;
static private final int MAX_FALLBACK = Integer.MAX_VALUE;
public static SendFragment newInstance(String uri) {
SendFragment f = new SendFragment();
@@ -166,28 +165,18 @@ public class SendFragment extends Fragment
}
});
bPrev.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
spendViewPager.previous();
}
});
bPrev.setOnClickListener(v -> spendViewPager.previous());
bNext.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
spendViewPager.next();
}
});
bNext.setOnClickListener(v -> spendViewPager.next());
bDone.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
Timber.d("bDone.onClick");
activityCallback.onFragmentDone();
}
bDone.setOnClickListener(v -> {
Timber.d("bDone.onClick");
activityCallback.onFragmentDone();
});
updatePosition(0);
etDummy = view.findViewById(R.id.etDummy);
final EditText etDummy = view.findViewById(R.id.etDummy);
etDummy.setRawInputType(InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
etDummy.requestFocus();
Helper.hideKeyboard(getActivity());
@@ -197,7 +186,7 @@ public class SendFragment extends Fragment
String uri = args.getString(WalletActivity.REQUEST_URI);
Timber.d("URI: %s", uri);
if (uri != null) {
barcodeData = BarcodeData.fromQrCode(uri);
barcodeData = BarcodeData.fromString(uri);
Timber.d("barcodeData: %s", barcodeData != null ? barcodeData.toString() : "null");
}
}
@@ -236,7 +225,7 @@ public class SendFragment extends Fragment
}
@Override
public void onAttach(Context context) {
public void onAttach(@NonNull Context context) {
Timber.d("onAttach %s", context);
super.onAttach(context);
if (context instanceof Listener) {
@@ -300,12 +289,7 @@ public class SendFragment extends Fragment
default:
throw new IllegalArgumentException("Mode " + String.valueOf(aMode) + " unknown!");
}
getView().post(new Runnable() {
@Override
public void run() {
pagerAdapter.notifyDataSetChanged();
}
});
getView().post(() -> pagerAdapter.notifyDataSetChanged());
Timber.d("New Mode = %s", mode.toString());
}
}
@@ -338,8 +322,9 @@ public class SendFragment extends Fragment
return numPages;
}
@NonNull
@Override
public Object instantiateItem(ViewGroup container, int position) {
public Object instantiateItem(@NonNull ViewGroup container, int position) {
Timber.d("instantiateItem %d", position);
SendWizardFragment fragment = (SendWizardFragment) super.instantiateItem(container, position);
myFragments.put(position, new WeakReference<>(fragment));
@@ -347,20 +332,21 @@ public class SendFragment extends Fragment
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
Timber.d("destroyItem %d", position);
myFragments.remove(position);
super.destroyItem(container, position, object);
}
public SendWizardFragment getFragment(int position) {
WeakReference ref = myFragments.get(position);
WeakReference<SendWizardFragment> ref = myFragments.get(position);
if (ref != null)
return myFragments.get(position).get();
else
return null;
}
@NonNull
@Override
public SendWizardFragment getItem(int position) {
Timber.d("getItem(%d) CREATE", position);
@@ -415,7 +401,7 @@ public class SendFragment extends Fragment
}
@Override
public int getItemPosition(Object object) {
public int getItemPosition(@NonNull Object object) {
Timber.d("getItemPosition %s", String.valueOf(object));
if (object instanceof SendAddressWizardFragment) {
// keep these pages

View File

@@ -17,18 +17,20 @@
package com.m2049r.xmrwallet.layout;
import android.content.Context;
import androidx.core.content.ContextCompat;
import androidx.recyclerview.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.core.content.ContextCompat;
import androidx.recyclerview.widget.RecyclerView;
import com.m2049r.xmrwallet.R;
import com.m2049r.xmrwallet.data.Crypto;
import com.m2049r.xmrwallet.data.UserNotes;
import com.m2049r.xmrwallet.model.TransactionInfo;
import com.m2049r.xmrwallet.util.Helper;
import com.m2049r.xmrwallet.data.UserNotes;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
@@ -141,7 +143,13 @@ public class TransactionInfoAdapter extends RecyclerView.Adapter<TransactionInfo
UserNotes userNotes = new UserNotes(infoItem.notes);
if (userNotes.xmrtoKey != null) {
ivTxType.setVisibility(View.VISIBLE);
final Crypto crypto = Crypto.withSymbol(userNotes.xmrtoCurrency);
if (crypto != null) {
ivTxType.setImageResource(crypto.getIconEnabledId());
ivTxType.setVisibility(View.VISIBLE);
} else {// otherwirse pretend we don't know it's a shift
ivTxType.setVisibility(View.GONE);
}
} else {
ivTxType.setVisibility(View.GONE); // gives us more space for the amount
}

View File

@@ -21,6 +21,8 @@ import java.util.Date;
public interface CreateOrder {
String TAG = "side";
String getBtcCurrency();
double getBtcAmount();
String getBtcAddress();

View File

@@ -23,8 +23,6 @@ import androidx.annotation.NonNull;
import com.m2049r.xmrwallet.service.shift.ShiftCallback;
public interface SideShiftApi {
String ASSET = "btc";
int QUERY_INTERVAL = 5000; // ms
/**

View File

@@ -25,6 +25,7 @@ import com.m2049r.xmrwallet.service.shift.ShiftCallback;
import com.m2049r.xmrwallet.service.shift.sideshift.api.CreateOrder;
import com.m2049r.xmrwallet.service.shift.sideshift.api.SideShiftApi;
import com.m2049r.xmrwallet.util.DateHelper;
import com.m2049r.xmrwallet.util.ServiceHelper;
import org.json.JSONException;
import org.json.JSONObject;
@@ -35,6 +36,8 @@ import java.util.Date;
import lombok.Getter;
class CreateOrderImpl implements CreateOrder {
@Getter
private final String btcCurrency;
@Getter
private final double btcAmount;
@Getter
@@ -56,9 +59,10 @@ class CreateOrderImpl implements CreateOrder {
// sanity checks
final String depositMethod = jsonObject.getString("depositMethodId");
final String settleMethod = jsonObject.getString("settleMethodId");
if (!"xmr".equals(depositMethod) || !SideShiftApi.ASSET.equals(settleMethod))
if (!"xmr".equals(depositMethod) || !ServiceHelper.ASSET.equals(settleMethod))
throw new IllegalStateException();
btcCurrency = settleMethod.toUpperCase();
btcAmount = jsonObject.getDouble("settleAmount");
JSONObject settleAddress = jsonObject.getJSONObject("settleAddress");
btcAddress = settleAddress.getString("address");

View File

@@ -23,6 +23,7 @@ import com.m2049r.xmrwallet.service.shift.ShiftApiCall;
import com.m2049r.xmrwallet.service.shift.sideshift.api.QueryOrderParameters;
import com.m2049r.xmrwallet.service.shift.sideshift.api.SideShiftApi;
import com.m2049r.xmrwallet.service.shift.ShiftCallback;
import com.m2049r.xmrwallet.util.ServiceHelper;
import org.json.JSONException;
import org.json.JSONObject;
@@ -53,7 +54,7 @@ class QueryOrderParametersImpl implements QueryOrderParameters {
public static void call(@NonNull final ShiftApiCall api,
@NonNull final ShiftCallback<QueryOrderParameters> callback) {
api.call("pairs/xmr/" + SideShiftApi.ASSET, new NetworkCallback() {
api.call("pairs/xmr/" + ServiceHelper.ASSET, new NetworkCallback() {
@Override
public void onSuccess(JSONObject jsonObject) {
try {

View File

@@ -24,6 +24,7 @@ import com.m2049r.xmrwallet.util.DateHelper;
import com.m2049r.xmrwallet.service.shift.sideshift.api.RequestQuote;
import com.m2049r.xmrwallet.service.shift.sideshift.api.SideShiftApi;
import com.m2049r.xmrwallet.service.shift.ShiftCallback;
import com.m2049r.xmrwallet.util.ServiceHelper;
import org.json.JSONException;
import org.json.JSONObject;
@@ -55,7 +56,7 @@ class RequestQuoteImpl implements RequestQuote {
// sanity checks
final String depositMethod = jsonObject.getString("depositMethod");
final String settleMethod = jsonObject.getString("settleMethod");
if (!"xmr".equals(depositMethod) || !SideShiftApi.ASSET.equals(settleMethod))
if (!"xmr".equals(depositMethod) || !ServiceHelper.ASSET.equals(settleMethod))
throw new IllegalStateException();
btcAmount = jsonObject.getDouble("settleAmount");
@@ -106,7 +107,7 @@ class RequestQuoteImpl implements RequestQuote {
static JSONObject createRequest(final double xmrAmount) throws JSONException {
final JSONObject jsonObject = new JSONObject();
jsonObject.put("depositMethod", "xmr");
jsonObject.put("settleMethod", SideShiftApi.ASSET);
jsonObject.put("settleMethod", ServiceHelper.ASSET);
// #sideshift is silly and likes numbers as strings
String amount = AmountFormatter.format(xmrAmount);
jsonObject.put("depositAmount", amount);

View File

@@ -323,15 +323,6 @@ public class Helper {
return ShakeAnimation;
}
static public HttpUrl getXmrToBaseUrl() {
if ((WalletManager.getInstance() == null)
|| (WalletManager.getInstance().getNetworkType() != NetworkType.NetworkType_Mainnet)) {
throw new IllegalStateException("Only mainnet not supported");
} else {
return HttpUrl.parse("https://sideshift.ai/api/v1/");
}
}
private final static char[] HexArray = "0123456789ABCDEF".toCharArray();
public static String bytesToHex(byte[] data) {
@@ -639,10 +630,6 @@ public class Helper {
}
}
static public ExchangeApi getExchangeApi() {
return new com.m2049r.xmrwallet.service.exchange.krakenEcb.ExchangeApiImpl(OkHttpHelper.getOkHttpClient());
}
public interface Action {
boolean run();
}

View File

@@ -21,6 +21,7 @@ package com.m2049r.xmrwallet.util;
import android.os.AsyncTask;
import com.m2049r.xmrwallet.data.BarcodeData;
import com.m2049r.xmrwallet.data.Crypto;
import org.jitsi.dnssec.validator.ValidatingResolver;
import org.xbill.DNS.DClass;
@@ -52,7 +53,6 @@ public class OpenAliasHelper {
public static final String OA1_NAME = "recipient_name";
public static final String OA1_DESCRIPTION = "tx_description";
public static final String OA1_AMOUNT = "tx_amount";
public static final String OA1_PAYMENTID = "tx_payment_id";
public static final int DNS_LOOKUP_TIMEOUT = 2500; // ms
@@ -65,7 +65,7 @@ public class OpenAliasHelper {
}
public interface OnResolvedListener {
void onResolved(Map<BarcodeData.Asset, BarcodeData> dataMap);
void onResolved(Map<Crypto, BarcodeData> dataMap);
void onFailure();
}
@@ -138,7 +138,7 @@ public class OpenAliasHelper {
public void onPostExecute(Boolean success) {
if (resolvedListener != null)
if (success) {
Map<BarcodeData.Asset, BarcodeData> dataMap = new HashMap<>();
Map<Crypto, BarcodeData> dataMap = new HashMap<>();
for (String txt : txts) {
BarcodeData bc = BarcodeData.parseOpenAlias(txt, dnssec);
if (bc != null) {

View File

@@ -0,0 +1,24 @@
package com.m2049r.xmrwallet.util;
import com.m2049r.xmrwallet.model.NetworkType;
import com.m2049r.xmrwallet.model.WalletManager;
import com.m2049r.xmrwallet.service.exchange.api.ExchangeApi;
import okhttp3.HttpUrl;
public class ServiceHelper {
public static String ASSET = null;
static public HttpUrl getXmrToBaseUrl() {
if ((WalletManager.getInstance() == null)
|| (WalletManager.getInstance().getNetworkType() != NetworkType.NetworkType_Mainnet)) {
throw new IllegalStateException("Only mainnet not supported");
} else {
return HttpUrl.parse("https://sideshift.ai/api/v1/");
}
}
static public ExchangeApi getExchangeApi() {
return new com.m2049r.xmrwallet.service.exchange.krakenEcb.ExchangeApiImpl(OkHttpHelper.getOkHttpClient());
}
}

View File

@@ -0,0 +1,51 @@
package com.m2049r.xmrwallet.util.validator;
import lombok.Getter;
public enum BitcoinAddressType {
BTC(Type.BTC, Type.BTC_BECH32_PREFIX),
LTC(Type.LTC, Type.LTC_BECH32_PREFIX),
DASH(Type.DASH, null),
DOGE(Type.DOGE, null);
@Getter
private final byte[] production;
@Getter
private final byte[] testnet;
@Getter
private final String productionBech32Prefix;
@Getter
private final String testnetBech32Prefix;
public boolean hasBech32() {
return productionBech32Prefix != null;
}
public String getBech32Prefix(boolean testnet) {
return testnet ? testnetBech32Prefix : productionBech32Prefix;
}
BitcoinAddressType(byte[][] types, String[] bech32Prefix) {
production = types[0];
testnet = types[1];
if (bech32Prefix != null) {
productionBech32Prefix = bech32Prefix[0];
testnetBech32Prefix = bech32Prefix[1];
} else {
productionBech32Prefix = null;
testnetBech32Prefix = null;
}
}
// Java is silly and doesn't allow array initializers in the construction
private static class Type {
private static final byte[][] BTC = {{0x00, 0x05}, {0x6f, (byte) 0xc4}};
private static final String[] BTC_BECH32_PREFIX = {"bc", "tb"};
private static final byte[][] LTC = {{0x30, 0x05, 0x32}, {0x6f, (byte) 0xc4, 0x3a}};
private static final String[] LTC_BECH32_PREFIX = {"ltc", "tltc"};
private static final byte[][] DASH = {{0x4c, 0x10}, {(byte) 0x8c, 0x13}};
private static final byte[][] DOGE = {{0x1e, 0x16}, {0x71, (byte) 0xc4}};
}
}

View File

@@ -14,10 +14,11 @@
* limitations under the License.
*/
package com.m2049r.xmrwallet.util;
package com.m2049r.xmrwallet.util.validator;
// mostly based on https://rosettacode.org/wiki/Bitcoin/address_validation#Java
import com.m2049r.xmrwallet.data.Crypto;
import com.m2049r.xmrwallet.model.NetworkType;
import com.m2049r.xmrwallet.model.WalletManager;
@@ -28,28 +29,47 @@ import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
public class BitcoinAddressValidator {
private static final String ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
public static boolean validate(String addrress) {
boolean testnet = WalletManager.getInstance().getNetworkType() != NetworkType.NetworkType_Mainnet;
if (validate(addrress, testnet)) return true;
return validateBech32Segwit(addrress, testnet);
public static Crypto validate(String address) {
for (BitcoinAddressType type : BitcoinAddressType.values()) {
if (validate(address, type))
return Crypto.valueOf(type.name());
}
return null;
}
public static boolean validate(String addrress, boolean testnet) {
// just for tests
public static boolean validateBTC(String addrress, boolean testnet) {
return validate(addrress, BitcoinAddressType.BTC, testnet);
}
public static boolean validate(String addrress, BitcoinAddressType type, boolean testnet) {
if (validate(addrress, testnet ? type.getTestnet() : type.getProduction()))
return true;
if (type.hasBech32())
return validateBech32Segwit(addrress, type, testnet);
else
return false;
}
public static boolean validate(String addrress, BitcoinAddressType type) {
final boolean testnet = WalletManager.getInstance().getNetworkType() != NetworkType.NetworkType_Mainnet;
return validate(addrress, type, testnet);
}
public static boolean validate(String addrress, byte[] addressTypes) {
if (addrress.length() < 26 || addrress.length() > 35)
return false;
byte[] decoded = decodeBase58To25Bytes(addrress);
if (decoded == null)
return false;
int v = decoded[0] & 0xFF;
if (!testnet) {
if ((v != 0x00) && (v != 0x05)) return false;
} else {
if ((v != 0x6f) && (v != 0xc4)) return false;
boolean nok = true;
for (byte b : addressTypes) {
nok = nok && (v != (b & 0xFF));
}
if (nok) return false;
byte[] hash1 = sha256(Arrays.copyOfRange(decoded, 0, 21));
byte[] hash2 = sha256(hash1);
@@ -95,18 +115,20 @@ public class BitcoinAddressValidator {
private static final String DATA_CHARS = "qpzry9x8gf2tvdw0s3jn54khce6mua7l";
public static boolean validateBech32Segwit(String bech32, boolean testnet) {
public static boolean validateBech32Segwit(String bech32, BitcoinAddressType type, boolean testnet) {
if (!bech32.equals(bech32.toLowerCase()) && !bech32.equals(bech32.toUpperCase())) {
return false; // mixing upper and lower case not allowed
}
bech32 = bech32.toLowerCase();
if (testnet && !bech32.startsWith("tb1")) return false;
if (!testnet && !bech32.startsWith("bc1")) return false;
if (!bech32.startsWith(type.getBech32Prefix(testnet))) return false;
if ((bech32.length() < 14) || (bech32.length() > 74)) return false;
int mod = bech32.length() % 8;
if ((mod == 0) || (mod == 3) || (mod == 5)) return false;
final int hrpLength = type.getBech32Prefix(testnet).length();
if ((bech32.length() < (12 + hrpLength)) || (bech32.length() > (72 + hrpLength)))
return false;
int mod = (bech32.length() - hrpLength) % 8;
if ((mod == 6) || (mod == 1) || (mod == 3)) return false;
int sep = -1;
final byte[] bytes = bech32.getBytes(StandardCharsets.US_ASCII);
@@ -117,7 +139,7 @@ public class BitcoinAddressValidator {
if (bytes[i] == 49) sep = i; // 49 := '1' in ASCII
}
if (sep != 2) return false; // bech32 always has len(hrp)==2
if (sep != hrpLength) return false;
if (sep > bytes.length - 7) {
return false; // min 6 bytes data
}
@@ -158,12 +180,12 @@ public class BitcoinAddressValidator {
private static byte[] hrpExpand(byte[] hrp) {
final byte[] expanded = new byte[(2 * hrp.length) + 1];
int i = 0;
for (int j = 0; j < hrp.length; j++) {
expanded[i++] = (byte) (hrp[j] >> 5);
for (byte b : hrp) {
expanded[i++] = (byte) (b >> 5);
}
expanded[i++] = 0;
for (int j = 0; j < hrp.length; j++) {
expanded[i++] = (byte) (hrp[j] & 0x1f);
for (byte b : hrp) {
expanded[i++] = (byte) (b & 0x1f);
}
return expanded;
}
@@ -195,4 +217,4 @@ public class BitcoinAddressValidator {
return true;
}
}
}

View File

@@ -0,0 +1,64 @@
/*
* Copyright (c) 2017 m2049r er 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.util.validator;
// mostly based on https://github.com/ognus/wallet-address-validator/blob/master/src/ethereum_validator.js
import com.theromus.sha.Keccak;
import com.theromus.sha.Parameters;
import java.nio.charset.StandardCharsets;
import java.util.regex.Pattern;
public class EthAddressValidator {
static private final Pattern ETH_ADDRESS = Pattern.compile("^0x[0-9a-fA-F]{40}$");
static private final Pattern ETH_ALLLOWER = Pattern.compile("^0x[0-9a-f]{40}$");
static private final Pattern ETH_ALLUPPER = Pattern.compile("^0x[0-9A-F]{40}$");
public static boolean validate(String address) {
// Check if it has the basic requirements of an address
if (!ETH_ADDRESS.matcher(address).matches())
return false;
// If it's all small caps or all all caps, return true
if (ETH_ALLLOWER.matcher(address).matches() || ETH_ALLUPPER.matcher(address).matches()) {
return true;
}
// Otherwise check each case
return validateChecksum(address);
}
private static boolean validateChecksum(String address) {
// Check each case
address = address.substring(2); // strip 0x
Keccak keccak = new Keccak();
final byte[] addressHash = keccak.getHash(
address.toLowerCase().getBytes(StandardCharsets.US_ASCII),
Parameters.KECCAK_256);
for (int i = 0; i < 40; i++) {
boolean upper = (addressHash[i / 2] & ((i % 2) == 0 ? 128 : 8)) != 0;
char c = address.charAt(i);
if (Character.isAlphabetic(c)) {
if (Character.isUpperCase(c) && !upper) return false;
if (Character.isLowerCase(c) && upper) return false;
}
}
return true;
}
}

View File

@@ -40,6 +40,7 @@ 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 com.m2049r.xmrwallet.util.ServiceHelper;
import java.util.ArrayList;
import java.util.Arrays;
@@ -184,6 +185,12 @@ public class ExchangeEditText extends LinearLayout {
private boolean isInitialized = false;
void postInitialize() {
setInitialSpinnerSelections(sCurrencyA, sCurrencyB);
isInitialized = true;
startExchange();
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
@@ -212,14 +219,7 @@ public class ExchangeEditText extends LinearLayout {
setCurrencyAdapter(sCurrencyA);
setCurrencyAdapter(sCurrencyB);
post(new Runnable() {
@Override
public void run() {
setInitialSpinnerSelections(sCurrencyA, sCurrencyB);
isInitialized = true;
startExchange();
}
});
post(this::postInitialize);
// make progress circle gray
pbExchange.getIndeterminateDrawable().
@@ -296,7 +296,7 @@ public class ExchangeEditText extends LinearLayout {
}
}
private final ExchangeApi exchangeApi = Helper.getExchangeApi();
private final ExchangeApi exchangeApi = ServiceHelper.getExchangeApi();
// starts exchange through exchange api
void startExchange() {

View File

@@ -25,6 +25,8 @@ import android.os.Looper;
import android.util.AttributeSet;
import android.widget.Spinner;
import androidx.annotation.NonNull;
import com.m2049r.xmrwallet.R;
import com.m2049r.xmrwallet.service.exchange.api.ExchangeCallback;
import com.m2049r.xmrwallet.service.exchange.api.ExchangeRate;
@@ -46,12 +48,15 @@ public class ExchangeOtherEditText extends ExchangeEditText {
public void setExchangeRate(double rate) {
exchangeRate = rate;
post(new Runnable() {
@Override
public void run() {
startExchange();
}
});
post(this::startExchange);
}
public void setBaseCurrency(@NonNull String symbol) {
if (symbol.equals(baseCurrency)) return;
baseCurrency = symbol;
setCurrencyAdapter(sCurrencyA);
setCurrencyAdapter(sCurrencyB);
post(this::postInitialize);
}
private void setBaseCurrency(Context context, AttributeSet attrs) {
@@ -184,12 +189,7 @@ public class ExchangeOtherEditText extends ExchangeEditText {
@Override
public void onError(final Exception e) {
Timber.e(e.getLocalizedMessage());
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
exchangeFailed();
}
});
new Handler(Looper.getMainLooper()).post(() -> exchangeFailed());
}
});
}

Some files were not shown because too many files have changed in this diff Show More