android: pass on QR binary scan result data as well
qml: add signals for QEQRScanner and fallbacks, add QEBytes container type so we can pass along byte arrays between QML and python, port qr scan occurrences to new signals.
This commit is contained in:
@@ -91,11 +91,11 @@ ElDialog {
|
||||
? qsTr('Scan another address')
|
||||
: qsTr('Scan another private key')
|
||||
})
|
||||
dialog.onFound.connect(function() {
|
||||
if (verify(dialog.scanData)) {
|
||||
dialog.onFoundText.connect(function(data) {
|
||||
if (verify(data)) {
|
||||
if (import_ta.text != '')
|
||||
import_ta.text = import_ta.text + ',\n'
|
||||
import_ta.text = import_ta.text + dialog.scanData
|
||||
import_ta.text = import_ta.text + data
|
||||
}
|
||||
dialog.close()
|
||||
})
|
||||
|
||||
@@ -62,8 +62,8 @@ ElDialog {
|
||||
var dialog = app.scanDialog.createObject(app, {
|
||||
hint: qsTr('Scan a channel backup')
|
||||
})
|
||||
dialog.onFound.connect(function() {
|
||||
channelbackup_ta.text = dialog.scanData
|
||||
dialog.onFoundText.connect(function(data) {
|
||||
channelbackup_ta.text = data
|
||||
dialog.close()
|
||||
})
|
||||
dialog.open()
|
||||
|
||||
@@ -124,9 +124,9 @@ ElDialog {
|
||||
var dialog = app.scanDialog.createObject(app, {
|
||||
hint: qsTr('Scan a node-id or a connect string')
|
||||
})
|
||||
dialog.onFound.connect(function() {
|
||||
if (channelopener.validateConnectString(dialog.scanData)) {
|
||||
channelopener.connectStr = dialog.scanData
|
||||
dialog.onFoundText.connect(function(data) {
|
||||
if (channelopener.validateConnectString(data)) {
|
||||
channelopener.connectStr = data
|
||||
node.text = channelopener.connectStr
|
||||
} else {
|
||||
var errdialog = app.messageDialog.createObject(app, {
|
||||
|
||||
@@ -2,17 +2,19 @@ import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
|
||||
import org.electrum
|
||||
|
||||
import "controls"
|
||||
|
||||
// currently not used on android, kept for future use when qt6 camera stops crashing
|
||||
ElDialog {
|
||||
id: scanDialog
|
||||
|
||||
property string scanData
|
||||
property string error
|
||||
property string hint
|
||||
|
||||
signal found
|
||||
signal foundText(data: string)
|
||||
signal foundBinary(data: Bytes)
|
||||
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
@@ -35,9 +37,8 @@ ElDialog {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
hint: scanDialog.hint
|
||||
onFound: {
|
||||
scanDialog.scanData = scanData
|
||||
scanDialog.found()
|
||||
onFoundText: (data) => {
|
||||
scanDialog.foundText(data)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -59,7 +59,10 @@ ElDialog {
|
||||
hint: Daemon.currentWallet.isLightning
|
||||
? qsTr('Scan an Invoice, an Address, an LNURL-pay, a PSBT or a Channel Backup')
|
||||
: qsTr('Scan an Invoice, an Address, an LNURL-pay or a PSBT')
|
||||
onFound: dialog.dispatch(scanData)
|
||||
|
||||
onFoundText: (data) => {
|
||||
dialog.dispatch(data)
|
||||
}
|
||||
}
|
||||
|
||||
ButtonContainer {
|
||||
|
||||
@@ -117,9 +117,9 @@ ElDialog {
|
||||
var dialog = app.scanDialog.createObject(app, {
|
||||
hint: qsTr('Scan a private key')
|
||||
})
|
||||
dialog.onFound.connect(function() {
|
||||
if (verifyPrivateKey(dialog.scanData))
|
||||
addPrivateKey(dialog.scanData)
|
||||
dialog.onFoundText.connect(function(data) {
|
||||
if (verifyPrivateKey(data))
|
||||
addPrivateKey(data)
|
||||
dialog.close()
|
||||
})
|
||||
dialog.open()
|
||||
|
||||
@@ -47,8 +47,7 @@ Item {
|
||||
? qsTr('Scan an Invoice, an Address, an LNURL-pay, a PSBT or a Channel Backup')
|
||||
: qsTr('Scan an Invoice, an Address, an LNURL-pay or a PSBT')
|
||||
})
|
||||
scanner.onFound.connect(function() {
|
||||
var data = scanner.scanData
|
||||
scanner.onFoundText.connect(function(data) {
|
||||
data = data.trim()
|
||||
if (bitcoin.isRawTx(data)) {
|
||||
app.stack.push(Qt.resolvedUrl('TxDetails.qml'), { rawtx: data })
|
||||
|
||||
@@ -10,14 +10,12 @@ Item {
|
||||
|
||||
property bool active: false
|
||||
property string url
|
||||
property string scanData
|
||||
property string hint
|
||||
|
||||
signal found
|
||||
signal foundText(data: string)
|
||||
|
||||
function restart() {
|
||||
console.log('qrscan.restart')
|
||||
scanData = ''
|
||||
qr.reset()
|
||||
start()
|
||||
}
|
||||
@@ -153,8 +151,7 @@ Item {
|
||||
function onDataChanged() {
|
||||
console.log('QR DATA: ' + qr.data)
|
||||
scanner.active = false
|
||||
scanner.scanData = qr.data
|
||||
scanner.found()
|
||||
scanner.foundText(qr.data)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -166,9 +166,9 @@ WizardComponent {
|
||||
? qsTr('Scan a cosigner master public key')
|
||||
: qsTr('Scan a master key')
|
||||
})
|
||||
dialog.onFound.connect(function() {
|
||||
if (verifyMasterKey(dialog.scanData))
|
||||
masterkey_ta.text = dialog.scanData
|
||||
dialog.onFoundText.connect(function(data) {
|
||||
if (verifyMasterKey(data))
|
||||
masterkey_ta.text = data
|
||||
else
|
||||
masterkey_ta.text = ''
|
||||
dialog.close()
|
||||
|
||||
@@ -76,11 +76,11 @@ WizardComponent {
|
||||
? qsTr('Scan another private key')
|
||||
: qsTr('Scan a private key or an address')
|
||||
})
|
||||
dialog.onFound.connect(function() {
|
||||
if (verify(dialog.scanData)) {
|
||||
dialog.onFoundText.connect(function(data) {
|
||||
if (verify(data)) {
|
||||
if (import_ta.text != '')
|
||||
import_ta.text = import_ta.text + '\n'
|
||||
import_ta.text = import_ta.text + dialog.scanData
|
||||
import_ta.text = import_ta.text + data
|
||||
}
|
||||
dialog.close()
|
||||
})
|
||||
|
||||
@@ -21,6 +21,8 @@ import androidx.core.app.ActivityCompat;
|
||||
import java.util.Arrays;
|
||||
|
||||
import de.markusfisch.android.barcodescannerview.widget.BarcodeScannerView;
|
||||
import de.markusfisch.android.zxingcpp.ZxingCpp.Result;
|
||||
import de.markusfisch.android.zxingcpp.ZxingCpp.ContentType;
|
||||
|
||||
|
||||
import org.electrum.electrum.res.R; // package set in build.gradle
|
||||
@@ -29,7 +31,7 @@ public class SimpleScannerActivity extends Activity {
|
||||
private static final int MY_PERMISSIONS_CAMERA = 1002;
|
||||
|
||||
private BarcodeScannerView mScannerView = null;
|
||||
final String TAG = "org.electrum.SimpleScannerActivity";
|
||||
final String TAG = "org.electrum.qr.SimpleScannerActivity";
|
||||
|
||||
private boolean mAlreadyRequestedPermissions = false;
|
||||
|
||||
@@ -60,7 +62,7 @@ public class SimpleScannerActivity extends Activity {
|
||||
Toast.makeText(SimpleScannerActivity.this, "Clipboard contents too large.", Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
SimpleScannerActivity.this.setResultAndClose(clipboardText);
|
||||
SimpleScannerActivity.this.setResultAndClose(null, clipboardText);
|
||||
} else {
|
||||
Toast.makeText(SimpleScannerActivity.this, "Clipboard is empty.", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
@@ -96,7 +98,7 @@ public class SimpleScannerActivity extends Activity {
|
||||
contentFrame.addView(mScannerView);
|
||||
mScannerView.setOnBarcodeListener(result -> {
|
||||
// Handle the scan result
|
||||
this.setResultAndClose(result.getText());
|
||||
this.setResultAndClose(result, null);
|
||||
// Return false to stop scanning after first result
|
||||
return false;
|
||||
});
|
||||
@@ -104,9 +106,22 @@ public class SimpleScannerActivity extends Activity {
|
||||
mScannerView.openAsync(); // Start camera on resume
|
||||
}
|
||||
|
||||
private void setResultAndClose(String resultText) {
|
||||
private void setResultAndClose(Result scanResult, String textOnly) {
|
||||
Intent resultIntent = new Intent();
|
||||
resultIntent.putExtra("text", resultText);
|
||||
if (textOnly != null) {
|
||||
Log.v(TAG, "clipboard contentType TEXT");
|
||||
resultIntent.putExtra("text", textOnly);
|
||||
} else if (scanResult != null) {
|
||||
if (scanResult.getContentType() == ContentType.TEXT) {
|
||||
Log.v(TAG, "scanResult contentType TEXT");
|
||||
resultIntent.putExtra("text", scanResult.getText());
|
||||
} else if (scanResult.getContentType() == ContentType.BINARY) {
|
||||
Log.v(TAG, "scanResult contentType BINARY");
|
||||
resultIntent.putExtra("binary", scanResult.getRawBytes());
|
||||
} else {
|
||||
Log.v(TAG, "scanresult contenttype unknown");
|
||||
}
|
||||
}
|
||||
setResult(Activity.RESULT_OK, resultIntent);
|
||||
this.finish();
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ from .qefx import QEFX
|
||||
from .qetxfinalizer import QETxFinalizer, QETxRbfFeeBumper, QETxCpfpFeeBumper, QETxCanceller, QETxSweepFinalizer, FeeSlider
|
||||
from .qeinvoice import QEInvoice, QEInvoiceParser
|
||||
from .qerequestdetails import QERequestDetails
|
||||
from .qetypes import QEAmount
|
||||
from .qetypes import QEAmount, QEBytes
|
||||
from .qeaddressdetails import QEAddressDetails
|
||||
from .qetxdetails import QETxDetails
|
||||
from .qechannelopener import QEChannelOpener
|
||||
@@ -426,6 +426,7 @@ class ElectrumQmlApplication(QGuiApplication):
|
||||
|
||||
# TODO QT6 order of declaration is important now?
|
||||
qmlRegisterType(QEAmount, 'org.electrum', 1, 0, 'Amount')
|
||||
qmlRegisterType(QEBytes, 'org.electrum', 1, 0, 'Bytes')
|
||||
qmlRegisterType(QENewWalletWizard, 'org.electrum', 1, 0, 'QNewWalletWizard')
|
||||
qmlRegisterType(QETermsOfUseWizard, 'org.electrum', 1, 0, 'QTermsOfUseWizard')
|
||||
qmlRegisterType(QEServerConnectWizard, 'org.electrum', 1, 0, 'QServerConnectWizard')
|
||||
|
||||
@@ -3,8 +3,8 @@ import os
|
||||
from PyQt6.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject, Qt
|
||||
from PyQt6.QtGui import QGuiApplication
|
||||
|
||||
from electrum.util import send_exception_to_crash_reporter, UserFacingException
|
||||
from electrum.simple_config import SimpleConfig
|
||||
from electrum.gui.qml.qetypes import QEBytes
|
||||
from electrum.util import send_exception_to_crash_reporter
|
||||
from electrum.logging import get_logger
|
||||
from electrum.i18n import _
|
||||
|
||||
@@ -21,14 +21,14 @@ if 'ANDROID_DATA' in os.environ:
|
||||
class QEQRScanner(QObject):
|
||||
_logger = get_logger(__name__)
|
||||
|
||||
found = pyqtSignal()
|
||||
foundText = pyqtSignal(str)
|
||||
foundBinary = pyqtSignal(QEBytes)
|
||||
|
||||
finished = pyqtSignal()
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
self._hint = _("Scan a QR code.")
|
||||
self._scan_data = "" # decoded qr code result
|
||||
self.finished.connect(self._unbind, Qt.ConnectionType.QueuedConnection)
|
||||
|
||||
self.destroyed.connect(lambda: self.on_destroy())
|
||||
@@ -44,14 +44,6 @@ class QEQRScanner(QObject):
|
||||
def hint(self, v: str):
|
||||
self._hint = v
|
||||
|
||||
@pyqtProperty(str)
|
||||
def scanData(self):
|
||||
return self._scan_data
|
||||
|
||||
@scanData.setter
|
||||
def scanData(self, v: str):
|
||||
self._scan_data = v
|
||||
|
||||
@pyqtSlot()
|
||||
def open(self):
|
||||
if 'ANDROID_DATA' not in os.environ:
|
||||
@@ -67,9 +59,11 @@ class QEQRScanner(QObject):
|
||||
def on_qr_activity_result(self, requestCode, resultCode, intent):
|
||||
try:
|
||||
if resultCode == -1: # RESULT_OK:
|
||||
contents = intent.getStringExtra(jString("text"))
|
||||
self.scanData = contents
|
||||
self.found.emit()
|
||||
if (contents := intent.getStringExtra(jString("text"))) is not None:
|
||||
self.foundText.emit(contents)
|
||||
if (contents := intent.getByteArrayExtra(jString("binary"))) is not None:
|
||||
self._binary_content = QEBytes(bytes(contents.tolist()))
|
||||
self.foundBinary.emit(self._binary_content)
|
||||
except Exception as e: # exc would otherwise get lost
|
||||
send_exception_to_crash_reporter(e)
|
||||
finally:
|
||||
@@ -82,23 +76,6 @@ class QEQRScanner(QObject):
|
||||
|
||||
def _scan_qr_non_android(self):
|
||||
data = QGuiApplication.clipboard().text()
|
||||
self.scanData = data
|
||||
self.found.emit()
|
||||
self.foundText.emit(data)
|
||||
self.finished.emit()
|
||||
return
|
||||
# from electrum import qrscanner
|
||||
# from .qeapp import ElectrumQmlApplication
|
||||
# daemon = ElectrumQmlApplication._daemon
|
||||
# config = daemon.config # type: SimpleConfig
|
||||
# try:
|
||||
# video_dev = config.get_video_device()
|
||||
# data = qrscanner.scan_barcode(video_dev)
|
||||
# if data is not None:
|
||||
# self.scanData = data
|
||||
# self.found.emit()
|
||||
# except UserFacingException as e:
|
||||
# self._logger.warning(f'camera error: {e!r}')
|
||||
# #self.show_error(e)
|
||||
# except Exception as e:
|
||||
# self._logger.exception('camera error')
|
||||
# #self.show_error(repr(e))
|
||||
|
||||
@@ -116,3 +116,27 @@ class QEAmount(QObject):
|
||||
|
||||
def __repr__(self):
|
||||
return f"<QEAmount max={self._is_max} sats={self._amount_sat} msats={self._amount_msat} empty={self.isEmpty}>"
|
||||
|
||||
|
||||
class QEBytes(QObject):
|
||||
def __init__(self, data: bytes = None, *, parent=None):
|
||||
super().__init__(parent)
|
||||
self.data = data
|
||||
|
||||
@property
|
||||
def data(self):
|
||||
return self._data
|
||||
|
||||
@data.setter
|
||||
def data(self, _data):
|
||||
self._data = _data
|
||||
|
||||
@pyqtProperty(bool)
|
||||
def isEmpty(self):
|
||||
return self._data is None or self._data == bytes()
|
||||
|
||||
def __str__(self):
|
||||
return f'{self._data}'
|
||||
|
||||
def __repr__(self):
|
||||
return f"<QEBytes data={'None' if self._data is None else self._data.hex()}>"
|
||||
|
||||
Reference in New Issue
Block a user