From f5535dbefa90d662b13485eb583d1f1349b41e22 Mon Sep 17 00:00:00 2001
From: m2049r <30435443+m2049r@users.noreply.github.com>
Date: Wed, 30 Aug 2017 19:54:27 +0200
Subject: [PATCH] QR Scanning incl. payment id

---
 .idea/libraries/core_1_9_8.xml                |  10 ++
 .idea/libraries/core_3_3_0.xml                |  11 ++
 .idea/libraries/zxing_1_9_8.xml               |  10 ++
 app/build.gradle                              |   1 +
 app/src/main/AndroidManifest.xml              |   1 +
 .../com/m2049r/xmrwallet/ScannerFragment.java | 131 ++++++++++++++++++
 .../com/m2049r/xmrwallet/SendFragment.java    |  60 +++++++-
 .../com/m2049r/xmrwallet/WalletActivity.java  |  64 ++++++++-
 .../xmrwallet/service/WalletService.java      |   1 +
 .../m2049r/xmrwallet/util/BarcodeData.java    |  28 ++++
 .../com/m2049r/xmrwallet/util/Helper.java     |  29 ++++
 app/src/main/res/layout/send_fragment.xml     | 107 ++++++++------
 app/src/main/res/values/strings.xml           |   9 +-
 13 files changed, 411 insertions(+), 51 deletions(-)
 create mode 100644 .idea/libraries/core_1_9_8.xml
 create mode 100644 .idea/libraries/core_3_3_0.xml
 create mode 100644 .idea/libraries/zxing_1_9_8.xml
 create mode 100644 app/src/main/java/com/m2049r/xmrwallet/ScannerFragment.java
 create mode 100644 app/src/main/java/com/m2049r/xmrwallet/util/BarcodeData.java

diff --git a/.idea/libraries/core_1_9_8.xml b/.idea/libraries/core_1_9_8.xml
new file mode 100644
index 00000000..0be1e35c
--- /dev/null
+++ b/.idea/libraries/core_1_9_8.xml
@@ -0,0 +1,10 @@
+<component name="libraryTable">
+  <library name="core-1.9.8">
+    <CLASSES>
+      <root url="jar://$USER_HOME$/.android/build-cache/db7eb580bfa6467818ef12c5f1fa42273b50aa3a/output/jars/classes.jar!/" />
+      <root url="file://$USER_HOME$/.android/build-cache/db7eb580bfa6467818ef12c5f1fa42273b50aa3a/output/res" />
+    </CLASSES>
+    <JAVADOC />
+    <SOURCES />
+  </library>
+</component>
\ No newline at end of file
diff --git a/.idea/libraries/core_3_3_0.xml b/.idea/libraries/core_3_3_0.xml
new file mode 100644
index 00000000..de46baa5
--- /dev/null
+++ b/.idea/libraries/core_3_3_0.xml
@@ -0,0 +1,11 @@
+<component name="libraryTable">
+  <library name="core-3.3.0">
+    <CLASSES>
+      <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.google.zxing/core/3.3.0/73c49077166faa4c3c0059c5f583d1d7bd1475fe/core-3.3.0.jar!/" />
+    </CLASSES>
+    <JAVADOC />
+    <SOURCES>
+      <root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.google.zxing/core/3.3.0/39d966e052e28fc7d83793b02e0af97ccf955745/core-3.3.0-sources.jar!/" />
+    </SOURCES>
+  </library>
+</component>
\ No newline at end of file
diff --git a/.idea/libraries/zxing_1_9_8.xml b/.idea/libraries/zxing_1_9_8.xml
new file mode 100644
index 00000000..e9af3efd
--- /dev/null
+++ b/.idea/libraries/zxing_1_9_8.xml
@@ -0,0 +1,10 @@
+<component name="libraryTable">
+  <library name="zxing-1.9.8">
+    <CLASSES>
+      <root url="jar://$USER_HOME$/.android/build-cache/8d8f2e0e10c7af73321454f6fa481e8e5438e654/output/jars/classes.jar!/" />
+      <root url="file://$USER_HOME$/.android/build-cache/8d8f2e0e10c7af73321454f6fa481e8e5438e654/output/res" />
+    </CLASSES>
+    <JAVADOC />
+    <SOURCES />
+  </library>
+</component>
\ No newline at end of file
diff --git a/app/build.gradle b/app/build.gradle
index ecd41f6e..36a9850f 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -46,5 +46,6 @@ dependencies {
     compile 'com.android.support:support-v4:25.3.1'
     compile 'com.android.support:recyclerview-v7:25.3.1'
     compile 'com.android.support:cardview-v7:25.3.1'
+    compile 'me.dm7.barcodescanner:zxing:1.9.8'
     testCompile 'junit:junit:4.12'
 }
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 0d7245cd..0f9eb50f 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -7,6 +7,7 @@
     <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
     <uses-permission android:name="android.permission.WAKE_LOCK" />
+    <uses-permission android:name="android.permission.CAMERA" />
 
     <application
         android:allowBackup="true"
diff --git a/app/src/main/java/com/m2049r/xmrwallet/ScannerFragment.java b/app/src/main/java/com/m2049r/xmrwallet/ScannerFragment.java
new file mode 100644
index 00000000..9678a317
--- /dev/null
+++ b/app/src/main/java/com/m2049r/xmrwallet/ScannerFragment.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (c) 2017 dm77, m2049r
+ *
+ * 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;
+
+import android.app.Fragment;
+import android.content.Context;
+import android.os.Bundle;
+import android.os.Handler;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+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 me.dm7.barcodescanner.zxing.ZXingScannerView;
+
+public class ScannerFragment extends Fragment implements ZXingScannerView.ResultHandler {
+    static final String TAG = "ScannerFragment";
+
+    Listener activityCallback;
+
+    public interface Listener {
+        void onAddressScanned(String address, String paymentId);
+
+        boolean isPaymentIdValid(String paymentId);
+    }
+
+    private ZXingScannerView mScannerView;
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+        Log.d(TAG, "onCreateView");
+        mScannerView = new ZXingScannerView(getActivity());
+        return mScannerView;
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        Log.d(TAG, "onResume");
+        mScannerView.setResultHandler(this);
+        mScannerView.startCamera();
+    }
+
+    static final String URI_PREFIX = "monero:";
+    static final String PAYMENTID_STRING = "?tx_payment_id=";
+
+    @Override
+    public void handleResult(Result rawResult) {
+        Log.d(TAG, rawResult.getBarcodeFormat().toString() + "/" + rawResult.getText());
+        String text = 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);
+                return;
+            } else {
+                Toast.makeText(getActivity(), getString(R.string.send_qr_address_invalid), Toast.LENGTH_SHORT).show();
+            }
+        } else {
+            Toast.makeText(getActivity(), getString(R.string.send_qr_invalid), Toast.LENGTH_SHORT).show();
+        }
+
+        // Note from dm77:
+        // * Wait 2 seconds to resume the preview.
+        // * 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);
+    }
+
+    @Override
+    public void onPause() {
+        Log.d(TAG, "onPause");
+        mScannerView.stopCamera();
+        super.onPause();
+    }
+
+    @Override
+    public void onAttach(Context context) {
+        super.onAttach(context);
+        Log.d(TAG, "attaching scan");
+        if (context instanceof Listener) {
+            this.activityCallback = (Listener) context;
+        } else {
+            throw new ClassCastException(context.toString()
+                    + " must implement Listener");
+        }
+    }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/m2049r/xmrwallet/SendFragment.java b/app/src/main/java/com/m2049r/xmrwallet/SendFragment.java
index 5a9ae094..d88b5c89 100644
--- a/app/src/main/java/com/m2049r/xmrwallet/SendFragment.java
+++ b/app/src/main/java/com/m2049r/xmrwallet/SendFragment.java
@@ -20,10 +20,12 @@ import android.app.Fragment;
 import android.content.Context;
 import android.content.DialogInterface;
 import android.os.Bundle;
+import android.os.Handler;
 import android.support.v7.app.AlertDialog;
 import android.text.Editable;
 import android.text.InputType;
 import android.text.TextWatcher;
+import android.util.Log;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -40,14 +42,16 @@ import com.m2049r.xmrwallet.model.PendingTransaction;
 import com.m2049r.xmrwallet.model.Wallet;
 import com.m2049r.xmrwallet.model.WalletManager;
 import com.m2049r.xmrwallet.util.Helper;
+import com.m2049r.xmrwallet.util.BarcodeData;
 import com.m2049r.xmrwallet.util.TxData;
 
 public class SendFragment extends Fragment {
-    static final String TAG = "GenerateFragment";
+    static final String TAG = "SendFragment";
 
     EditText etAddress;
     EditText etPaymentId;
     EditText etAmount;
+    Button bAddress;
     Button bSweep;
     Spinner sMixin;
     Spinner sPriority;
@@ -80,6 +84,7 @@ public class SendFragment extends Fragment {
         etAddress = (EditText) view.findViewById(R.id.etAddress);
         etPaymentId = (EditText) view.findViewById(R.id.etPaymentId);
         etAmount = (EditText) view.findViewById(R.id.etAmount);
+        bAddress = (Button) view.findViewById(R.id.bAddress);
         bSweep = (Button) view.findViewById(R.id.bSweep);
         bPrepareSend = (Button) view.findViewById(R.id.bPrepareSend);
         bPaymentId = (Button) view.findViewById(R.id.bPaymentId);
@@ -194,6 +199,13 @@ public class SendFragment extends Fragment {
             }
         });
 
+        bAddress.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                activityCallback.onScanAddress();
+            }
+        });
+
         bPaymentId.setOnClickListener(new View.OnClickListener() {
             @Override
             public void onClick(View v) {
@@ -231,7 +243,14 @@ public class SendFragment extends Fragment {
                 if (testnet) {
                     send();
                 } else {
-                    bReallySend.setVisibility(View.VISIBLE);
+                    etNotes.setEnabled(false);
+                    Handler handler = new Handler();
+                    handler.postDelayed(new Runnable() {
+                        @Override
+                        public void run() {
+                            bReallySend.setVisibility(View.VISIBLE);
+                        }
+                    }, 1000);
                 }
             }
         });
@@ -256,11 +275,7 @@ public class SendFragment extends Fragment {
 
     private boolean addressOk() {
         String address = etAddress.getText().toString();
-        if (WalletManager.getInstance().isTestNet()) {
-            return ((address.length() == 95) && ("9A".indexOf(address.charAt(0)) >= 0));
-        } else {
-            return ((address.length() == 95) && ("4".indexOf(address.charAt(0)) >= 0));
-        }
+        return Helper.isAddressOk(address, WalletManager.getInstance().isTestNet());
     }
 
     private boolean amountOk() {
@@ -321,6 +336,8 @@ public class SendFragment extends Fragment {
         bSweep.setEnabled(true);
         bPrepareSend.setEnabled(true);
         llConfirmSend.setVisibility(View.GONE);
+        bSend.setEnabled(true);
+        etNotes.setEnabled(true);
         bReallySend.setVisibility(View.GONE);
     }
 
@@ -347,6 +364,35 @@ public class SendFragment extends Fragment {
 
         void onDisposeRequest();
 
+        void onScanAddress();
+
+        BarcodeData getScannedData();
+
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        BarcodeData data = activityCallback.getScannedData();
+        if (data != null) {
+            String scannedAddress = data.address;
+            if (scannedAddress != null) {
+                etAddress.setText(scannedAddress);
+            }
+            String scannedPaymenId = data.paymentId;
+            if (scannedPaymenId != null) {
+                etPaymentId.setText(scannedPaymenId);
+            }
+        }
+        // jump to first empty field
+        if (etAddress.getText().toString().isEmpty()) {
+            etAddress.requestFocus();
+        } else if (etPaymentId.getText().toString().isEmpty()) {
+            etPaymentId.requestFocus();
+        } else {
+            etAmount.requestFocus();
+        }
+        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 e7511131..b5c1d054 100644
--- a/app/src/main/java/com/m2049r/xmrwallet/WalletActivity.java
+++ b/app/src/main/java/com/m2049r/xmrwallet/WalletActivity.java
@@ -23,9 +23,11 @@ import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.ServiceConnection;
+import android.content.pm.PackageManager;
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.PowerManager;
+import android.support.annotation.NonNull;
 import android.support.v7.app.AppCompatActivity;
 import android.support.v7.widget.Toolbar;
 import android.util.Log;
@@ -39,10 +41,14 @@ import com.m2049r.xmrwallet.model.TransactionInfo;
 import com.m2049r.xmrwallet.model.Wallet;
 import com.m2049r.xmrwallet.model.WalletManager;
 import com.m2049r.xmrwallet.service.WalletService;
+import com.m2049r.xmrwallet.util.BarcodeData;
+import com.m2049r.xmrwallet.util.Helper;
 import com.m2049r.xmrwallet.util.TxData;
 
 public class WalletActivity extends AppCompatActivity implements WalletFragment.Listener,
-        WalletService.Observer, SendFragment.Listener, TxFragment.Listener, GenerateReviewFragment.ListenerWithWallet {
+        WalletService.Observer, SendFragment.Listener, TxFragment.Listener,
+        GenerateReviewFragment.ListenerWithWallet,
+        ScannerFragment.Listener {
     private static final String TAG = "WalletActivity";
 
     public static final String REQUEST_ID = "id";
@@ -596,4 +602,60 @@ public class WalletActivity extends AppCompatActivity implements WalletFragment.
     public void onDisposeRequest() {
         getWallet().disposePendingTransaction();
     }
+
+
+    private void startScanFragment() {
+        Fragment fragment = getFragmentManager().findFragmentById(R.id.fragment_container);
+        if (fragment instanceof SendFragment) {
+            Bundle extras = new Bundle();
+            replaceFragment(new ScannerFragment(), null, extras);
+        }
+    }
+
+    /// QR scanner callbacks
+    @Override
+    public void onScanAddress() {
+        if (Helper.getCameraPermission(this)) {
+            startScanFragment();
+        } else {
+            Log.i(TAG, "Waiting for permissions");
+        }
+
+    }
+
+    private BarcodeData scannedData = null;
+
+    @Override
+    public void onAddressScanned(String address, String paymentId) {
+        // Log.d(TAG, "got " + address);
+        scannedData = new BarcodeData(address, paymentId);
+        popFragmentStack(null);
+    }
+
+    @Override
+    public BarcodeData getScannedData() {
+        BarcodeData data = scannedData;
+        scannedData = null;
+        return data;
+    }
+
+    @Override
+    public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], @NonNull int[] grantResults) {
+        Log.d(TAG, "onRequestPermissionsResult()");
+        switch (requestCode) {
+            case Helper.PERMISSIONS_REQUEST_CAMERA:
+                // If request is cancelled, the result arrays are empty.
+                if (grantResults.length > 0
+                        && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
+                    startScanFragment();
+                } else {
+                    String msg = getString(R.string.message_camera_not_permitted);
+                    Log.e(TAG, msg);
+                    Toast.makeText(this, msg, Toast.LENGTH_LONG).show();
+                }
+                break;
+            default:
+        }
+    }
+
 }
\ No newline at end of file
diff --git a/app/src/main/java/com/m2049r/xmrwallet/service/WalletService.java b/app/src/main/java/com/m2049r/xmrwallet/service/WalletService.java
index 22d67d0d..374c83c0 100644
--- a/app/src/main/java/com/m2049r/xmrwallet/service/WalletService.java
+++ b/app/src/main/java/com/m2049r/xmrwallet/service/WalletService.java
@@ -127,6 +127,7 @@ public class WalletService extends Service {
                     boolean fullRefresh = false;
                     updateDaemonState(wallet, wallet.isSynchronized() ? height : 0);
                     if (!wallet.isSynchronized()) {
+                        updated = true;
                         // we want to see our transactions as they come in
                         wallet.getHistory().refresh();
                         int txCount = wallet.getHistory().getCount();
diff --git a/app/src/main/java/com/m2049r/xmrwallet/util/BarcodeData.java b/app/src/main/java/com/m2049r/xmrwallet/util/BarcodeData.java
new file mode 100644
index 00000000..6c94c490
--- /dev/null
+++ b/app/src/main/java/com/m2049r/xmrwallet/util/BarcodeData.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2017 m2049r
+ *
+ * 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;
+
+public class BarcodeData {
+    public String address = null;
+    public String paymentId = null;
+    //String amount = null:
+
+    public BarcodeData(String address, String paymentId) {
+        this.address = address;
+        this.paymentId = paymentId;
+    }
+}
\ 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 f3454527..e94064b1 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.WalletManager;
 
 import java.io.File;
 
@@ -71,6 +72,24 @@ public class Helper {
         }
     }
 
+    public static final int PERMISSIONS_REQUEST_CAMERA = 1;
+
+    static public boolean getCameraPermission(Activity context) {
+        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
+            if (context.checkSelfPermission(Manifest.permission.CAMERA)
+                    == PackageManager.PERMISSION_DENIED) {
+                Log.d(TAG, "Permission denied for CAMERA - requesting it");
+                String[] permissions = {Manifest.permission.CAMERA};
+                context.requestPermissions(permissions, PERMISSIONS_REQUEST_CAMERA);
+                return false;
+            } else {
+                return true;
+            }
+        } else {
+            return true;
+        }
+    }
+
     static public String getWalletPath(Context context, String aWalletName) {
         File walletDir = getStorageRoot(context);
         //d(TAG, "walletdir=" + walletDir.getAbsolutePath());
@@ -107,4 +126,14 @@ public class Helper {
     static public void hideKeyboardAlways(Activity act) {
         act.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
     }
+
+    static public boolean isAddressOk(String address, boolean testnet) {
+        if (address == null) return false;
+        if (testnet) {
+            return ((address.length() == 95) && ("9A".indexOf(address.charAt(0)) >= 0));
+        } else {
+            return ((address.length() == 95) && ("4".indexOf(address.charAt(0)) >= 0));
+        }
+    }
+
 }
diff --git a/app/src/main/res/layout/send_fragment.xml b/app/src/main/res/layout/send_fragment.xml
index b1a15e9b..135132da 100644
--- a/app/src/main/res/layout/send_fragment.xml
+++ b/app/src/main/res/layout/send_fragment.xml
@@ -16,7 +16,7 @@
 
             <Spinner
                 android:id="@+id/sMixin"
-                android:layout_width="0dp"
+                android:layout_width="0sp"
                 android:layout_height="match_parent"
                 android:layout_weight="1"
                 android:entries="@array/mixin"
@@ -24,7 +24,7 @@
 
             <Spinner
                 android:id="@+id/sPriority"
-                android:layout_width="0dp"
+                android:layout_width="0sp"
                 android:layout_height="match_parent"
                 android:layout_weight="1"
                 android:entries="@array/priority"
@@ -32,25 +32,49 @@
 
         </LinearLayout>
 
-        <EditText
-            android:id="@+id/etAddress"
+        <LinearLayout
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:hint="@string/send_address_hint"
-            android:imeOptions="actionNext"
-            android:inputType="textMultiLine"
-            android:textAlignment="center"
-            android:textSize="16sp" />
+            android:orientation="horizontal"
+            android:layout_marginBottom="4sp"
+            android:layout_marginTop="4sp"
+            android:weightSum="10">
+
+            <EditText
+                android:id="@+id/etAddress"
+                android:layout_width="0sp"
+                android:layout_height="wrap_content"
+                android:layout_weight="8"
+                android:hint="@string/send_address_hint"
+                android:imeOptions="actionNext"
+                android:inputType="textMultiLine"
+                android:textAlignment="center"
+                android:textSize="16sp" />
+
+            <Button
+                android:id="@+id/bAddress"
+                android:layout_width="0sp"
+                android:layout_height="wrap_content"
+                android:layout_gravity="center"
+                android:minHeight="36sp"
+                android:layout_weight="2"
+                android:background="@color/colorPrimary"
+                android:enabled="true"
+                android:text="@string/send_qr_hint"
+                android:textSize="12sp" />
+        </LinearLayout>
 
         <LinearLayout
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:orientation="horizontal"
+            android:layout_marginBottom="4sp"
+            android:layout_marginTop="4sp"
             android:weightSum="10">
 
             <EditText
                 android:id="@+id/etPaymentId"
-                android:layout_width="0dp"
+                android:layout_width="0sp"
                 android:layout_height="wrap_content"
                 android:layout_weight="8"
                 android:hint="@string/send_paymentid_hint"
@@ -61,10 +85,10 @@
 
             <Button
                 android:id="@+id/bPaymentId"
-                android:layout_width="0dp"
-                android:layout_height="match_parent"
-                android:layout_marginBottom="8dp"
-                android:layout_marginTop="8dp"
+                android:layout_width="0sp"
+                android:layout_height="wrap_content"
+                android:layout_gravity="center"
+                android:minHeight="36sp"
                 android:layout_weight="2"
                 android:background="@color/colorPrimary"
                 android:enabled="true"
@@ -76,11 +100,13 @@
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:orientation="horizontal"
+            android:layout_marginBottom="4sp"
+            android:layout_marginTop="4sp"
             android:weightSum="10">
 
             <EditText
                 android:id="@+id/etAmount"
-                android:layout_width="0dp"
+                android:layout_width="0sp"
                 android:layout_height="wrap_content"
                 android:layout_weight="8"
                 android:hint="@string/send_amount_hint"
@@ -91,10 +117,8 @@
 
             <Button
                 android:id="@+id/bSweep"
-                android:layout_width="0dp"
+                android:layout_width="0sp"
                 android:layout_height="match_parent"
-                android:layout_marginBottom="8dp"
-                android:layout_marginTop="8dp"
                 android:layout_weight="2"
                 android:background="@color/colorPrimary"
                 android:enabled="true"
@@ -106,8 +130,9 @@
             android:id="@+id/bPrepareSend"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:layout_marginBottom="8dp"
-            android:layout_marginTop="8dp"
+            android:layout_marginBottom="4sp"
+            android:layout_marginTop="4sp"
+            android:minHeight="36sp"
             android:background="@color/colorPrimary"
             android:enabled="false"
             android:text="@string/send_prepare_hint" />
@@ -124,18 +149,19 @@
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:orientation="vertical"
+            android:layout_marginBottom="4sp"
+            android:layout_marginTop="4sp"
             android:visibility="gone">
 
             <Button
                 android:id="@+id/bDispose"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
-                android:layout_marginBottom="8dp"
-                android:layout_marginTop="8dp"
+                android:minHeight="36sp"
+                android:layout_marginBottom="4sp"
                 android:background="@color/colorPrimary"
                 android:text="@string/send_dispose_hint" />
 
-
             <LinearLayout
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
@@ -144,9 +170,9 @@
 
                 <TextView
                     android:id="@+id/tvTxAmountLabel"
-                    android:layout_width="0dp"
+                    android:layout_width="0sp"
                     android:layout_height="wrap_content"
-                    android:layout_marginRight="8dp"
+                    android:layout_marginRight="8sp"
                     android:layout_weight="1"
                     android:text="@string/send_amount_label"
                     android:textAlignment="textEnd"
@@ -155,9 +181,9 @@
 
                 <TextView
                     android:id="@+id/tvTxAmount"
-                    android:layout_width="0dp"
+                    android:layout_width="0sp"
                     android:layout_height="wrap_content"
-                    android:layout_marginLeft="8dp"
+                    android:layout_marginLeft="8sp"
                     android:layout_weight="2"
                     android:textAlignment="textEnd"
                     android:textSize="20sp" />
@@ -171,9 +197,9 @@
 
                 <TextView
                     android:id="@+id/tvTxFeeLabel"
-                    android:layout_width="0dp"
+                    android:layout_width="0sp"
                     android:layout_height="wrap_content"
-                    android:layout_marginRight="8dp"
+                    android:layout_marginRight="4sp"
                     android:layout_weight="1"
                     android:text="@string/send_fee_label"
                     android:textAlignment="textEnd"
@@ -182,9 +208,9 @@
 
                 <TextView
                     android:id="@+id/tvTxFee"
-                    android:layout_width="0dp"
+                    android:layout_width="0sp"
                     android:layout_height="wrap_content"
-                    android:layout_marginLeft="8dp"
+                    android:layout_marginLeft="8sp"
                     android:layout_weight="2"
                     android:textAlignment="textEnd"
                     android:textSize="20sp" />
@@ -198,9 +224,9 @@
 
                 <TextView
                     android:id="@+id/tvTxDustLabel"
-                    android:layout_width="0dp"
+                    android:layout_width="0sp"
                     android:layout_height="wrap_content"
-                    android:layout_marginRight="8dp"
+                    android:layout_marginRight="8sp"
                     android:layout_weight="1"
                     android:text="@string/send_dust_label"
                     android:textAlignment="textEnd"
@@ -209,9 +235,9 @@
 
                 <TextView
                     android:id="@+id/tvTxDust"
-                    android:layout_width="0dp"
+                    android:layout_width="0sp"
                     android:layout_height="wrap_content"
-                    android:layout_marginLeft="8dp"
+                    android:layout_marginLeft="8sp"
                     android:layout_weight="2"
                     android:textAlignment="textEnd"
                     android:textSize="20sp" />
@@ -221,7 +247,7 @@
                 android:id="@+id/etNotes"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
-                android:layout_marginTop="8dp"
+                android:layout_marginTop="4sp"
                 android:hint="@string/send_notes_hint"
                 android:imeOptions="actionDone"
                 android:inputType="textMultiLine"
@@ -232,8 +258,9 @@
                 android:id="@+id/bSend"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
-                android:layout_marginBottom="8dp"
-                android:layout_marginTop="8dp"
+                android:layout_marginBottom="4sp"
+                android:layout_marginTop="4sp"
+                android:minHeight="36sp"
                 android:background="@color/colorPrimary"
                 android:text="@string/send_send_hint" />
 
@@ -241,8 +268,8 @@
                 android:id="@+id/bReallySend"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
-                android:layout_marginBottom="8dp"
-                android:layout_marginTop="8dp"
+                android:layout_marginBottom="4sp"
+                android:layout_marginTop="4sp"
                 android:background="@color/moneroOrange"
                 android:text="@string/send_really_send_hint"
                 android:visibility="gone" />
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 1d3851a0..c1553fe2 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -5,7 +5,7 @@
 
     <string name="menu_info">Details</string>
 
-    <string name="prompt_daemon">[&lt;user&gt;:&lt;pass&gt;@]&lt;daemonhost&gt;[:&lt;port&gt;]</string>
+    <string name="prompt_daemon">[&lt;user&gt;:&lt;pass&gt;@]&lt;daemon&gt;[:&lt;port&gt;]</string>
     <string name="prompt_mainnet">Net Selection</string>
     <string name="connect_testnet">TestNet</string>
     <string name="connect_mainnet">MainNet</string>
@@ -58,6 +58,7 @@
     <string name="prompt_problems">Problems</string>
     <string name="message_strorage_not_writable">External Storage is not writable! Panic!</string>
     <string name="message_strorage_not_permitted">We really need those External Storage permissions!</string>
+    <string name="message_camera_not_permitted">No camera = No QR scanning!</string>
 
     <string name="generate_title">Create Wallet</string>
     <string name="generate_name_hint">Wallet Name</string>
@@ -111,11 +112,13 @@
     <string name="send_mixin_hint">Mixin</string>
     <string name="send_sweep_hint">Sweep</string>
     <string name="send_generate_paymentid_hint">Generate</string>
+    <string name="send_qr_hint">Scan</string>
     <string name="send_prepare_hint">Prepare</string>
     <string name="send_dispose_hint">Dispose (Undo)</string>
     <string name="send_send_hint">Spend my sweet Moneroj</string>
-    <string name="send_really_send_hint">I understand I am on mainnet\nTHis is real Monero!</string>
-
+    <string name="send_really_send_hint">I understand I am sending real Monero!</string>
+    <string name="send_qr_invalid">Not a monero QR Code</string>
+    <string name="send_qr_address_invalid">Invalid Monero address</string>
     <string name="send_preparing_progress">Preparing transaction</string>
 
     <string name="send_amount_label">Amount</string>