From ae62c528aa9a447375ebb73f372f3371b85c371f Mon Sep 17 00:00:00 2001 From: m2049r <30435443+m2049r@users.noreply.github.com> Date: Sat, 9 Dec 2017 20:38:01 +0100 Subject: [PATCH] Tweaks & Fixes (#153) * clear error * isAddressValid refactoring * better numpad layout * receive qr code as card * scan integrated address --- app/build.gradle | 4 +- .../com/m2049r/xmrwallet/ScannerFragment.java | 3 +- .../xmrwallet/SendAddressWizardFragment.java | 11 +- .../xmrwallet/SendAmountWizardFragment.java | 4 +- .../com/m2049r/xmrwallet/WalletActivity.java | 48 +------- .../m2049r/xmrwallet/data/BarcodeData.java | 112 +++++++++++++++++- .../com/m2049r/xmrwallet/model/Wallet.java | 4 + app/src/main/res/layout/fragment_receive.xml | 24 +++- app/src/main/res/layout/view_number_pad.xml | 31 +++-- 9 files changed, 158 insertions(+), 83 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index c65873b..5fc484d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -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 { diff --git a/app/src/main/java/com/m2049r/xmrwallet/ScannerFragment.java b/app/src/main/java/com/m2049r/xmrwallet/ScannerFragment.java index dcd7fa3..e9997cf 100644 --- a/app/src/main/java/com/m2049r/xmrwallet/ScannerFragment.java +++ b/app/src/main/java/com/m2049r/xmrwallet/ScannerFragment.java @@ -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 { diff --git a/app/src/main/java/com/m2049r/xmrwallet/SendAddressWizardFragment.java b/app/src/main/java/com/m2049r/xmrwallet/SendAddressWizardFragment.java index 085da6f..3b5a9d3 100644 --- a/app/src/main/java/com/m2049r/xmrwallet/SendAddressWizardFragment.java +++ b/app/src/main/java/com/m2049r/xmrwallet/SendAddressWizardFragment.java @@ -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() { diff --git a/app/src/main/java/com/m2049r/xmrwallet/SendAmountWizardFragment.java b/app/src/main/java/com/m2049r/xmrwallet/SendAmountWizardFragment.java index a1a5175..033dfea 100644 --- a/app/src/main/java/com/m2049r/xmrwallet/SendAmountWizardFragment.java +++ b/app/src/main/java/com/m2049r/xmrwallet/SendAmountWizardFragment.java @@ -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); } } } diff --git a/app/src/main/java/com/m2049r/xmrwallet/WalletActivity.java b/app/src/main/java/com/m2049r/xmrwallet/WalletActivity.java index e495580..5fa4e44 100644 --- a/app/src/main/java/com/m2049r/xmrwallet/WalletActivity.java +++ b/app/src/main/java/com/m2049r/xmrwallet/WalletActivity.java @@ -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 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; diff --git a/app/src/main/java/com/m2049r/xmrwallet/data/BarcodeData.java b/app/src/main/java/com/m2049r/xmrwallet/data/BarcodeData.java index e0fd433..22ceca7 100644 --- a/app/src/main/java/com/m2049r/xmrwallet/data/BarcodeData.java +++ b/app/src/main/java/com/m2049r/xmrwallet/data/BarcodeData.java @@ -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(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(String address, String paymentId, long 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 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); + } } \ No newline at end of file diff --git a/app/src/main/java/com/m2049r/xmrwallet/model/Wallet.java b/app/src/main/java/com/m2049r/xmrwallet/model/Wallet.java index ad576fa..dfe8cb1 100644 --- a/app/src/main/java/com/m2049r/xmrwallet/model/Wallet.java +++ b/app/src/main/java/com/m2049r/xmrwallet/model/Wallet.java @@ -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); diff --git a/app/src/main/res/layout/fragment_receive.xml b/app/src/main/res/layout/fragment_receive.xml index f236af8..bb76485 100644 --- a/app/src/main/res/layout/fragment_receive.xml +++ b/app/src/main/res/layout/fragment_receive.xml @@ -121,13 +121,25 @@ android:textSize="16sp" android:visibility="invisible" /> - - + android:layout_gravity="center" + android:clickable="true" + android:foreground="?android:attr/selectableItemBackground" + card_view:cardCornerRadius="2dp" + card_view:cardElevation="8dp" + card_view:contentPadding="4dp"> + + + diff --git a/app/src/main/res/layout/view_number_pad.xml b/app/src/main/res/layout/view_number_pad.xml index 0b451ee..41ce926 100644 --- a/app/src/main/res/layout/view_number_pad.xml +++ b/app/src/main/res/layout/view_number_pad.xml @@ -8,7 +8,9 @@ + android:layout_height="0dp" + android:layout_weight="1" + android:gravity="center"> @@ -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" /> + android:layout_height="0dp" + android:layout_weight="1" + + android:gravity="center"> @@ -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" /> + android:layout_height="0dp" + android:layout_weight="1" + android:gravity="center"> @@ -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" /> + android:layout_height="0dp" + android:layout_weight="1" + android:gravity="center">