mirror of
https://github.com/m2049r/xmrwallet
synced 2025-09-03 08:23:04 +02:00
Compare commits
6 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
e56369b91a | ||
![]() |
b04aa24269 | ||
![]() |
dda86bd5de | ||
![]() |
a19ad7fd52 | ||
![]() |
0443fd808c | ||
![]() |
faf57c96fc |
@@ -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
|
||||
|
@@ -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 {
|
||||
|
@@ -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
|
||||
|
@@ -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) {
|
||||
|
@@ -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
|
||||
|
@@ -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);
|
||||
|
@@ -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");
|
||||
|
@@ -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
89
app/src/main/java/com/m2049r/xmrwallet/data/Crypto.java
Normal file
89
app/src/main/java/com/m2049r/xmrwallet/data/Crypto.java
Normal 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);
|
||||
}
|
||||
}
|
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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()))
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -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 {
|
||||
|
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
}
|
||||
|
@@ -21,6 +21,8 @@ import java.util.Date;
|
||||
public interface CreateOrder {
|
||||
String TAG = "side";
|
||||
|
||||
String getBtcCurrency();
|
||||
|
||||
double getBtcAmount();
|
||||
|
||||
String getBtcAddress();
|
||||
|
@@ -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
|
||||
|
||||
/**
|
||||
|
@@ -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");
|
||||
|
@@ -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 {
|
||||
|
@@ -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);
|
||||
|
@@ -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();
|
||||
}
|
||||
|
@@ -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) {
|
||||
|
@@ -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());
|
||||
}
|
||||
}
|
@@ -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}};
|
||||
}
|
||||
|
||||
}
|
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@@ -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;
|
||||
}
|
||||
}
|
@@ -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() {
|
||||
|
@@ -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
Reference in New Issue
Block a user