1
0
Files
electrum/electrum/gui/qml/qeqrscanner.py
Sander van Grieken 450b9a03ce 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)
```
2024-05-30 14:53:33 +02:00

105 lines
3.2 KiB
Python

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.logging import get_logger
from electrum.i18n import _
if 'ANDROID_DATA' in os.environ:
from jnius import autoclass
from android import activity
jpythonActivity = autoclass('org.kivy.android.PythonActivity').mActivity
jString = autoclass('java.lang.String')
jIntent = autoclass('android.content.Intent')
class QEQRScanner(QObject):
_logger = get_logger(__name__)
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):
return self._hint
@hint.setter
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:
self._scan_qr_non_android()
return
jSimpleScannerActivity = autoclass("org.electrum.qr.SimpleScannerActivity")
intent = jIntent(jpythonActivity, jSimpleScannerActivity)
intent.putExtra(jIntent.EXTRA_TEXT, jString(self._hint))
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 _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
# 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))