electrum

Electrum Bitcoin wallet
git clone https://git.parazyd.org/electrum
Log | Files | Refs | Submodules

crash_reporter.py (6768B)


      1 import sys
      2 import json
      3 
      4 from aiohttp.client_exceptions import ClientError
      5 from kivy import base, utils
      6 from kivy.clock import Clock
      7 from kivy.core.window import Window
      8 from kivy.factory import Factory
      9 from kivy.lang import Builder
     10 from kivy.uix.label import Label
     11 from kivy.utils import platform
     12 
     13 from electrum.gui.kivy.i18n import _
     14 
     15 from electrum.base_crash_reporter import BaseCrashReporter
     16 from electrum.logging import Logger
     17 
     18 
     19 Builder.load_string('''
     20 <CrashReporter@Popup>
     21     BoxLayout:
     22         orientation: 'vertical'
     23         Label:
     24             id: crash_message
     25             text_size: root.width, None
     26             size: self.texture_size
     27             size_hint: None, None
     28         Label:
     29             id: request_help_message
     30             text_size: root.width*.95, None
     31             size: self.texture_size
     32             size_hint: None, None
     33         BoxLayout:
     34             size_hint: 1, 0.1
     35         Button:
     36             text: 'Show report contents'
     37             height: '48dp'
     38             size_hint: 1, None
     39             on_release: root.show_contents()
     40         BoxLayout:
     41             size_hint: 1, 0.1
     42         Label:
     43             id: describe_error_message
     44             text_size: root.width, None
     45             size: self.texture_size
     46             size_hint: None, None
     47         TextInput:
     48             id: user_message
     49             size_hint: 1, 0.3
     50         BoxLayout:
     51             size_hint: 1, 0.7
     52         BoxLayout:
     53             size_hint: 1, None
     54             height: '48dp'
     55             orientation: 'horizontal'
     56             Button:
     57                 height: '48dp'
     58                 text: 'Send'
     59                 on_release: root.send_report()
     60             Button:
     61                 text: 'Never'
     62                 on_release: root.show_never()
     63             Button:
     64                 text: 'Not now'
     65                 on_release: root.dismiss()
     66 
     67 <CrashReportDetails@Popup>
     68     BoxLayout:
     69         orientation: 'vertical'
     70         ScrollView:
     71             do_scroll_x: False
     72             Label:
     73                 id: contents
     74                 text_size: root.width*.9, None
     75                 size: self.texture_size
     76                 size_hint: None, None
     77         Button:
     78             text: 'Close'
     79             height: '48dp'
     80             size_hint: 1, None
     81             on_release: root.dismiss()
     82 ''')
     83 
     84 
     85 class CrashReporter(BaseCrashReporter, Factory.Popup):
     86     issue_template = """[b]Traceback[/b]
     87 
     88 [i]{traceback}[/i]
     89 
     90 
     91 [b]Additional information[/b]
     92  * Electrum version: {app_version}
     93  * Operating system: {os}
     94  * Wallet type: {wallet_type}
     95  * Locale: {locale}
     96         """
     97 
     98     def __init__(self, main_window, exctype, value, tb):
     99         BaseCrashReporter.__init__(self, exctype, value, tb)
    100         Factory.Popup.__init__(self)
    101         self.main_window = main_window
    102         self.title = BaseCrashReporter.CRASH_TITLE
    103         self.title_size = "24sp"
    104         self.ids.crash_message.text = BaseCrashReporter.CRASH_MESSAGE
    105         self.ids.request_help_message.text = BaseCrashReporter.REQUEST_HELP_MESSAGE
    106         self.ids.describe_error_message.text = BaseCrashReporter.DESCRIBE_ERROR_MESSAGE
    107 
    108     def show_contents(self):
    109         details = CrashReportDetails(self.get_report_string())
    110         details.open()
    111 
    112     def show_popup(self, title, content):
    113         popup = Factory.Popup(title=title,
    114                               content=Label(text=content, text_size=(Window.size[0] * 3/4, None)),
    115                               size_hint=(3/4, 3/4))
    116         popup.open()
    117 
    118     def send_report(self):
    119         try:
    120             loop = self.main_window.network.asyncio_loop
    121             proxy = self.main_window.network.proxy
    122             # FIXME network request in GUI thread...
    123             response = json.loads(BaseCrashReporter.send_report(self, loop, proxy,
    124                                                                 "/crash.json", timeout=10))
    125         except (ValueError, ClientError) as e:
    126             self.logger.warning(f"Error sending crash report. exc={e!r}")
    127             self.show_popup(_('Unable to send report'), _("Please check your network connection."))
    128         else:
    129             self.show_popup(_('Report sent'), response["text"])
    130             location = response["location"]
    131             if location:
    132                 self.logger.info(f"Crash report sent. location={location!r}")
    133                 self.open_url(location)
    134         self.dismiss()
    135 
    136     def on_dismiss(self):
    137         self.main_window.on_wizard_aborted()
    138 
    139     def open_url(self, url):
    140         if platform != 'android':
    141             return
    142         from jnius import autoclass, cast
    143         String = autoclass("java.lang.String")
    144         url = String(url)
    145         PythonActivity = autoclass('org.kivy.android.PythonActivity')
    146         activity = PythonActivity.mActivity
    147         Intent = autoclass('android.content.Intent')
    148         Uri = autoclass('android.net.Uri')
    149         browserIntent = Intent()
    150         # This line crashes the app:
    151         # browserIntent.setAction(Intent.ACTION_VIEW)
    152         # Luckily we don't need it because the OS is smart enough to recognize the URL
    153         browserIntent.setData(Uri.parse(url))
    154         currentActivity = cast('android.app.Activity', activity)
    155         currentActivity.startActivity(browserIntent)
    156 
    157     def show_never(self):
    158         self.main_window.electrum_config.set_key(BaseCrashReporter.config_key, False)
    159         self.dismiss()
    160 
    161     def get_user_description(self):
    162         return self.ids.user_message.text
    163 
    164     def get_wallet_type(self):
    165         return self.main_window.wallet.wallet_type
    166 
    167 
    168 class CrashReportDetails(Factory.Popup):
    169     def __init__(self, text):
    170         Factory.Popup.__init__(self)
    171         self.title = "Report Details"
    172         self.ids.contents.text = text
    173         print(text)
    174 
    175 
    176 class ExceptionHook(base.ExceptionHandler, Logger):
    177     def __init__(self, main_window):
    178         base.ExceptionHandler.__init__(self)
    179         Logger.__init__(self)
    180         self.main_window = main_window
    181         if not main_window.electrum_config.get(BaseCrashReporter.config_key, default=True):
    182             return
    183         # For exceptions in Kivy:
    184         base.ExceptionManager.add_handler(self)
    185         # For everything else:
    186         sys.excepthook = lambda exctype, value, tb: self.handle_exception(value)
    187 
    188     def handle_exception(self, _inst):
    189         exc_info = sys.exc_info()
    190         self.logger.error('exception caught by crash reporter', exc_info=exc_info)
    191         # Check if this is an exception from within the exception handler:
    192         import traceback
    193         for item in traceback.extract_tb(exc_info[2]):
    194             if item.filename.endswith("crash_reporter.py"):
    195                 return
    196         e = CrashReporter(self.main_window, *exc_info)
    197         # Open in main thread:
    198         Clock.schedule_once(lambda _: e.open(), 0)
    199         return base.ExceptionManager.PASS