From 450b9a03ce3403f78cfc2e574b59da8fed7f7ca1 Mon Sep 17 00:00:00 2001 From: Sander van Grieken Date: Thu, 30 May 2024 14:53:33 +0200 Subject: [PATCH] qml: don't unbind/unregister the ActivityResultListener from within the ActivityResultListener handler func. instead, schedule a queued finished signal to unregister the listener after the handler has finished. See PythonActivity.java in P4A for why this probably causes the most often occurring crash we see on the Play Store: ``` Exception java.lang.RuntimeException: at android.app.ActivityThread.deliverResults (ActivityThread.java:5164) at android.app.ActivityThread.handleSendResult (ActivityThread.java:5205) at android.app.servertransaction.ActivityResultItem.execute (ActivityResultItem.java:51) at android.app.servertransaction.TransactionExecutor.executeCallbacks (TransactionExecutor.java:135) at android.app.servertransaction.TransactionExecutor.execute (TransactionExecutor.java:95) at android.app.ActivityThread$H.handleMessage (ActivityThread.java:2136) at android.os.Handler.dispatchMessage (Handler.java:106) at android.os.Looper.loop (Looper.java:236) at android.app.ActivityThread.main (ActivityThread.java:8061) at java.lang.reflect.Method.invoke at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:656) at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:967) Caused by java.util.ConcurrentModificationException: at java.util.ArrayList$Itr.next (ArrayList.java:860) at org.kivy.android.PythonActivity.onActivityResult (PythonActivity.java:218) at android.app.Activity.dispatchActivityResult (Activity.java:8501) at android.app.ActivityThread.deliverResults (ActivityThread.java:5157) ``` --- electrum/gui/qml/components/main.qml | 2 +- electrum/gui/qml/qeqrscanner.py | 48 ++++++++++++++++------------ 2 files changed, 29 insertions(+), 21 deletions(-) diff --git a/electrum/gui/qml/components/main.qml b/electrum/gui/qml/components/main.qml index 298c4010f..c646a84ed 100644 --- a/electrum/gui/qml/components/main.qml +++ b/electrum/gui/qml/components/main.qml @@ -413,7 +413,7 @@ ApplicationWindow Component { id: _scanDialog QRScanner { - //onClosed: destroy() + onFinished: destroy() } } Component { diff --git a/electrum/gui/qml/qeqrscanner.py b/electrum/gui/qml/qeqrscanner.py index 6e967cf31..a301cdaa9 100644 --- a/electrum/gui/qml/qeqrscanner.py +++ b/electrum/gui/qml/qeqrscanner.py @@ -1,6 +1,6 @@ import os -from PyQt6.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject +from PyQt6.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject, Qt from PyQt6.QtGui import QGuiApplication from electrum.util import send_exception_to_crash_reporter, UserFacingException @@ -10,7 +10,7 @@ from electrum.i18n import _ if 'ANDROID_DATA' in os.environ: - from jnius import autoclass, cast + from jnius import autoclass from android import activity jpythonActivity = autoclass('org.kivy.android.PythonActivity').mActivity @@ -23,10 +23,18 @@ class QEQRScanner(QObject): found = pyqtSignal() + 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()) + + def on_destroy(self): + self._unbind() @pyqtProperty(str) def hint(self): @@ -49,34 +57,34 @@ class QEQRScanner(QObject): if 'ANDROID_DATA' not in os.environ: self._scan_qr_non_android() return - SimpleScannerActivity = autoclass("org.electrum.qr.SimpleScannerActivity") - intent = jIntent(jpythonActivity, SimpleScannerActivity) + jSimpleScannerActivity = autoclass("org.electrum.qr.SimpleScannerActivity") + intent = jIntent(jpythonActivity, jSimpleScannerActivity) intent.putExtra(jIntent.EXTRA_TEXT, jString(self._hint)) - def on_qr_result(requestCode, resultCode, intent): - try: - if resultCode == -1: # RESULT_OK: - # this doesn't work due to some bug in jnius: - # contents = intent.getStringExtra("text") - contents = intent.getStringExtra(jString("text")) - #self._logger.info(f"on_qr_result. {contents=!r}") - self.scanData = contents - self.found.emit() - except Exception as e: # exc would otherwise get lost - send_exception_to_crash_reporter(e) - finally: - activity.unbind(on_activity_result=on_qr_result) - activity.bind(on_activity_result=on_qr_result) + activity.bind(on_activity_result=self.on_qr_activity_result) jpythonActivity.startActivityForResult(intent, 0) + 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() + except Exception as e: # exc would otherwise get lost + send_exception_to_crash_reporter(e) + finally: + self.finished.emit() + @pyqtSlot() - def close(self): - pass + def _unbind(self): + if 'ANDROID_DATA' in os.environ: + activity.unbind(on_activity_result=self.on_qr_activity_result) def _scan_qr_non_android(self): data = QGuiApplication.clipboard().text() self.scanData = data self.found.emit() + self.finished.emit() return # from electrum import qrscanner # from .qeapp import ElectrumQmlApplication