Skip to content

Commit 8b6dbc7

Browse files
committed
qml: integrate java based QR scanner for android, keep qt scanner for desktop
1 parent 2e15004 commit 8b6dbc7

11 files changed

Lines changed: 271 additions & 26 deletions

File tree

.gitignore

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,7 @@ electrum/locale/
1212
packages
1313
env/
1414
.buildozer
15-
.buildozer_kivy/
16-
.buildozer_qml/
15+
.buildozer_*/
1716
bin/
1817
/app.fil
1918
.idea

contrib/android/Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,7 @@ RUN cd /opt \
198198
&& git fetch --all \
199199
# commit: from branch accumulator/qt6-wip (note: careful with force-pushing! see #8162) \
200200
#
201-
&& git checkout "3b3733dbf5f461e197ba83887ac0d3b6d0f1c396^{commit}" \
201+
&& git checkout "eb4a3522373e0b4e2749b8a8bc965ff51355ea35^{commit}" \
202202
&& /opt/venv/bin/python3 -m pip install --no-build-isolation --no-dependencies -e .
203203

204204
# build env vars

contrib/android/buildozer_qml.spec

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -138,9 +138,25 @@ android.add_jars = .buildozer/android/platform/*/build/libs_collections/Electrum
138138

139139
# (list) List of Java files to add to the android project (can be java or a
140140
# directory containing the files)
141-
# android.add_src = ...
142-
# android.add_activities = ...
143-
android.gradle_dependencies = com.android.support:support-compat:28.0.0
141+
android.add_src = electrum/gui/qml/java_classes/
142+
143+
android.gradle_dependencies =
144+
com.android.support:support-compat:28.0.0,
145+
me.dm7.barcodescanner:zxing:1.9.8
146+
147+
android.add_activities = org.electrum.qr.SimpleScannerActivity
148+
149+
# (list) Put these files or directories in the apk res directory.
150+
# The option may be used in three ways, the value may contain one or zero ':'
151+
# Some examples:
152+
# 1) A file to add to resources, legal resource names contain ['a-z','0-9','_']
153+
# android.add_resources = my_icons/all-inclusive.png:drawable/all_inclusive.png
154+
# 2) A directory, here 'legal_icons' must contain resources of one kind
155+
# android.add_resources = legal_icons:drawable
156+
# 3) A directory, here 'legal_resources' must contain one or more directories,
157+
# each of a resource kind: drawable, xml, etc...
158+
# android.add_resources = legal_resources
159+
android.add_resources = electrum/gui/qml/android_res/layout:layout
144160

145161
# (str) python-for-android branch to use, if not master, useful to try
146162
# not yet merged features.
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
3+
<FrameLayout
4+
xmlns:android="http://schemas.android.com/apk/res/android"
5+
xmlns:app="http://schemas.android.com/apk/res-auto"
6+
android:layout_width="match_parent"
7+
android:layout_height="match_parent">
8+
9+
<FrameLayout
10+
android:id="@+id/content_frame"
11+
android:layout_width="match_parent"
12+
android:layout_height="match_parent" />
13+
14+
<TextView
15+
android:id="@+id/hint"
16+
android:layout_gravity="center|top"
17+
android:text="Scan a QR code."
18+
android:layout_width="wrap_content"
19+
android:textColor="#ffffff"
20+
android:layout_height="wrap_content" />
21+
22+
<Button
23+
android:id="@+id/paste_btn"
24+
android:layout_gravity="center|bottom"
25+
android:layout_width="wrap_content"
26+
android:layout_height="wrap_content"
27+
android:text="Paste from clipboard" />
28+
29+
</FrameLayout>

electrum/gui/qml/components/ScanDialog.qml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import QtQuick.Layouts
44

55
import "controls"
66

7+
// currently not used on android, kept for future use when qt6 camera stops crashing
78
ElDialog {
89
id: scanDialog
910

electrum/gui/qml/components/SendDialog.qml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import org.electrum 1.0
77

88
import "controls"
99

10+
// currently not used on android, kept for future use when qt6 camera stops crashing
1011
ElDialog {
1112
id: dialog
1213

electrum/gui/qml/components/WalletMainView.qml

Lines changed: 53 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -34,20 +34,56 @@ Item {
3434
}
3535

3636
function openSendDialog() {
37-
_sendDialog = sendDialog.createObject(mainView, {invoiceParser: invoiceParser})
38-
_sendDialog.open()
37+
// Qt based send dialog if not on android
38+
if (!AppController.isAndroid()) {
39+
_sendDialog = qtSendDialog.createObject(mainView, {invoiceParser: invoiceParser})
40+
_sendDialog.open()
41+
return
42+
}
43+
44+
// Android based send dialog if on android
45+
var scanner = app.scanDialog.createObject(mainView, {
46+
hint: qsTr('Scan an Invoice, an Address, an LNURL-pay, a PSBT or a Channel backup'),
47+
})
48+
scanner.onFound.connect(function() {
49+
var data = scanner.scanData
50+
data = data.trim()
51+
if (bitcoin.isRawTx(data)) {
52+
app.stack.push(Qt.resolvedUrl('TxDetails.qml'), { rawtx: data })
53+
} else if (Daemon.currentWallet.isValidChannelBackup(data)) {
54+
var dialog = app.messageDialog.createObject(app, {
55+
title: qsTr('Import Channel backup?'),
56+
yesno: true
57+
})
58+
dialog.accepted.connect(function() {
59+
Daemon.currentWallet.importChannelBackup(data)
60+
})
61+
dialog.open()
62+
} else {
63+
invoiceParser.recipient = data
64+
}
65+
//scanner.destroy() // TODO
66+
})
67+
scanner.open()
3968
}
4069

4170
function closeSendDialog() {
42-
if (_sendDialog) {
43-
_sendDialog.doClose()
44-
_sendDialog = null
71+
if (!AppController.isAndroid()) {
72+
if (_sendDialog) {
73+
_sendDialog.doClose()
74+
_sendDialog = null
75+
}
4576
}
4677
}
4778

4879
function restartSendDialog() {
49-
if (_sendDialog) {
50-
_sendDialog.restart()
80+
if (!AppController.isAndroid()) {
81+
if (_sendDialog) {
82+
_sendDialog.restart()
83+
}
84+
return
85+
} else {
86+
openSendDialog()
5187
}
5288
}
5389

@@ -86,6 +122,11 @@ Item {
86122
dialog.open()
87123
}
88124

125+
function createRequest(lightning_only, reuse_address) {
126+
var qamt = Config.unitsToSats(_request_amount)
127+
Daemon.currentWallet.createRequest(qamt, _request_description, _request_expiry, lightning_only, reuse_address)
128+
}
129+
89130
property QtObject menu: Menu {
90131
id: menu
91132

@@ -314,6 +355,10 @@ Item {
314355
}
315356
}
316357

358+
Bitcoin {
359+
id: bitcoin
360+
}
361+
317362
Connections {
318363
target: AppController
319364
function onUriReceived(uri) {
@@ -420,7 +465,7 @@ Item {
420465
}
421466

422467
Component {
423-
id: sendDialog
468+
id: qtSendDialog
424469
SendDialog {
425470
width: parent.width
426471
height: parent.height
@@ -447,11 +492,6 @@ Item {
447492
}
448493
}
449494

450-
function createRequest(lightning_only, reuse_address) {
451-
var qamt = Config.unitsToSats(_request_amount)
452-
Daemon.currentWallet.createRequest(qamt, _request_description, _request_expiry, lightning_only, reuse_address)
453-
}
454-
455495
Component {
456496
id: receiveDetailsDialog
457497

electrum/gui/qml/components/main.qml

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -386,9 +386,15 @@ ApplicationWindow
386386
}
387387
}
388388

389-
property alias scanDialog: _scanDialog
389+
property Component scanDialog // set in Component.onCompleted
390390
Component {
391391
id: _scanDialog
392+
QRScanner {
393+
//onClosed: destroy()
394+
}
395+
}
396+
Component {
397+
id: _qtScanDialog
392398
ScanDialog {
393399
onClosed: destroy()
394400
}
@@ -443,6 +449,12 @@ ApplicationWindow
443449
Component.onCompleted: {
444450
coverTimer.start()
445451

452+
if (AppController.isAndroid()) {
453+
app.scanDialog = _scanDialog
454+
} else {
455+
app.scanDialog = _qtScanDialog
456+
}
457+
446458
if (!Config.autoConnectDefined) {
447459
var dialog = serverConnectWizard.createObject(app)
448460
// without completed serverConnectWizard we can't start

electrum/gui/qml/java_classes/org/electrum/qr/SimpleScannerActivity.java

Lines changed: 55 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,16 @@
55
import android.util.Log;
66
import android.content.Intent;
77
import android.Manifest;
8+
import android.content.ClipData;
9+
import android.content.ClipDescription;
10+
import android.content.ClipboardManager;
11+
import android.content.Context;
812
import android.content.pm.PackageManager;
13+
import android.view.View;
14+
import android.view.ViewGroup;
15+
import android.widget.Button;
16+
import android.widget.TextView;
17+
import android.widget.Toast;
918

1019
import androidx.core.app.ActivityCompat;
1120

@@ -16,18 +25,53 @@
1625
import com.google.zxing.Result;
1726
import com.google.zxing.BarcodeFormat;
1827

28+
import org.electrum.electrum.res.R; // package set in build.gradle
29+
1930
public class SimpleScannerActivity extends Activity implements ZXingScannerView.ResultHandler {
2031
private static final int MY_PERMISSIONS_CAMERA = 1002;
2132

2233
private ZXingScannerView mScannerView = null;
2334
final String TAG = "org.electrum.SimpleScannerActivity";
2435

36+
private boolean mAlreadyRequestedPermissions = false;
37+
38+
@Override
39+
public void onCreate(Bundle savedInstanceState) {
40+
super.onCreate(savedInstanceState);
41+
setContentView(R.layout.scanner_layout);
42+
43+
// change top text
44+
Intent intent = getIntent();
45+
String text = intent.getStringExtra(intent.EXTRA_TEXT);
46+
TextView hintTextView = (TextView) findViewById(R.id.hint);
47+
hintTextView.setText(text);
48+
49+
// bind "paste" button
50+
Button btn = (Button) findViewById(R.id.paste_btn);
51+
btn.setOnClickListener(new View.OnClickListener() {
52+
@Override
53+
public void onClick(View v) {
54+
ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
55+
if (clipboard.hasPrimaryClip()
56+
&& (clipboard.getPrimaryClipDescription().hasMimeType(ClipDescription.MIMETYPE_TEXT_PLAIN)
57+
|| clipboard.getPrimaryClipDescription().hasMimeType(ClipDescription.MIMETYPE_TEXT_HTML))) {
58+
ClipData.Item item = clipboard.getPrimaryClip().getItemAt(0);
59+
String clipboardText = item.getText().toString();
60+
SimpleScannerActivity.this.setResultAndClose(clipboardText);
61+
} else {
62+
Toast.makeText(SimpleScannerActivity.this, "Clipboard is empty.", Toast.LENGTH_SHORT).show();
63+
}
64+
}
65+
});
66+
}
67+
2568
@Override
2669
public void onResume() {
2770
super.onResume();
2871
if (this.hasPermission()) {
2972
this.startCamera();
30-
} else {
73+
} else if (!mAlreadyRequestedPermissions) {
74+
mAlreadyRequestedPermissions = true;
3175
this.requestPermission();
3276
}
3377
}
@@ -41,18 +85,23 @@ public void onPause() {
4185
}
4286

4387
private void startCamera() {
44-
mScannerView = new ZXingScannerView(this); // Programmatically initialize the scanner view
88+
mScannerView = new ZXingScannerView(this);
4589
mScannerView.setFormats(Arrays.asList(BarcodeFormat.QR_CODE));
46-
setContentView(mScannerView); // Set the scanner view as the content view
90+
ViewGroup contentFrame = (ViewGroup) findViewById(R.id.content_frame);
91+
contentFrame.addView(mScannerView);
4792
mScannerView.setResultHandler(this); // Register ourselves as a handler for scan results.
4893
mScannerView.startCamera(); // Start camera on resume
4994
}
5095

5196
@Override
5297
public void handleResult(Result rawResult) {
98+
//resultIntent.putExtra("format", rawResult.getBarcodeFormat().toString());
99+
this.setResultAndClose(rawResult.getText());
100+
}
101+
102+
private void setResultAndClose(String resultText) {
53103
Intent resultIntent = new Intent();
54-
resultIntent.putExtra("text", rawResult.getText());
55-
resultIntent.putExtra("format", rawResult.getBarcodeFormat().toString());
104+
resultIntent.putExtra("text", resultText);
56105
setResult(Activity.RESULT_OK, resultIntent);
57106
this.finish();
58107
}
@@ -80,7 +129,7 @@ public void onRequestPermissionsResult(int requestCode,
80129
this.startCamera();
81130
} else {
82131
// permission denied
83-
this.finish();
132+
//this.finish();
84133
}
85134
return;
86135
}

electrum/gui/qml/qeapp.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
from .qenetwork import QENetwork
2626
from .qewallet import QEWallet
2727
from .qeqr import QEQRParser, QEQRImageProvider, QEQRImageProviderHelper
28+
from .qeqrscanner import QEQRScanner
2829
from .qewalletdb import QEWalletDB
2930
from .qebitcoin import QEBitcoin
3031
from .qefx import QEFX
@@ -355,6 +356,7 @@ def __init__(self, args, *, config: 'SimpleConfig', daemon: 'Daemon', plugins: '
355356
qmlRegisterType(QEWalletDB, 'org.electrum', 1, 0, 'WalletDB')
356357
qmlRegisterType(QEBitcoin, 'org.electrum', 1, 0, 'Bitcoin')
357358
qmlRegisterType(QEQRParser, 'org.electrum', 1, 0, 'QRParser')
359+
qmlRegisterType(QEQRScanner, 'org.electrum', 1, 0, 'QRScanner')
358360
qmlRegisterType(QEFX, 'org.electrum', 1, 0, 'FX')
359361
qmlRegisterType(QETxFinalizer, 'org.electrum', 1, 0, 'TxFinalizer')
360362
qmlRegisterType(QEInvoice, 'org.electrum', 1, 0, 'Invoice')

0 commit comments

Comments
 (0)