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