From 6822aa83d8be4d83cf58c167a6c3891282eb63ac Mon Sep 17 00:00:00 2001 From: m2049r <30435443+m2049r@users.noreply.github.com> Date: Thu, 31 Aug 2017 09:04:32 +0200 Subject: [PATCH] QR scan of amount as well bugfix: use getSupportFragmentManager everywhere --- .../com/m2049r/xmrwallet/LoginActivity.java | 2 +- .../com/m2049r/xmrwallet/ScannerFragment.java | 37 +++------- .../com/m2049r/xmrwallet/SendFragment.java | 30 +++++--- .../com/m2049r/xmrwallet/WalletActivity.java | 71 ++++++++++++++++--- .../com/m2049r/xmrwallet/WalletFragment.java | 26 ++----- .../m2049r/xmrwallet/util/BarcodeData.java | 5 +- .../com/m2049r/xmrwallet/util/Helper.java | 18 ++++- 7 files changed, 120 insertions(+), 69 deletions(-) diff --git a/app/src/main/java/com/m2049r/xmrwallet/LoginActivity.java b/app/src/main/java/com/m2049r/xmrwallet/LoginActivity.java index b68c65d..fc64961 100644 --- a/app/src/main/java/com/m2049r/xmrwallet/LoginActivity.java +++ b/app/src/main/java/com/m2049r/xmrwallet/LoginActivity.java @@ -308,7 +308,7 @@ public class LoginActivity extends AppCompatActivity } void popFragmentStack(String name) { - getFragmentManager().popBackStack(name, FragmentManager.POP_BACK_STACK_INCLUSIVE); + getSupportFragmentManager().popBackStack(name, FragmentManager.POP_BACK_STACK_INCLUSIVE); } ////////////////////////////////////////// diff --git a/app/src/main/java/com/m2049r/xmrwallet/ScannerFragment.java b/app/src/main/java/com/m2049r/xmrwallet/ScannerFragment.java index 307c6be..668f172 100644 --- a/app/src/main/java/com/m2049r/xmrwallet/ScannerFragment.java +++ b/app/src/main/java/com/m2049r/xmrwallet/ScannerFragment.java @@ -28,8 +28,7 @@ import android.widget.Toast; import com.google.zxing.BarcodeFormat; import com.google.zxing.Result; -import com.m2049r.xmrwallet.model.WalletManager; -import com.m2049r.xmrwallet.util.Helper; +import com.m2049r.xmrwallet.util.BarcodeData; import me.dm7.barcodescanner.zxing.ZXingScannerView; @@ -39,9 +38,7 @@ public class ScannerFragment extends Fragment implements ZXingScannerView.Result Listener activityCallback; public interface Listener { - void onAddressScanned(String address, String paymentId); - - boolean isPaymentIdValid(String paymentId); + boolean onAddressScanned(String uri); } private ZXingScannerView mScannerView; @@ -61,32 +58,16 @@ public class ScannerFragment extends Fragment implements ZXingScannerView.Result mScannerView.startCamera(); } - static final String URI_PREFIX = "monero:"; - static final String PAYMENTID_STRING = "?tx_payment_id="; + static final String QR_SCHEME = "monero:"; + static final String QR_PAYMENTID = "tx_payment_id"; + static final String QR_AMOUNT = "tx_amount"; @Override public void handleResult(Result rawResult) { - Log.d(TAG, rawResult.getBarcodeFormat().toString() + "/" + rawResult.getText()); - String text = rawResult.getText(); + //Log.d(TAG, rawResult.getBarcodeFormat().toString() + "/" + rawResult.getText()); if ((rawResult.getBarcodeFormat() == BarcodeFormat.QR_CODE) && - (text.startsWith(URI_PREFIX))) { - String address = null; - String paymentId = null; - String s = text.substring(URI_PREFIX.length()); - if (s.length() == 95) { - address = s; - } else { - int i = s.indexOf(PAYMENTID_STRING); - if ((i == 95) && (s.length() == (95 + PAYMENTID_STRING.length() + 16))) { - address = s.substring(0, 95); - paymentId = s.substring(95 + PAYMENTID_STRING.length()); - if (!activityCallback.isPaymentIdValid(paymentId)) { - address = null; - } - } - } - if (Helper.isAddressOk(address, WalletManager.getInstance().isTestNet())) { - activityCallback.onAddressScanned(address, paymentId); + (rawResult.getText().startsWith(QR_SCHEME))) { + if (activityCallback.onAddressScanned(rawResult.getText())) { return; } else { Toast.makeText(getActivity(), getString(R.string.send_qr_address_invalid), Toast.LENGTH_SHORT).show(); @@ -120,7 +101,7 @@ public class ScannerFragment extends Fragment implements ZXingScannerView.Result @Override public void onAttach(Context context) { super.onAttach(context); - Log.d(TAG, "attaching scan"); + //Log.d(TAG, "attaching scan"); if (context instanceof Listener) { this.activityCallback = (Listener) context; } else { diff --git a/app/src/main/java/com/m2049r/xmrwallet/SendFragment.java b/app/src/main/java/com/m2049r/xmrwallet/SendFragment.java index f690768..4d196c7 100644 --- a/app/src/main/java/com/m2049r/xmrwallet/SendFragment.java +++ b/app/src/main/java/com/m2049r/xmrwallet/SendFragment.java @@ -373,26 +373,40 @@ public class SendFragment extends Fragment { @Override public void onResume() { super.onResume(); + Log.d(TAG, "onResume"); BarcodeData data = activityCallback.getScannedData(); if (data != null) { String scannedAddress = data.address; if (scannedAddress != null) { etAddress.setText(scannedAddress); + } else { + etAddress.getText().clear(); } String scannedPaymenId = data.paymentId; if (scannedPaymenId != null) { etPaymentId.setText(scannedPaymenId); + } else { + etPaymentId.getText().clear(); + } + if (data.amount >= 0) { + String scannedAmount = Helper.getDisplayAmount(data.amount); + etAmount.setText(scannedAmount); + } else { + etAmount.getText().clear(); } - } - // jump to first empty field - if (etAddress.getText().toString().isEmpty()) { - etAddress.requestFocus(); - } else if (etPaymentId.getText().toString().isEmpty()) { - etPaymentId.requestFocus(); - } else { etAmount.requestFocus(); + etAmount.setSelection(etAmount.getText().length()); + } else { // no scan data + // jump to first empty field + if (etAddress.getText().toString().isEmpty()) { + etAddress.requestFocus(); + } else if (etPaymentId.getText().toString().isEmpty()) { + etPaymentId.requestFocus(); + } else { + etAmount.requestFocus(); + etAmount.setSelection(etAmount.getText().length()); + } } - Log.d(TAG, "onResume"); } @Override diff --git a/app/src/main/java/com/m2049r/xmrwallet/WalletActivity.java b/app/src/main/java/com/m2049r/xmrwallet/WalletActivity.java index f6f1915..9b71fae 100644 --- a/app/src/main/java/com/m2049r/xmrwallet/WalletActivity.java +++ b/app/src/main/java/com/m2049r/xmrwallet/WalletActivity.java @@ -21,6 +21,8 @@ import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.content.pm.PackageManager; +import android.net.Uri; +import android.net.UrlQuerySanitizer; import android.os.Bundle; import android.os.IBinder; import android.os.PowerManager; @@ -45,6 +47,9 @@ import com.m2049r.xmrwallet.util.BarcodeData; import com.m2049r.xmrwallet.util.Helper; import com.m2049r.xmrwallet.util.TxData; +import java.util.HashMap; +import java.util.Map; + public class WalletActivity extends AppCompatActivity implements WalletFragment.Listener, WalletService.Observer, SendFragment.Listener, TxFragment.Listener, GenerateReviewFragment.ListenerWithWallet, @@ -563,7 +568,7 @@ public class WalletActivity extends AppCompatActivity implements WalletFragment. @Override public boolean isPaymentIdValid(String paymentId) { - return getWallet().isPaymentIdValid(paymentId); + return Wallet.isPaymentIdValid(paymentId); } @Override @@ -573,9 +578,9 @@ public class WalletActivity extends AppCompatActivity implements WalletFragment. void popFragmentStack(String name) { if (name == null) { - getFragmentManager().popBackStack(); + getSupportFragmentManager().popBackStack(); } else { - getFragmentManager().popBackStack(name, FragmentManager.POP_BACK_STACK_INCLUSIVE); + getSupportFragmentManager().popBackStack(name, FragmentManager.POP_BACK_STACK_INCLUSIVE); } } @@ -626,12 +631,61 @@ public class WalletActivity extends AppCompatActivity implements WalletFragment. private BarcodeData scannedData = null; @Override - public void onAddressScanned(String address, String paymentId) { - // Log.d(TAG, "got " + address); - scannedData = new BarcodeData(address, paymentId); - popFragmentStack(null); + public boolean onAddressScanned(String uri) { + BarcodeData bcData = parseMoneroUri(uri); + if (bcData != null) { + this.scannedData = bcData; + popFragmentStack(null); + return true; + } else { + return false; + } + } + + /** + * 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)) { + address = null; + } + + if (Helper.isAddressOk(address, WalletManager.getInstance().isTestNet())) { + return new BarcodeData(address, paymentId, amount); + } + return null; } + @Override public BarcodeData getScannedData() { BarcodeData data = scannedData; @@ -640,7 +694,8 @@ public class WalletActivity extends AppCompatActivity implements WalletFragment. } @Override - public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], @NonNull int[] grantResults) { + public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], + @NonNull int[] grantResults) { Log.d(TAG, "onRequestPermissionsResult()"); switch (requestCode) { case Helper.PERMISSIONS_REQUEST_CAMERA: diff --git a/app/src/main/java/com/m2049r/xmrwallet/WalletFragment.java b/app/src/main/java/com/m2049r/xmrwallet/WalletFragment.java index ee48c7d..ca53a84 100644 --- a/app/src/main/java/com/m2049r/xmrwallet/WalletFragment.java +++ b/app/src/main/java/com/m2049r/xmrwallet/WalletFragment.java @@ -34,6 +34,7 @@ import android.widget.TextView; import com.m2049r.xmrwallet.layout.TransactionInfoAdapter; import com.m2049r.xmrwallet.model.TransactionInfo; import com.m2049r.xmrwallet.model.Wallet; +import com.m2049r.xmrwallet.util.Helper; import java.text.NumberFormat; import java.util.List; @@ -62,9 +63,9 @@ public class WalletFragment extends Fragment implements TransactionInfoAdapter.O clProgress = (ConstraintLayout) view.findViewById(R.id.clProgress); llUnconfirmedAmount = (LinearLayout) view.findViewById(R.id.llUnconfirmedAmount); tvBalance = (TextView) view.findViewById(R.id.tvBalance); - tvBalance.setText(getDisplayAmount(0)); + tvBalance.setText(Helper.getDisplayAmount(0)); tvUnconfirmedAmount = (TextView) view.findViewById(R.id.tvUnconfirmedAmount); - tvUnconfirmedAmount.setText(getDisplayAmount(0)); + tvUnconfirmedAmount.setText(Helper.getDisplayAmount(0)); tvBlockHeightProgress = (TextView) view.findViewById(R.id.tvBlockHeightProgress); bSend = (Button) view.findViewById(R.id.bSend); @@ -168,23 +169,6 @@ public class WalletFragment extends Fragment implements TransactionInfoAdapter.O private long firstBlock = 0; private String walletTitle = null; - private String getDisplayAmount(long amount) { - String s = Wallet.getDisplayAmount(amount); - int lastZero = 0; - int decimal = 0; - for (int i = s.length() - 1; i >= 0; i--) { - if ((lastZero == 0) && (s.charAt(i) != '0')) lastZero = i + 1; - // TODO i18n - if (s.charAt(i) == '.') { - decimal = i; - break; - } - } - //Log.d(TAG, decimal + "/" + lastZero + "/" + s); - int cutoff = Math.max(lastZero, decimal + 2); - return s.substring(0, cutoff); - } - private void updateStatus(Wallet wallet) { Log.d(TAG, "updateStatus()"); if (walletTitle == null) { @@ -193,8 +177,8 @@ public class WalletFragment extends Fragment implements TransactionInfoAdapter.O } long balance = wallet.getBalance(); long unlockedBalance = wallet.getUnlockedBalance(); - tvBalance.setText(getDisplayAmount(unlockedBalance)); - tvUnconfirmedAmount.setText(getDisplayAmount(balance - unlockedBalance)); + tvBalance.setText(Helper.getDisplayAmount(unlockedBalance)); + tvUnconfirmedAmount.setText(Helper.getDisplayAmount(balance - unlockedBalance)); // balance cannot be less than unlockedBalance /*if (balance != unlockedBalance) { llPendingAmount.setVisibility(View.VISIBLE); diff --git a/app/src/main/java/com/m2049r/xmrwallet/util/BarcodeData.java b/app/src/main/java/com/m2049r/xmrwallet/util/BarcodeData.java index 6c94c49..e035e33 100644 --- a/app/src/main/java/com/m2049r/xmrwallet/util/BarcodeData.java +++ b/app/src/main/java/com/m2049r/xmrwallet/util/BarcodeData.java @@ -19,10 +19,11 @@ package com.m2049r.xmrwallet.util; public class BarcodeData { public String address = null; public String paymentId = null; - //String amount = null: + public long amount = -1; - public BarcodeData(String address, String paymentId) { + public BarcodeData(String address, String paymentId, long amount) { this.address = address; this.paymentId = paymentId; + this.amount = amount; } } \ No newline at end of file diff --git a/app/src/main/java/com/m2049r/xmrwallet/util/Helper.java b/app/src/main/java/com/m2049r/xmrwallet/util/Helper.java index e94064b..f2b6b2a 100644 --- a/app/src/main/java/com/m2049r/xmrwallet/util/Helper.java +++ b/app/src/main/java/com/m2049r/xmrwallet/util/Helper.java @@ -27,6 +27,7 @@ import android.view.WindowManager; import android.view.inputmethod.InputMethodManager; import com.m2049r.xmrwallet.R; +import com.m2049r.xmrwallet.model.Wallet; import com.m2049r.xmrwallet.model.WalletManager; import java.io.File; @@ -135,5 +136,20 @@ public class Helper { return ((address.length() == 95) && ("4".indexOf(address.charAt(0)) >= 0)); } } - + static public String getDisplayAmount(long amount) { + String s = Wallet.getDisplayAmount(amount); + int lastZero = 0; + int decimal = 0; + for (int i = s.length() - 1; i >= 0; i--) { + if ((lastZero == 0) && (s.charAt(i) != '0')) lastZero = i + 1; + // TODO i18n + if (s.charAt(i) == '.') { + decimal = i; + break; + } + } + //Log.d(TAG, decimal + "/" + lastZero + "/" + s); + int cutoff = Math.max(lastZero, decimal + 2); + return s.substring(0, cutoff); + } }