Tweaks & Fixes (#153)
* clear error * isAddressValid refactoring * better numpad layout * receive qr code as card * scan integrated address
This commit is contained in:
parent
e6522769d0
commit
ae62c528aa
|
@ -8,8 +8,8 @@ android {
|
|||
applicationId "com.m2049r.xmrwallet"
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 25
|
||||
versionCode 48
|
||||
versionName "1.2.8"
|
||||
versionCode 49
|
||||
versionName "1.2.9"
|
||||
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
|
|
|
@ -62,8 +62,7 @@ public class ScannerFragment extends Fragment implements ZXingScannerView.Result
|
|||
|
||||
@Override
|
||||
public void handleResult(Result rawResult) {
|
||||
if ((rawResult.getBarcodeFormat() == BarcodeFormat.QR_CODE) &&
|
||||
(rawResult.getText().startsWith(QR_SCHEME))) {
|
||||
if ((rawResult.getBarcodeFormat() == BarcodeFormat.QR_CODE)) {
|
||||
if (onScannedListener.onScanned(rawResult.getText())) {
|
||||
return;
|
||||
} else {
|
||||
|
|
|
@ -72,8 +72,6 @@ public class SendAddressWizardFragment extends SendWizardFragment {
|
|||
private View tvPaymentIdIntegrated;
|
||||
private View llPaymentId;
|
||||
|
||||
private String scannedAmount = null;
|
||||
|
||||
OnScanListener onScanListener;
|
||||
|
||||
public interface OnScanListener {
|
||||
|
@ -113,8 +111,8 @@ public class SendAddressWizardFragment extends SendWizardFragment {
|
|||
etAddress.getEditText().addTextChangedListener(new TextWatcher() {
|
||||
@Override
|
||||
public void afterTextChanged(Editable editable) {
|
||||
if ((etAddress.getEditText().getText().toString().length() == INTEGRATED_ADDRESS_LENGTH) &&
|
||||
checkAddressNoError()) { // we have an integrated address
|
||||
etAddress.setError(null);
|
||||
if (isIntegratedAddress()) {
|
||||
etPaymentId.getEditText().getText().clear();
|
||||
llPaymentId.setVisibility(View.GONE);
|
||||
tvPaymentIdIntegrated.setVisibility(View.VISIBLE);
|
||||
|
@ -190,7 +188,7 @@ public class SendAddressWizardFragment extends SendWizardFragment {
|
|||
|
||||
private boolean checkAddressNoError() {
|
||||
String address = etAddress.getEditText().getText().toString();
|
||||
return Wallet.isAddressValid(address, WalletManager.getInstance().isTestNet());
|
||||
return Wallet.isAddressValid(address);
|
||||
}
|
||||
|
||||
private boolean checkAddress() {
|
||||
|
@ -205,8 +203,7 @@ public class SendAddressWizardFragment extends SendWizardFragment {
|
|||
|
||||
private boolean isIntegratedAddress() {
|
||||
String address = etAddress.getEditText().getText().toString();
|
||||
return Wallet.isAddressValid(address, WalletManager.getInstance().isTestNet())
|
||||
&& address.length() == 106;
|
||||
return (address.length() == INTEGRATED_ADDRESS_LENGTH) && Wallet.isAddressValid(address);
|
||||
}
|
||||
|
||||
private boolean checkPaymentId() {
|
||||
|
|
|
@ -121,8 +121,8 @@ public class SendAmountWizardFragment extends SendWizardFragment {
|
|||
// getAmount is null if exchange is in progress
|
||||
if ((evAmount.getAmount() != null) && evAmount.getAmount().isEmpty()) {
|
||||
final BarcodeData data = sendListener.popBarcodeData();
|
||||
if ((data != null) && (data.amount > 0)) {
|
||||
evAmount.setAmount(Wallet.getDisplayAmount(data.amount));
|
||||
if ((data != null) && (data.amount != null)) {
|
||||
evAmount.setAmount(data.amount);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -727,8 +727,9 @@ public class WalletActivity extends SecureActivity implements WalletFragment.Lis
|
|||
private BarcodeData scannedData = null;
|
||||
|
||||
@Override
|
||||
public boolean onScanned(String uri) {
|
||||
BarcodeData bcData = parseMoneroUri(uri);
|
||||
public boolean onScanned(String qrCode) {
|
||||
// #gurke
|
||||
BarcodeData bcData = BarcodeData.fromQrCode(qrCode);
|
||||
if (bcData != null) {
|
||||
this.scannedData = bcData;
|
||||
popFragmentStack(null);
|
||||
|
@ -738,49 +739,6 @@ public class WalletActivity extends SecureActivity implements WalletFragment.Lis
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse and decode a monero scheme string. It is here because it needs to validate the data.
|
||||
*
|
||||
* @param uri String containing a monero URL
|
||||
* @return BarcodeData object or null if uri not valid
|
||||
*/
|
||||
public BarcodeData parseMoneroUri(String uri) {
|
||||
if (uri == null) return null;
|
||||
|
||||
if (!uri.startsWith(ScannerFragment.QR_SCHEME)) return null;
|
||||
|
||||
String noScheme = uri.substring(ScannerFragment.QR_SCHEME.length());
|
||||
Uri monero = Uri.parse(noScheme);
|
||||
Map<String, String> parms = new HashMap<>();
|
||||
String query = monero.getQuery();
|
||||
if (query != null) {
|
||||
String[] args = query.split("&");
|
||||
for (String arg : args) {
|
||||
String[] namevalue = arg.split("=");
|
||||
if (namevalue.length == 0) {
|
||||
continue;
|
||||
}
|
||||
parms.put(Uri.decode(namevalue[0]).toLowerCase(),
|
||||
namevalue.length > 1 ? Uri.decode(namevalue[1]) : null);
|
||||
}
|
||||
}
|
||||
String address = monero.getPath();
|
||||
String paymentId = parms.get(ScannerFragment.QR_PAYMENTID);
|
||||
String amountString = parms.get(ScannerFragment.QR_AMOUNT);
|
||||
long amount = -1;
|
||||
if (amountString != null) {
|
||||
amount = Wallet.getAmountFromString(amountString);
|
||||
}
|
||||
if ((paymentId != null) && !Wallet.isPaymentIdValid(paymentId)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (Wallet.isAddressValid(address, WalletManager.getInstance().isTestNet())) {
|
||||
return new BarcodeData(address, paymentId, amount);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BarcodeData popScannedData() {
|
||||
BarcodeData data = scannedData;
|
||||
|
|
|
@ -16,14 +16,122 @@
|
|||
|
||||
package com.m2049r.xmrwallet.data;
|
||||
|
||||
import android.net.Uri;
|
||||
|
||||
import com.m2049r.xmrwallet.model.Wallet;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import timber.log.Timber;
|
||||
|
||||
public class BarcodeData {
|
||||
public static final String XMR_SCHEME = "monero:";
|
||||
public static final String XMR_PAYMENTID = "tx_payment_id";
|
||||
public static final String XMR_AMOUNT = "tx_amount";
|
||||
|
||||
static final String BTC_SCHEME = "bitcoin:";
|
||||
static final String BTC_AMOUNT = "amount";
|
||||
|
||||
public enum Asset {
|
||||
XMR
|
||||
}
|
||||
|
||||
public Asset asset = null;
|
||||
public String address = null;
|
||||
public String paymentId = null;
|
||||
public long amount = -1;
|
||||
public String amount = null;
|
||||
|
||||
public BarcodeData(String address, String paymentId, long amount) {
|
||||
public BarcodeData(Asset asset, String address) {
|
||||
this.asset = asset;
|
||||
this.address = address;
|
||||
}
|
||||
|
||||
public BarcodeData(Asset asset, String address, String amount) {
|
||||
this.asset = asset;
|
||||
this.address = address;
|
||||
this.amount = amount;
|
||||
}
|
||||
|
||||
public BarcodeData(Asset asset, String address, String paymentId, String amount) {
|
||||
this.asset = asset;
|
||||
this.address = address;
|
||||
this.paymentId = paymentId;
|
||||
this.amount = amount;
|
||||
}
|
||||
|
||||
static public BarcodeData fromQrCode(String qrCode) {
|
||||
// check for monero uri
|
||||
BarcodeData bcData = parseMoneroUri(qrCode);
|
||||
// check for naked monero address / integrated address
|
||||
if (bcData == null) {
|
||||
bcData = parseMoneroNaked(qrCode);
|
||||
}
|
||||
return bcData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse and decode a monero scheme string. It is here because it needs to validate the data.
|
||||
*
|
||||
* @param uri String containing a monero URL
|
||||
* @return BarcodeData object or null if uri not valid
|
||||
*/
|
||||
|
||||
static public BarcodeData parseMoneroUri(String uri) {
|
||||
Timber.d("parseMoneroUri=%s", uri);
|
||||
|
||||
if (uri == null) return null;
|
||||
|
||||
if (!uri.startsWith(XMR_SCHEME)) return null;
|
||||
|
||||
String noScheme = uri.substring(XMR_SCHEME.length());
|
||||
Uri monero = Uri.parse(noScheme);
|
||||
Map<String, String> parms = new HashMap<>();
|
||||
String query = monero.getQuery();
|
||||
if (query != null) {
|
||||
String[] args = query.split("&");
|
||||
for (String arg : args) {
|
||||
String[] namevalue = arg.split("=");
|
||||
if (namevalue.length == 0) {
|
||||
continue;
|
||||
}
|
||||
parms.put(Uri.decode(namevalue[0]).toLowerCase(),
|
||||
namevalue.length > 1 ? Uri.decode(namevalue[1]) : "");
|
||||
}
|
||||
}
|
||||
String address = monero.getPath();
|
||||
String paymentId = parms.get(XMR_PAYMENTID);
|
||||
String amount = parms.get(XMR_AMOUNT);
|
||||
if (amount != null) {
|
||||
try {
|
||||
Double.parseDouble(amount);
|
||||
} catch (NumberFormatException ex) {
|
||||
Timber.d(ex.getLocalizedMessage());
|
||||
return null; // we have an amount but its not a number!
|
||||
}
|
||||
}
|
||||
if ((paymentId != null) && !Wallet.isPaymentIdValid(paymentId)) {
|
||||
Timber.d("paymentId invalid");
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!Wallet.isAddressValid(address)) {
|
||||
Timber.d("address invalid");
|
||||
return null;
|
||||
}
|
||||
return new BarcodeData(Asset.XMR, address, paymentId, amount);
|
||||
}
|
||||
|
||||
static public BarcodeData parseMoneroNaked(String address) {
|
||||
Timber.d("parseMoneroNaked=%s", address);
|
||||
|
||||
if (address == null) return null;
|
||||
|
||||
if (!Wallet.isAddressValid(address)) {
|
||||
Timber.d("address invalid");
|
||||
return null;
|
||||
}
|
||||
|
||||
return new BarcodeData(Asset.XMR, address);
|
||||
}
|
||||
}
|
|
@ -154,6 +154,10 @@ public class Wallet {
|
|||
|
||||
public static native boolean isPaymentIdValid(String payment_id);
|
||||
|
||||
public static boolean isAddressValid(String address) {
|
||||
return isAddressValid(address, WalletManager.getInstance().isTestNet());
|
||||
}
|
||||
|
||||
public static native boolean isAddressValid(String address, boolean isTestNet);
|
||||
|
||||
//TODO static static bool keyValid(const std::string &secret_key_string, const std::string &address_string, bool isViewKey, bool testnet, std::string &error);
|
||||
|
|
|
@ -121,13 +121,25 @@
|
|||
android:textSize="16sp"
|
||||
android:visibility="invisible" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/qrCode"
|
||||
android:layout_width="match_parent"
|
||||
<android.support.v7.widget.CardView xmlns:card_view="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/cvQrCode"
|
||||
android:layout_margin="16dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:adjustViewBounds="true"
|
||||
android:background="#00000000" />
|
||||
android:layout_gravity="center"
|
||||
android:clickable="true"
|
||||
android:foreground="?android:attr/selectableItemBackground"
|
||||
card_view:cardCornerRadius="2dp"
|
||||
card_view:cardElevation="8dp"
|
||||
card_view:contentPadding="4dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/qrCode"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:adjustViewBounds="true"
|
||||
android:background="#00000000" />
|
||||
</android.support.v7.widget.CardView>
|
||||
</FrameLayout>
|
||||
</LinearLayout>
|
||||
|
||||
|
|
|
@ -8,7 +8,9 @@
|
|||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"
|
||||
android:gravity="center">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/numberPad1"
|
||||
|
@ -18,7 +20,6 @@
|
|||
android:layout_weight="1"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:gravity="center"
|
||||
android:padding="8dp"
|
||||
android:tag="1"
|
||||
android:text="1" />
|
||||
|
||||
|
@ -30,7 +31,6 @@
|
|||
android:layout_weight="1"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:gravity="center"
|
||||
android:padding="8dp"
|
||||
android:tag="2"
|
||||
android:text="2" />
|
||||
|
||||
|
@ -42,14 +42,16 @@
|
|||
android:layout_weight="1"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:gravity="center"
|
||||
android:padding="8dp"
|
||||
android:tag="3"
|
||||
android:text="3" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"
|
||||
|
||||
android:gravity="center">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/numberPad4"
|
||||
|
@ -59,7 +61,6 @@
|
|||
android:layout_weight="1"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:gravity="center"
|
||||
android:padding="8dp"
|
||||
android:tag="4"
|
||||
android:text="4" />
|
||||
|
||||
|
@ -71,7 +72,6 @@
|
|||
android:layout_weight="1"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:gravity="center"
|
||||
android:padding="8dp"
|
||||
android:tag="5"
|
||||
android:text="5" />
|
||||
|
||||
|
@ -83,14 +83,15 @@
|
|||
android:layout_weight="1"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:gravity="center"
|
||||
android:padding="8dp"
|
||||
android:tag="6"
|
||||
android:text="6" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"
|
||||
android:gravity="center">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/numberPad7"
|
||||
|
@ -100,7 +101,6 @@
|
|||
android:layout_weight="1"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:gravity="center"
|
||||
android:padding="8dp"
|
||||
android:tag="7"
|
||||
android:text="7" />
|
||||
|
||||
|
@ -112,7 +112,6 @@
|
|||
android:layout_weight="1"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:gravity="center"
|
||||
android:padding="8dp"
|
||||
android:tag="8"
|
||||
android:text="8" />
|
||||
|
||||
|
@ -124,14 +123,15 @@
|
|||
android:layout_weight="1"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:gravity="center"
|
||||
android:padding="8dp"
|
||||
android:tag="9"
|
||||
android:text="9" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"
|
||||
android:gravity="center">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/numberPadPoint"
|
||||
|
@ -141,7 +141,6 @@
|
|||
android:layout_weight="1"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:gravity="center"
|
||||
android:padding="8dp"
|
||||
android:text="." />
|
||||
|
||||
<TextView
|
||||
|
@ -152,18 +151,16 @@
|
|||
android:layout_weight="1"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:gravity="center"
|
||||
android:padding="8dp"
|
||||
android:tag="0"
|
||||
android:text="0" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/numberPadBackSpace"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_height="36sp"
|
||||
android:layout_gravity="center"
|
||||
android:layout_weight="1"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:padding="16dp"
|
||||
android:src="@drawable/ic_backspace_black_36dp" />
|
||||
</LinearLayout>
|
||||
|
||||
|
|
Loading…
Reference in New Issue