From 222162931580f812b56c21e20ba35c8e4cf4dcac Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Sun, 20 Oct 2024 14:15:34 +0200 Subject: [PATCH 01/30] Updated requirements --- sbapp/buildozer.spec | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sbapp/buildozer.spec b/sbapp/buildozer.spec index 4c92699..42cd319 100644 --- a/sbapp/buildozer.spec +++ b/sbapp/buildozer.spec @@ -10,7 +10,7 @@ source.exclude_patterns = app_storage/*,venv/*,Makefile,./Makefil*,requirements, version.regex = __version__ = ['"](.*)['"] version.filename = %(source.dir)s/main.py -android.numeric_version = 20241013 +android.numeric_version = 20241020 requirements = kivy==2.3.0,libbz2,pillow==10.2.0,qrcode==7.3.1,usb4a,usbserial4a,able_recipe,libwebp,libogg,libopus,opusfile,numpy,cryptography,ffpyplayer,codec2,pycodec2,sh,pynacl,typing-extensions diff --git a/setup.py b/setup.py index bab6ed7..2bc2e76 100644 --- a/setup.py +++ b/setup.py @@ -99,7 +99,7 @@ setuptools.setup( ] }, install_requires=[ - "rns>=0.8.4", + "rns>=0.8.5", "lxmf>=0.5.7", "kivy>=2.3.0", "pillow>=10.2.0", From 91883a051085fa4e43453e6ff58c67196e10cacc Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Fri, 15 Nov 2024 17:06:33 +0100 Subject: [PATCH 02/30] Fixed invalid speed being reported to telemetry system --- docs/example_plugins/gpsd_location.py | 2 +- sbapp/sideband/sense.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/example_plugins/gpsd_location.py b/docs/example_plugins/gpsd_location.py index 2c3e713..06d47cd 100644 --- a/docs/example_plugins/gpsd_location.py +++ b/docs/example_plugins/gpsd_location.py @@ -67,7 +67,7 @@ class GpsdLocationPlugin(SidebandTelemetryPlugin): self.latitude = gpsd_latitude self.longitude = gpsd_longitude self.altitude = gpsd_altitude - self.speed = gpsd_speed + self.speed = gpsd_speed*3.6 # Convert from m/s to km/h self.bearing = gpsd_bearing epx = result.get("epx", None); epy = result.get("epy", None) diff --git a/sbapp/sideband/sense.py b/sbapp/sideband/sense.py index aa1c3fa..843149e 100644 --- a/sbapp/sideband/sense.py +++ b/sbapp/sideband/sense.py @@ -744,7 +744,8 @@ class Location(Sensor): if "altitude" in self._raw: self.altitude = self._raw["altitude"] if "speed" in self._raw: - self.speed = self._raw["speed"] + # Android GPS reports speed in m/s, convert to km/h + self.speed = self._raw["speed"]*3.6 if self.speed < 0: self.speed = 0 if "bearing" in self._raw: From 293023be128714d04bb7b4d38fbedd32bfa64e3e Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Sun, 17 Nov 2024 11:35:40 +0100 Subject: [PATCH 03/30] Updated sync dialog --- sbapp/main.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/sbapp/main.py b/sbapp/main.py index 3184b6a..7739ec4 100644 --- a/sbapp/main.py +++ b/sbapp/main.py @@ -2404,9 +2404,10 @@ class SidebandApp(MDApp): close_button = MDRectangleFlatButton(text="Close",font_size=dp(18)) stop_button = MDRectangleFlatButton(text="Stop",font_size=dp(18), theme_text_color="Custom", line_color=self.color_reject, text_color=self.color_reject) + sync_title = "LXMF Sync" dialog_content = MsgSync() dialog = MDDialog( - title="LXMF Sync via "+RNS.prettyhexrep(self.sideband.message_router.get_outbound_propagation_node()), + title=sync_title, type="custom", content_cls=dialog_content, buttons=[ stop_button, close_button ], @@ -2443,7 +2444,8 @@ class SidebandApp(MDApp): dsp = 0 self.sideband.setstate("app.flags.lxmf_sync_dialog_open", True) - self.message_sync_dialog.title = f"LXMF Sync via "+RNS.prettyhexrep(self.sideband.message_router.get_outbound_propagation_node()) + self.message_sync_dialog.title = sync_title + self.message_sync_dialog.d_content.ids.node_info.text = f"Via {RNS.prettyhexrep(self.sideband.message_router.get_outbound_propagation_node())}\n" self.message_sync_dialog.d_content.ids.sync_status.text = self.sideband.get_sync_status() self.message_sync_dialog.d_content.ids.sync_progress.value = dsp self.message_sync_dialog.d_content.ids.sync_progress.start() From c14699151d2facd6d915da71e61a4a52d7e711a0 Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Sun, 17 Nov 2024 11:42:08 +0100 Subject: [PATCH 04/30] Updated sync dialog --- sbapp/ui/conversations.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sbapp/ui/conversations.py b/sbapp/ui/conversations.py index 4427af3..9dcc210 100644 --- a/sbapp/ui/conversations.py +++ b/sbapp/ui/conversations.py @@ -652,6 +652,10 @@ Builder.load_string(""" padding: [0, 0, 0, dp(16)] height: self.minimum_height+dp(24) + MDLabel: + id: node_info + text: "Unknown propagation node" + MDProgressBar: id: sync_progress type: "determinate" @@ -659,7 +663,6 @@ Builder.load_string(""" MDLabel: id: sync_status - hint_text: "Name" text: "Initiating sync..." From 46450098b41be908e8571e4310d59639d9856822 Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Sun, 17 Nov 2024 11:43:03 +0100 Subject: [PATCH 05/30] Validate propagation node data before logging it --- sbapp/sideband/core.py | 55 ++++++++++++++++++++++++------------------ 1 file changed, 32 insertions(+), 23 deletions(-) diff --git a/sbapp/sideband/core.py b/sbapp/sideband/core.py index d8c7c1a..97efab3 100644 --- a/sbapp/sideband/core.py +++ b/sbapp/sideband/core.py @@ -10,6 +10,7 @@ import shlex import RNS.vendor.umsgpack as msgpack import RNS.Interfaces.Interface as Interface +from LXMF import pn_announce_data_is_valid import multiprocessing.connection @@ -46,35 +47,42 @@ class PropagationNodeDetector(): def received_announce(self, destination_hash, announced_identity, app_data): try: if app_data != None and len(app_data) > 0: - unpacked = msgpack.unpackb(app_data) - node_active = unpacked[0] - emitted = unpacked[1] - hops = RNS.Transport.hops_to(destination_hash) + if pn_announce_data_is_valid(app_data): + unpacked = msgpack.unpackb(app_data) + node_active = unpacked[0] + emitted = unpacked[1] + hops = RNS.Transport.hops_to(destination_hash) - age = time.time() - emitted - if age < 0: - RNS.log("Warning, propagation node announce emitted in the future, possible timing inconsistency or tampering attempt.") - if age < -1*PropagationNodeDetector.EMITTED_DELTA_GRACE: - raise ValueError("Announce timestamp too far in the future, discarding it") + age = time.time() - emitted + if age < 0: + RNS.log("Warning, propagation node announce emitted in the future, possible timing inconsistency or tampering attempt.") + if age < -1*PropagationNodeDetector.EMITTED_DELTA_GRACE: + raise ValueError("Announce timestamp too far in the future, discarding it") - if age > -1*PropagationNodeDetector.EMITTED_DELTA_IGNORE: - # age = 0 - pass + if age > -1*PropagationNodeDetector.EMITTED_DELTA_IGNORE: + # age = 0 + pass - RNS.log("Detected active propagation node "+RNS.prettyhexrep(destination_hash)+" emission "+str(age)+" seconds ago, "+str(hops)+" hops away") - self.owner.log_announce(destination_hash, RNS.prettyhexrep(destination_hash).encode("utf-8"), dest_type=PropagationNodeDetector.aspect_filter) + RNS.log("Detected active propagation node "+RNS.prettyhexrep(destination_hash)+" emission "+str(age)+" seconds ago, "+str(hops)+" hops away") + self.owner.log_announce(destination_hash, app_data, dest_type=PropagationNodeDetector.aspect_filter) - if self.owner.config["lxmf_propagation_node"] == None: - if self.owner.active_propagation_node == None: - self.owner.set_active_propagation_node(destination_hash) - else: - prev_hops = RNS.Transport.hops_to(self.owner.active_propagation_node) - if hops <= prev_hops: + if self.owner.config["lxmf_propagation_node"] == None: + if self.owner.active_propagation_node == None: self.owner.set_active_propagation_node(destination_hash) else: - pass + prev_hops = RNS.Transport.hops_to(self.owner.active_propagation_node) + if hops <= prev_hops: + self.owner.set_active_propagation_node(destination_hash) + else: + pass + else: + pass + else: - pass + RNS.log(f"Received malformed propagation node announce from {RNS.prettyhexrep(destination_hash)} with data: {app_data}", RNS.LOG_DEBUG) + + else: + RNS.log(f"Received malformed propagation node announce from {RNS.prettyhexrep(destination_hash)} with data: {app_data}", RNS.LOG_DEBUG) except Exception as e: RNS.log("Error while processing received propagation node announce: "+str(e)) @@ -910,7 +918,8 @@ class SidebandCore(): try: if app_data == None: app_data = b"" - app_data = msgpack.packb([app_data, stamp_cost]) + if type(app_data) != bytes: + app_data = msgpack.packb([app_data, stamp_cost]) RNS.log("Received "+str(dest_type)+" announce for "+RNS.prettyhexrep(dest)+" with data: "+str(app_data), RNS.LOG_DEBUG) self._db_save_announce(dest, app_data, dest_type) self.setstate("app.flags.new_announces", True) From 6fb9a94a43021ad3e3834acb5149716c5e5f7ece Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Sun, 17 Nov 2024 12:12:26 +0100 Subject: [PATCH 06/30] Fixed stamp cost display in announce stream --- sbapp/sideband/core.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/sbapp/sideband/core.py b/sbapp/sideband/core.py index 97efab3..82d78d7 100644 --- a/sbapp/sideband/core.py +++ b/sbapp/sideband/core.py @@ -2422,12 +2422,19 @@ class SidebandCore(): try: if not entry[2] in added_dests: app_data = entry[3] + dest_type = entry[4] + if dest_type == "lxmf.delivery": + announced_name = LXMF.display_name_from_app_data(app_data) + announced_cost = self.message_router.get_outbound_stamp_cost(entry[2]) + else: + announced_name = None + announced_cost = None announce = { "dest": entry[2], - "name": LXMF.display_name_from_app_data(app_data), - "cost": LXMF.stamp_cost_from_app_data(app_data), + "name": announced_name, + "cost": announced_cost, "time": entry[1], - "type": entry[4] + "type": dest_type } added_dests.append(entry[2]) announces.append(announce) From 9f86c4130c46c15d337c6b31f8d2091d02aa6487 Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Thu, 21 Nov 2024 16:42:27 +0100 Subject: [PATCH 07/30] Prepare interface modularity. Fixed occasional missing view redraw on wake on Android. --- sbapp/main.py | 21 ++++- sbapp/sideband/core.py | 171 +++++++++++++++++++++-------------------- 2 files changed, 108 insertions(+), 84 deletions(-) diff --git a/sbapp/main.py b/sbapp/main.py index 7739ec4..ffc4a45 100644 --- a/sbapp/main.py +++ b/sbapp/main.py @@ -303,6 +303,7 @@ class SidebandApp(MDApp): self.hw_error_dialog = None self.final_load_completed = False + self.wants_wake_update = False self.service_last_available = 0 self.closing_app = False @@ -699,6 +700,10 @@ class SidebandApp(MDApp): else: RNS.log("Conversations view did not exist", RNS.LOG_DEBUG) + def cb(dt): + self.wants_wake_update = True + Clock.schedule_once(cb, 1.2) + RNS.log("App resumed", RNS.LOG_DEBUG) def on_stop(self): @@ -972,6 +977,11 @@ class SidebandApp(MDApp): if self.conversations_view != None: self.conversations_view.update() + if self.wants_wake_update: + self.wants_wake_update = False + if self.conversations_view != None: + self.conversations_view.update() + if self.sideband.getstate("app.flags.lxmf_sync_dialog_open", allow_cache=True) and self.sync_dialog != None: state = self.sideband.message_router.propagation_transfer_state @@ -993,6 +1003,11 @@ class SidebandApp(MDApp): if self.announces_view != None: self.announces_view.update() + if self.wants_wake_update: + self.wants_wake_update = False + if self.announces_view != None: + self.announces_view.update() + elif self.root.ids.screen_manager.current == "map_screen": if self.map_screen and hasattr(self.map_screen.ids.map_layout, "map") and self.map_screen.ids.map_layout.map != None: self.sideband.config["map_lat"] = self.map_screen.ids.map_layout.map.lat @@ -1003,6 +1018,10 @@ class SidebandApp(MDApp): if self.last_telemetry_received > self.last_map_update: self.map_update_markers() + if self.wants_wake_update: + self.wants_wake_update = False + self.map_update_markers() + if self.sideband.getstate("app.flags.new_conversations", allow_cache=True): if self.conversations_view != None: self.conversations_view.update() @@ -2400,11 +2419,11 @@ class SidebandApp(MDApp): else: sl = None + sync_title = "LXMF Sync" if not hasattr(self, "message_sync_dialog") or self.message_sync_dialog == None: close_button = MDRectangleFlatButton(text="Close",font_size=dp(18)) stop_button = MDRectangleFlatButton(text="Stop",font_size=dp(18), theme_text_color="Custom", line_color=self.color_reject, text_color=self.color_reject) - sync_title = "LXMF Sync" dialog_content = MsgSync() dialog = MDDialog( title=sync_title, diff --git a/sbapp/sideband/core.py b/sbapp/sideband/core.py index 82d78d7..fdfc56f 100644 --- a/sbapp/sideband/core.py +++ b/sbapp/sideband/core.py @@ -3437,11 +3437,12 @@ class SidebandCore(): else: ifac_netkey = self.config["connect_local_ifac_passphrase"] - autointerface = RNS.Interfaces.AutoInterface.AutoInterface( - RNS.Transport, - name = "AutoInterface", - group_id = group_id - ) + interface_config = { + "name": "AutoInterface", + "group_id": group_id + } + + autointerface = RNS.Interfaces.AutoInterface.AutoInterface(RNS.Transport, interface_config) autointerface.OUT = True if RNS.Reticulum.transport_enabled(): @@ -3529,45 +3530,50 @@ class SidebandCore(): else: atl_long = self.config["hw_rnode_atl_long"] + interface_config = None if rnode_allow_ble: - rnodeinterface = RNS.Interfaces.Android.RNodeInterface.RNodeInterface( - RNS.Transport, - "RNodeInterface", - None, - frequency = self.config["hw_rnode_frequency"], - bandwidth = self.config["hw_rnode_bandwidth"], - txpower = self.config["hw_rnode_tx_power"], - sf = self.config["hw_rnode_spreading_factor"], - cr = self.config["hw_rnode_coding_rate"], - flow_control = None, - id_interval = self.config["hw_rnode_beaconinterval"], - id_callsign = self.config["hw_rnode_beacondata"], - allow_bluetooth = False, - st_alock = atl_short, - lt_alock = atl_long, - force_ble = True, - ble_name = bt_device_name, - ) + interface_config = { + "name": "RNodeInterface", + "port": None, + "frequency": self.config["hw_rnode_frequency"], + "bandwidth": self.config["hw_rnode_bandwidth"], + "txpower": self.config["hw_rnode_tx_power"], + "spreadingfactor": self.config["hw_rnode_spreading_factor"], + "codingrate": self.config["hw_rnode_coding_rate"], + "flow_control": False, + "id_interval": self.config["hw_rnode_beaconinterval"], + "id_callsign": self.config["hw_rnode_beacondata"], + "st_alock": atl_short, + "lt_alock": atl_long, + "allow_bluetooth": False, + "target_device_name": None, + "force_ble": True, + "ble_name": bt_device_name, + "ble_addr": None, + } else: - rnodeinterface = RNS.Interfaces.Android.RNodeInterface.RNodeInterface( - RNS.Transport, - "RNodeInterface", - target_port, - frequency = self.config["hw_rnode_frequency"], - bandwidth = self.config["hw_rnode_bandwidth"], - txpower = self.config["hw_rnode_tx_power"], - sf = self.config["hw_rnode_spreading_factor"], - cr = self.config["hw_rnode_coding_rate"], - flow_control = None, - id_interval = self.config["hw_rnode_beaconinterval"], - id_callsign = self.config["hw_rnode_beacondata"], - allow_bluetooth = rnode_allow_bluetooth, - target_device_name = bt_device_name, - st_alock = atl_short, - lt_alock = atl_long, - ) + interface_config = { + "name": "RNodeInterface", + "port": target_port, + "frequency": self.config["hw_rnode_frequency"], + "bandwidth": self.config["hw_rnode_bandwidth"], + "txpower": self.config["hw_rnode_tx_power"], + "spreadingfactor": self.config["hw_rnode_spreading_factor"], + "codingrate": self.config["hw_rnode_coding_rate"], + "flow_control": False, + "id_interval": self.config["hw_rnode_beaconinterval"], + "id_callsign": self.config["hw_rnode_beacondata"], + "st_alock": atl_short, + "lt_alock": atl_long, + "allow_bluetooth": rnode_allow_bluetooth, + "target_device_name": bt_device_name, + "force_ble": False, + "ble_name": None, + "ble_addr": None, + } + rnodeinterface = RNS.Interfaces.Android.RNodeInterface.RNodeInterface(RNS.Transport, interface_config) rnodeinterface.OUT = True if RNS.Reticulum.transport_enabled(): @@ -3603,6 +3609,7 @@ class SidebandCore(): except Exception as e: RNS.log("Error while adding RNode Interface. The contained exception was: "+str(e)) + RNS.trace_exception(e) self.interface_rnode = None self.interface_rnode_adding = False @@ -3678,15 +3685,14 @@ class SidebandCore(): else: ifac_size = None - tcpinterface = RNS.Interfaces.TCPInterface.TCPClientInterface( - RNS.Transport, - "TCPClientInterface", - tcp_host, - tcp_port, - kiss_framing = False, - i2p_tunneled = False - ) - + interface_config = { + "name": "TCPClientInterface", + "target_host": tcp_host, + "target_port": tcp_port, + "kiss_framing": False, + "i2p_tunneled": False, + } + tcpinterface = RNS.Interfaces.TCPInterface.TCPClientInterface(RNS.Transport, interface_config) tcpinterface.OUT = True if RNS.Reticulum.transport_enabled(): @@ -3730,13 +3736,14 @@ class SidebandCore(): else: ifac_size = None - i2pinterface = RNS.Interfaces.I2PInterface.I2PInterface( - RNS.Transport, - "I2PInterface", - RNS.Reticulum.storagepath, - [self.config["connect_i2p_b32"]], - connectable = False, - ) + interface_config = { + "name": "I2PInterface", + "storagepath": RNS.Reticulum.storagepath, + "peers": [self.config["connect_i2p_b32"]], + "connectable": False, + } + + i2pinterface = RNS.Interfaces.I2PInterface.I2PInterface(RNS.Transport, interface_config) i2pinterface.OUT = True @@ -3789,16 +3796,15 @@ class SidebandCore(): else: ifac_netkey = self.config["connect_serial_ifac_passphrase"] - serialinterface = RNS.Interfaces.Android.SerialInterface.SerialInterface( - RNS.Transport, - "SerialInterface", - target_device["port"], - self.config["hw_serial_baudrate"], - self.config["hw_serial_databits"], - self.config["hw_serial_parity"], - self.config["hw_serial_stopbits"], - ) - + interface_config = { + "name": "SerialInterface", + "port": target_device["port"], + "speed": self.config["hw_serial_baudrate"], + "databits": self.config["hw_serial_databits"], + "parity": self.config["hw_serial_parity"], + "stopbits": self.config["hw_serial_stopbits"], + } + serialinterface = RNS.Interfaces.Android.SerialInterface.SerialInterface(RNS.Transport, interface_config) serialinterface.OUT = True if RNS.Reticulum.transport_enabled(): @@ -3842,23 +3848,22 @@ class SidebandCore(): else: ifac_netkey = self.config["connect_modem_ifac_passphrase"] - modeminterface = RNS.Interfaces.Android.KISSInterface.KISSInterface( - RNS.Transport, - "ModemInterface", - target_device["port"], - self.config["hw_modem_baudrate"], - self.config["hw_modem_databits"], - self.config["hw_modem_parity"], - self.config["hw_modem_stopbits"], - self.config["hw_modem_preamble"], - self.config["hw_modem_tail"], - self.config["hw_modem_persistence"], - self.config["hw_modem_slottime"], - False, # flow control - self.config["hw_modem_beaconinterval"], - self.config["hw_modem_beacondata"], - ) - + interface_config = { + "name": "ModemInterface", + "port": target_device["port"], + "speed": self.config["hw_modem_baudrate"], + "databits": self.config["hw_modem_databits"], + "parity": self.config["hw_modem_parity"], + "stopbits": self.config["hw_modem_stopbits"], + "preamble": self.config["hw_modem_preamble"], + "txtail": self.config["hw_modem_tail"], + "persistence": self.config["hw_modem_persistence"], + "slottime": self.config["hw_modem_slottime"], + "flow_control": False, + "beacon_interval": self.config["hw_modem_beaconinterval"], + "beacon_data": self.config["hw_modem_beacondata"], + } + modeminterface = RNS.Interfaces.Android.KISSInterface.KISSInterface(RNS.Transport, interface_config) modeminterface.OUT = True if RNS.Reticulum.transport_enabled(): From bf8fbe5f86d5dbffdcefc01ebd3c4669f0cb02f0 Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Sat, 23 Nov 2024 14:24:45 +0100 Subject: [PATCH 08/30] Fixed UI redraw bug causing black screen on Android on app resume --- sbapp/main.py | 39 +++++++++++++++++++++------------------ 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/sbapp/main.py b/sbapp/main.py index ffc4a45..0549cec 100644 --- a/sbapp/main.py +++ b/sbapp/main.py @@ -303,7 +303,6 @@ class SidebandApp(MDApp): self.hw_error_dialog = None self.final_load_completed = False - self.wants_wake_update = False self.service_last_available = 0 self.closing_app = False @@ -700,9 +699,12 @@ class SidebandApp(MDApp): else: RNS.log("Conversations view did not exist", RNS.LOG_DEBUG) - def cb(dt): - self.wants_wake_update = True - Clock.schedule_once(cb, 1.2) + def ui_update_job(): + time.sleep(0.05) + def cb(dt): + self.perform_wake_update() + Clock.schedule_once(cb, 0.1) + threading.Thread(target=ui_update_job, daemon=True).start() RNS.log("App resumed", RNS.LOG_DEBUG) @@ -719,6 +721,21 @@ class SidebandApp(MDApp): else: return False + def perform_wake_update(self): + # This workaround mitigates a bug in Kivy on Android + # which causes the UI to turn black on app resume, + # probably due to an invalid GL draw context. By + # simply opening and immediately closing the nav + # drawer, we force the UI to do a frame redraw, which + # results in the UI actually being visible again. + if RNS.vendor.platformutils.is_android(): + RNS.log("Performing app wake UI update", RNS.LOG_DEBUG) + self.root.ids.nav_drawer.set_state("open") + def cb(dt): + self.root.ids.nav_drawer.set_state("closed") + Clock.schedule_once(cb, 0) + + def check_bluetooth_permissions(self): if RNS.vendor.platformutils.get_platform() == "android": mActivity = autoclass('org.kivy.android.PythonActivity').mActivity @@ -977,11 +994,6 @@ class SidebandApp(MDApp): if self.conversations_view != None: self.conversations_view.update() - if self.wants_wake_update: - self.wants_wake_update = False - if self.conversations_view != None: - self.conversations_view.update() - if self.sideband.getstate("app.flags.lxmf_sync_dialog_open", allow_cache=True) and self.sync_dialog != None: state = self.sideband.message_router.propagation_transfer_state @@ -1003,11 +1015,6 @@ class SidebandApp(MDApp): if self.announces_view != None: self.announces_view.update() - if self.wants_wake_update: - self.wants_wake_update = False - if self.announces_view != None: - self.announces_view.update() - elif self.root.ids.screen_manager.current == "map_screen": if self.map_screen and hasattr(self.map_screen.ids.map_layout, "map") and self.map_screen.ids.map_layout.map != None: self.sideband.config["map_lat"] = self.map_screen.ids.map_layout.map.lat @@ -1018,10 +1025,6 @@ class SidebandApp(MDApp): if self.last_telemetry_received > self.last_map_update: self.map_update_markers() - if self.wants_wake_update: - self.wants_wake_update = False - self.map_update_markers() - if self.sideband.getstate("app.flags.new_conversations", allow_cache=True): if self.conversations_view != None: self.conversations_view.update() From a9269f20d45ce47e06e5af9815a03503783807bd Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Sat, 23 Nov 2024 17:19:17 +0100 Subject: [PATCH 09/30] Updated graphics --- sbapp/kivymd/images/folder.png | Bin 2311 -> 5552 bytes sbapp/kivymd/images/folder_orange.png | Bin 0 -> 2311 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 sbapp/kivymd/images/folder_orange.png diff --git a/sbapp/kivymd/images/folder.png b/sbapp/kivymd/images/folder.png index fe0ed5bab2fe2fa84524331f1b7cb9c4f1c0b6ab..0e500841a840df19d29b6c890784b3900cbc11a1 100644 GIT binary patch literal 5552 zcmeHLX;f3!7QR7bP@vNK5V2|u3PsI4kOTr~0)iz#kygsE%FWHa5JLu&3k0e{i}+BX z)+)7Wk*Bq^wFuN9DxiQuK`Ba|Xsec~O0^D16|owi=LTeW?OJcSUi;6RbvXO%v-fxQ z`OZF@+-!^pTjn^$Z3+ZIjv>L4NC+YUkp$Vf7VYeYT(rfa+<>WL4oE9>(s zn9w-wV8!eEfBDyd>rz)Ft9daJ_Ce&m__zA)?%k&~95lBNRGeJZCJP9d{~o*U&_?gv zM4l@9zTfKg1@eMNg@%)#S#6wKQD!hVRNOdF%XY^+MBy*bbJ;w5AI?~0bnE4;j=F?5 z%{VZpr0@t|S>UF;bUM?u>DHoev*_RpYfm=%cRgO9 z^SZV$dn-bEJ?Z^;|3?n%`db%ok;9hu6a`8 znDB7hgN)eCb4Zh})P9o^yW(Nix3d!-oqk{XW5K(Ja+Z<|{(G3$vrfeB(ww;!duz@Y zF9dAJD5xYkIla?MLCc?*tBxGh3{@-odTg~X=!$)6jW<9Z8x&E|c(gQBh-g$aSgw(w zG=oZOIY(cAgBC`VC{C84i3+ud(owB4qcHE7KLOe5rLfI z?>EKQAOrv^6o<(MRkB(yG>9k`Tp^GNHJw7XnBYngB^qR#SffM992$qlpavQgsVs`$ z6tb^QjtL_rK_d`gB%&nYxK>D~r=_LQ(%3YOE|Ja@2n2Kni_T(Efdy5cuEt>lRjqd? zAcip{s2bI@lg>_asY*K zc}$pr$f#U|g;6;$%A~?_zKn{YJQhnX;9?B6%mO7xgh3je3I@liP{D~PU8_#COb~<% zmqdh!C@dOdOcRj|;}~!dQ9>2!6vNm=ltP8Bz+r+-rZ=0#<_VYrE{`W*GkIf1Z=gCo z$V39uQeiDK#IXp$W&p7;k*5H_q6d2sTFR(K7p2i8izuVDd30I|u9Fo;QPO6bBu=M|BOM@q;Z#&^i4$NAPa#RLIuQlcVJ@*H3M_Ay3`Q_66_K+UR1Sjhs4&M{ zKt*6ohN2iIW1*ah=z0x?r@=aONh078a0SxS@_&r{ishiZCR(Q@p~NWw!l+CpmBs#V z!ssI#rV};e>5hHrf8xZ~Vlb}A0KZ`!C|;lz(npKo2xmmu`3Ikov-k&B0MO4Dc_x0J z)AgLLXJX))jGt%MbGn|1foC#)o?U+%T~o$hr%*Nc7L*2FmQ1bb#EZL)?A2uw$Q$y3 z{3gvl#ReLCZSb3V2%@ShM^EqWwKtal)l57v8W(F+#J6(j zPw(6--*K7&LDttoBukq-;p$@R8Gm(l=*#mPX!$o+#mNt> zNnY$kjhJTZ*R=9e@r)O1Uwg^zXKedN!48XQPL+bAA0Nf{+V@qLPdc)nY4y$9yN@5Q zdFy=6Zm*V3bBjK#Wq}ol=kI*7L<{m049i;L?gZ82O75!Ct;VgzdAFY&iP>X1+_&X! zyKknf?wWJfZ2ZQ>Z`4ZF=NE1+mUMqo>Sq_=wScwE`*Lf{#h4*MlCht4KMX62^RGJE z9ZZINv|T$QT_`dSc2#V6>uW{k*qWZq=*_v=rKT%9E9;J#{56jw%T1{zCSL6Fn|}G* z2JF4F^P7*wFBc@u3V@347SzmeE-?y@ptIZ0LCL+#B*!v-zPj2u;mRiGG09mGV=>ptZke>Qk9*c8f>xO=6 zz5G?UcnY+w%DFj3xbu>GVp(n$#MnLSMBJgn2@9=i*RLw2o~2V;K3)$oj2WGE5mvPl zA70I&W9_ciS*H*AH$2FWvx4dd>(rv?08w7KY^4LQQioZnJ>nOk4;W^L@N003$MHd zy)IduXg46%SpP%CC&pV1WJp}GE_HQjdr#*17`;Z|cW$uw!p*p<^uhWK3cK1@*2E}H zlk>t!)jK~f8f+pTW-fI9q3HMG>lL}t)Zzy=D`$#-%!yGdjlVbb9o+n`YLe^D7FwZM zNNFoERhC~p?m3iE+PBuT2J*dRSD)?KeA{e4H@4`^zQFpovypmUN?yll^MksynrgrM zXSW!y7w0}aF!S!dm|H=THQS7upZIC!#Fu&nUOVQP6ii>O#+VY5NyHhcu zDR$9wyLk(n)4W7XdCe0O4^k$hV&3+w|LVW{oO93b{Lc4tKHuN(d(Q83zArz9mj_W> z(@+xtpzZ2{Cj$UcLI{AXDNWFY`;khc7VbvG1I6c6(tIyp$!Ns7oTLI^j{3YIi9FmT zC8-|o>h7#Qqp@4-+anYwqw`8?cf7NI{Ew8Fn27W7;KvIQVet_^nIuHTpEV)6x_g~S z+im~=oZ^ai@<|+(j{#<^_}(GA{`BStk2P{9#y$oL z#KeGa9sijB#ZFT63#HhaO@^K=@6x3M00nSh0{#x5b#8d>c0GcDYlcQ}=d^ zUzM6Vqo}FIEoPVbkuebx0fXIk+D@Qmd*c`K6|@)QS4EE{w)WR#o2T(UYu(0D64CZ@v^ZAq{|#|?KzpS(CMq2^TDTV zQU^@1GzeDiOg~TqmM-DPb3E7tV|zuRaB<4Zx9v{lA4Z|6(21v-+S>G|IEVJ%8((TfhtBhYEM58Sq|<9Y;Z`Q>ExtJjxFqA1-OOed@@n{hkTa}guWTj zI%uNS3vtvAe-J{cFaY-%{F;^>Cqgbeg9#Hu#j7k?$Iz3Wk)(Rt{y{o$>=7g2z^d=i z(VZTkq!pnH?$6m?$?&k-c|=;j);t9T?mtw)l_v^-u&GeT-`*7T>2Hz=##yDeR;QgH zbTz7FAFMD0Q*o?y|f1%QS726lx81Q|gdo z2*uvKOuI*#BeBWWgB!?>d@6LVtHI3=0vB@Gs-AOj|R#bnjg-za^i0Ih|sS93qo$FI?3J z?a&A7+>8aM^@d9-5A+dk2w69|3rz{iUdh?(k)T*ig{M)0Rd_}|*#~ox&-$&>^k~xR z{f(%VN^PhxO-I=Gp4e&I(mZBCnF%^yzlRLYzG0U5^7@rYARCG>gFs5+%&z#El@2od z_fInai2#O?vFsAbjRd7)`%y?>WeFXNwPb<5v)yMn4<&gAfz`mVXLJ7wf`Vjhy{Yc_ zn*+eg3UHMb_eG2P0{%U13;t#FuR~JHW8mBhyr?#;;9C`;r}wrE4u zFf3XWSqrGABc@+$@$ee;tY;C;Bj5D-NxiVF@mHzc_$kS02gmb{aQy3FRr9C;n890kZh1$oG(r7O(Iz_Shn4e5 zIN%;V?QoL{q+=HO>p5ChF{%%eXk#u{4g;)3w}PP-`f+Q`$J`tATgp}-TEr6X{U|Mq z`)tCj=Xz)N$8l()CpbP_qqr=#1z7nxL%;_GO$my1xlhZ2k00Q_)0wC9YOJYUEkKFWu&t0ZcPNBzg#xl_EiCVoudD@3p=TBs%D#Z TGlJZe{}bTq?18U77JB7>kpHcX diff --git a/sbapp/kivymd/images/folder_orange.png b/sbapp/kivymd/images/folder_orange.png new file mode 100644 index 0000000000000000000000000000000000000000..fe0ed5bab2fe2fa84524331f1b7cb9c4f1c0b6ab GIT binary patch literal 2311 zcmb_eeKZq#AOCHJ5wW-_MecTMjbi8WT!cnnUOVQP6ii>O#+VY5NyHhcu zDR$9wyLk(n)4W7XdCe0O4^k$hV&3+w|LVW{oO93b{Lc4tKHuN(d(Q83zArz9mj_W> z(@+xtpzZ2{Cj$UcLI{AXDNWFY`;khc7VbvG1I6c6(tIyp$!Ns7oTLI^j{3YIi9FmT zC8-|o>h7#Qqp@4-+anYwqw`8?cf7NI{Ew8Fn27W7;KvIQVet_^nIuHTpEV)6x_g~S z+im~=oZ^ai@<|+(j{#<^_}(GA{`BStk2P{9#y$oL z#KeGa9sijB#ZFT63#HhaO@^K=@6x3M00nSh0{#x5b#8d>c0GcDYlcQ}=d^ zUzM6Vqo}FIEoPVbkuebx0fXIk+D@Qmd*c`K6|@)QS4EE{w)WR#o2T(UYu(0D64CZ@v^ZAq{|#|?KzpS(CMq2^TDTV zQU^@1GzeDiOg~TqmM-DPb3E7tV|zuRaB<4Zx9v{lA4Z|6(21v-+S>G|IEVJ%8((TfhtBhYEM58Sq|<9Y;Z`Q>ExtJjxFqA1-OOed@@n{hkTa}guWTj zI%uNS3vtvAe-J{cFaY-%{F;^>Cqgbeg9#Hu#j7k?$Iz3Wk)(Rt{y{o$>=7g2z^d=i z(VZTkq!pnH?$6m?$?&k-c|=;j);t9T?mtw)l_v^-u&GeT-`*7T>2Hz=##yDeR;QgH zbTz7FAFMD0Q*o?y|f1%QS726lx81Q|gdo z2*uvKOuI*#BeBWWgB!?>d@6LVtHI3=0vB@Gs-AOj|R#bnjg-za^i0Ih|sS93qo$FI?3J z?a&A7+>8aM^@d9-5A+dk2w69|3rz{iUdh?(k)T*ig{M)0Rd_}|*#~ox&-$&>^k~xR z{f(%VN^PhxO-I=Gp4e&I(mZBCnF%^yzlRLYzG0U5^7@rYARCG>gFs5+%&z#El@2od z_fInai2#O?vFsAbjRd7)`%y?>WeFXNwPb<5v)yMn4<&gAfz`mVXLJ7wf`Vjhy{Yc_ zn*+eg3UHMb_eG2P0{%U13;t#FuR~JHW8mBhyr?#;;9C`;r}wrE4u zFf3XWSqrGABc@+$@$ee;tY;C;Bj5D-NxiVF@mHzc_$kS02gmb{aQy3FRr9C;n890kZh1$oG(r7O(Iz_Shn4e5 zIN%;V?QoL{q+=HO>p5ChF{%%eXk#u{4g;)3w}PP-`f+Q`$J`tATgp}-TEr6X{U|Mq z`)tCj=Xz)N$8l()CpbP_qqr=#1z7nxL%;_GO$my1xlhZ2k00Q_)0wC9YOJYUEkKFWu&t0ZcPNBzg#xl_EiCVoudD@3p=TBs%D#Z TGlJZe{}bTq?18U77JB7>kpHcX literal 0 HcmV?d00001 From 0cbd4c71aba2b790c8b47c1753e27068d2c1e0b4 Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Sat, 23 Nov 2024 17:23:12 +0100 Subject: [PATCH 10/30] Added message attachments from sharing intent on Android and drag-and-drop on desktop --- sbapp/main.py | 130 ++++++++++++++++++++++++++++++-- sbapp/patches/intent-filter.xml | 17 ++++- sbapp/sideband/core.py | 10 ++- 3 files changed, 146 insertions(+), 11 deletions(-) diff --git a/sbapp/main.py b/sbapp/main.py index 0549cec..31e4df8 100644 --- a/sbapp/main.py +++ b/sbapp/main.py @@ -1,6 +1,6 @@ __debug_build__ = False __disable_shaders__ = False -__version__ = "1.1.3" +__version__ = "1.1.4" __variant__ = "" import sys @@ -306,9 +306,11 @@ class SidebandApp(MDApp): self.service_last_available = 0 self.closing_app = False + self.file_manager = None self.attach_path = None self.attach_type = None self.attach_dialog = None + self.shared_attach_dialog = None self.rec_dialog = None self.last_msg_audio = None self.msg_sound = None @@ -892,6 +894,37 @@ class SidebandApp(MDApp): if data.lower().startswith(LXMF.LXMessage.URI_SCHEMA): action = "lxm_uri" + if intent_action == "android.intent.action.SEND": + try: + Intent = autoclass("android.content.Intent") + extras = intent.getExtras() + target = extras.get(Intent.EXTRA_STREAM) + mime_types = extras.get(Intent.EXTRA_MIME_TYPES) + target_uri = target.toString() + target_path = target.getPath() + target_filename = target.getLastPathSegment() + + RNS.log(f"Received share intent: {target_uri} / {target_path} / {target_filename}", RNS.LOG_DEBUG) + for cf in os.listdir(self.sideband.share_cache): + rt = os.path.join(self.sideband.share_cache, cf) + os.unlink(rt) + RNS.log("Removed previously cached data: "+str(rt), RNS.LOG_DEBUG) + + ContentResolver = autoclass("android.content.ContentResolver") + cr = mActivity.getContentResolver() + cache_path = os.path.join(self.sideband.share_cache, target_filename) + input_stream = cr.openInputStream(target) + with open(cache_path, "wb") as cache_file: + cache_file.write(bytes(input_stream.readAllBytes())) + RNS.log("Cached shared data from Android intent", RNS.LOG_DEBUG) + + action = "shared_data" + data = {"filename": target_filename, "data_path": cache_path} + + except Exception as e: + RNS.log(f"Error while getting intent action data: {e}", RNS.LOG_ERROR) + RNS.trace_exception(e) + if action != None: self.handle_action(action, data) @@ -899,6 +932,16 @@ class SidebandApp(MDApp): if action == "lxm_uri": self.ingest_lxm_uri(data) + if action == "shared_data": + RNS.log("Got shared data: "+str(data)) + def cb(dt): + try: + self.shared_attachment_action(data) + except Exception as e: + RNS.log("Error while handling external message attachment", RNS.LOG_ERROR) + RNS.trace_exception(e) + Clock.schedule_once(cb, 0.1) + def ingest_lxm_uri(self, lxm_uri): RNS.log("Ingesting LXMF paper message from URI: "+str(lxm_uri), RNS.LOG_DEBUG) self.sideband.lxm_ingest_uri(lxm_uri) @@ -1086,6 +1129,9 @@ class SidebandApp(MDApp): self.quit_action(None) return True + def file_dropped(self, window, file_path, x, y, *args): + self.shared_attachment_action({"data_path": file_path.decode("utf-8")}) + def on_start(self): self.last_exit_event = time.time() self.root.ids.screen_manager.transition = self.slide_transition @@ -1096,6 +1142,7 @@ class SidebandApp(MDApp): EventLoop.window.bind(on_key_down=self.keydown_event) EventLoop.window.bind(on_key_up=self.keyup_event) Window.bind(on_request_close=self.close_requested) + Window.bind(on_drop_file=self.file_dropped) if __variant__ != "": variant_str = " "+__variant__ @@ -1701,7 +1748,8 @@ class SidebandApp(MDApp): def message_fm_exited(self, *args): self.manager_open = False - self.file_manager.close() + if self.file_manager != None: + self.file_manager.close() def message_select_file_action(self, sender=None): perm_ok = False @@ -1716,11 +1764,20 @@ class SidebandApp(MDApp): if perm_ok and path != None: try: - self.file_manager = MDFileManager( - exit_manager=self.message_fm_exited, - select_path=self.message_fm_got_path, - ) - # self.file_manager.ext = ["*"] + if self.attach_type in ["lbimg", "defimg", "hqimg"]: + self.file_manager = MDFileManager( + exit_manager=self.message_fm_exited, + select_path=self.message_fm_got_path, + # Current KivyMD preview implementation is too slow to be reliable on Android + preview=False) + else: + self.file_manager = MDFileManager( + exit_manager=self.message_fm_exited, + select_path=self.message_fm_got_path, + preview=False) + + # self.file_manager.ext = [] + # self.file_manager.search = "all" self.file_manager.show(path) except Exception as e: @@ -2197,6 +2254,65 @@ class SidebandApp(MDApp): ate_dialog.open() + def shared_attachment_action(self, attachment_data): + if not self.root.ids.screen_manager.current == "messages_screen": + if RNS.vendor.platformutils.is_android(): + toast("Please select a conversation first") + else: + ok_button = MDRectangleFlatButton(text="OK",font_size=dp(18)) + ate_dialog = MDDialog( + title="No active conversation", + text="To drop files as attachments, please open a conversation first", + buttons=[ ok_button ], + ) + ok_button.bind(on_release=ate_dialog.dismiss) + ate_dialog.open() + else: + self.rec_dialog_is_open = False + + def a_img_lb(sender): + self.attach_type="lbimg" + self.shared_attach_dialog.dismiss() + self.shared_attach_dialog.att_exc() + def a_img_def(sender): + self.attach_type="defimg" + self.shared_attach_dialog.dismiss() + self.shared_attach_dialog.att_exc() + def a_img_hq(sender): + self.attach_type="hqimg" + self.shared_attach_dialog.dismiss() + self.shared_attach_dialog.att_exc() + def a_file(sender): + self.attach_type="file" + self.shared_attach_dialog.dismiss() + self.shared_attach_dialog.att_exc() + + if self.shared_attach_dialog == None: + ss = int(dp(18)) + cancel_button = MDRectangleFlatButton(text="Cancel", font_size=dp(18)) + ad_items = [ + DialogItem(IconLeftWidget(icon="message-image-outline", on_release=a_img_lb), text="[size="+str(ss)+"]Low-bandwidth Image[/size]", on_release=a_img_lb), + DialogItem(IconLeftWidget(icon="file-image", on_release=a_img_def), text="[size="+str(ss)+"]Medium Image[/size]", on_release=a_img_def), + DialogItem(IconLeftWidget(icon="image-outline", on_release=a_img_hq), text="[size="+str(ss)+"]High-res Image[/size]", on_release=a_img_hq), + DialogItem(IconLeftWidget(icon="file-outline", on_release=a_file), text="[size="+str(ss)+"]File Attachment[/size]", on_release=a_file)] + + self.shared_attach_dialog = MDDialog( + title="Add Attachment", + type="simple", + text="Select how you want to attach this data to the next message sent\n", + items=ad_items, + buttons=[ cancel_button ], + width_offset=dp(32), + ) + + cancel_button.bind(on_release=self.shared_attach_dialog.dismiss) + + def att_exc(): + self.message_fm_got_path(attachment_data["data_path"]) + + self.shared_attach_dialog.att_exc = att_exc + self.shared_attach_dialog.open() + def update_message_widgets(self): toolbar_items = self.messages_view.ids.messages_toolbar.ids.right_actions.children mode_item = toolbar_items[1] diff --git a/sbapp/patches/intent-filter.xml b/sbapp/patches/intent-filter.xml index 48850af..6ec9ab0 100644 --- a/sbapp/patches/intent-filter.xml +++ b/sbapp/patches/intent-filter.xml @@ -13,11 +13,22 @@ - - - + + + + + + + + + + + + + + \ No newline at end of file diff --git a/sbapp/sideband/core.py b/sbapp/sideband/core.py index fdfc56f..c3542b8 100644 --- a/sbapp/sideband/core.py +++ b/sbapp/sideband/core.py @@ -203,6 +203,10 @@ class SidebandCore(): if not os.path.isdir(self.rec_cache): os.makedirs(self.rec_cache) + self.share_cache = self.cache_dir+"/share" + if not os.path.isdir(self.share_cache): + os.makedirs(self.share_cache) + self.icon = self.asset_dir+"/icon.png" self.icon_48 = self.asset_dir+"/icon_48.png" self.icon_32 = self.asset_dir+"/icon_32.png" @@ -4369,7 +4373,11 @@ class SidebandCore(): if not originator and LXMF.FIELD_AUDIO in message.fields and ptt_enabled: self.ptt_event(message) - should_notify = False + if self.gui_conversation() != context_dest: + if not RNS.vendor.platformutils.is_android(): + should_notify = True + else: + should_notify = False if self.is_client: should_notify = False From fe4c61880ef1a8fc7778629a2e113b3416ed372f Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Sun, 24 Nov 2024 12:45:49 +0100 Subject: [PATCH 11/30] Fixed new conversation error dialog being wonky --- sbapp/main.py | 4 ++++ sbapp/sideband/core.py | 6 +++++- sbapp/ui/conversations.py | 4 ++-- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/sbapp/main.py b/sbapp/main.py index 31e4df8..fa8eeb3 100644 --- a/sbapp/main.py +++ b/sbapp/main.py @@ -2627,11 +2627,15 @@ class SidebandApp(MDApp): RNS.log("Error while creating conversation: "+str(e), RNS.LOG_ERROR) if new_result: + dialog.d_content.ids["n_address_field"].helper_text = "" + dialog.d_content.ids["n_address_field"].helper_text_mode = "on_focus" dialog.d_content.ids["n_address_field"].error = False dialog.dismiss() if self.conversations_view != None: self.conversations_view.update() else: + dialog.d_content.ids["n_address_field"].helper_text = "Invalid address, check your input" + dialog.d_content.ids["n_address_field"].helper_text_mode = "persistent" dialog.d_content.ids["n_address_field"].error = True # dialog.d_content.ids["n_error_field"].text = "Could not create conversation. Check your input." diff --git a/sbapp/sideband/core.py b/sbapp/sideband/core.py index c3542b8..137dc8d 100644 --- a/sbapp/sideband/core.py +++ b/sbapp/sideband/core.py @@ -4264,7 +4264,11 @@ class SidebandCore(): try: addr_b = bytes.fromhex(dest_str) - self._db_create_conversation(addr_b, name, trusted) + if addr_b == self.lxmf_destination.hash: + RNS.log("Cannot create conversation with own LXMF address", RNS.LOG_ERROR) + return False + else: + self._db_create_conversation(addr_b, name, trusted) except Exception as e: RNS.log("Error while creating conversation: "+str(e), RNS.LOG_ERROR) diff --git a/sbapp/ui/conversations.py b/sbapp/ui/conversations.py index 9dcc210..d624100 100644 --- a/sbapp/ui/conversations.py +++ b/sbapp/ui/conversations.py @@ -524,8 +524,8 @@ Builder.load_string(""" id: n_address_field max_text_length: 32 hint_text: "Address" - helper_text: "Error, check your input" - helper_text_mode: "on_error" + helper_text: "" + helper_text_mode: "on_focus" text: "" font_size: dp(24) From 7885f39708c26586a31768426399a58a81fadbb6 Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Mon, 25 Nov 2024 20:59:50 +0100 Subject: [PATCH 12/30] Updated dependencies --- setup.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/setup.py b/setup.py index 2bc2e76..55d83d6 100644 --- a/setup.py +++ b/setup.py @@ -99,8 +99,8 @@ setuptools.setup( ] }, install_requires=[ - "rns>=0.8.5", - "lxmf>=0.5.7", + "rns>=0.8.6", + "lxmf>=0.5.8", "kivy>=2.3.0", "pillow>=10.2.0", "qrcode", @@ -108,11 +108,10 @@ setuptools.setup( "ffpyplayer", "sh", "numpy<=1.26.4", - "pycodec2;platform_system!='Windows'", + "pycodec2;sys.platform!='Windows' and sys.platform!='darwin'", "pyaudio;sys.platform=='linux'", "pyobjus;sys.platform=='darwin'", - "pyogg;sys.platform=='darwin'", - "pyogg;platform_system=='Windows'", + "pyogg;sys.platform=='Windows'", ], python_requires='>=3.7', ) From 4004151f397202af82d3870f1e179b78d19c037f Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Mon, 25 Nov 2024 21:00:08 +0100 Subject: [PATCH 13/30] Added user interface scaling --- sbapp/main.py | 119 +++++++++++++++++++++++++++++++++++++++++++- sbapp/ui/layouts.py | 36 +++++++++++++- 2 files changed, 152 insertions(+), 3 deletions(-) diff --git a/sbapp/main.py b/sbapp/main.py index fa8eeb3..a8446c2 100644 --- a/sbapp/main.py +++ b/sbapp/main.py @@ -24,6 +24,51 @@ import base64 import threading import RNS.vendor.umsgpack as msgpack +app_ui_scaling_path = None +def apply_ui_scale(): + global app_ui_scaling_path + default_scale = os.environ["KIVY_METRICS_DENSITY"] if "KIVY_METRICS_DENSITY" in os.environ else "unknown" + config_path = None + ui_scale_path = None + + try: + if RNS.vendor.platformutils.is_android(): + import plyer + ui_scale_path = plyer.storagepath.get_application_dir()+"/io.unsigned.sideband/files/app_storage/ui_scale" + else: + if config_path == None: + import sbapp.plyer as plyer + ui_scale_path = plyer.storagepath.get_home_dir()+"/.config/sideband/app_storage/ui_scale" + if ui_scale_path.startswith("file://"): + ui_scale_path = ui_scale_path.replace("file://", "") + else: + ui_scale_path = config_path+"/app_storage/ui_scale" + + app_ui_scaling_path = ui_scale_path + + except Exception as e: + RNS.log(f"Error while locating UI scale file: {e}", RNS.LOG_ERROR) + + if ui_scale_path != None: + RNS.log("Default scaling factor on this platform is "+str(default_scale), RNS.LOG_NOTICE) + try: + RNS.log("Looking for scaling info in "+str(ui_scale_path)) + if os.path.isfile(ui_scale_path): + scale_factor = None + with open(ui_scale_path, "r") as sf: + scale_factor = float(sf.readline()) + + if scale_factor != None: + if scale_factor >= 0.3 and scale_factor <= 5.0: + os.environ["KIVY_METRICS_DENSITY"] = str(scale_factor) + RNS.log("UI scaling factor set to "+str(os.environ["KIVY_METRICS_DENSITY"]), RNS.LOG_NOTICE) + elif scale_factor == 0.0: + RNS.log("Using default UI scaling factor", RNS.LOG_NOTICE) + + + except Exception as e: + RNS.log(f"Error while reading UI scaling factor: {e}", RNS.LOG_ERROR) + if args.export_settings: from .sideband.core import SidebandCore sideband = SidebandCore( @@ -143,9 +188,11 @@ if args.daemon: NewConv = DaemonElement; Telemetry = DaemonElement; ObjectDetails = DaemonElement; Announces = DaemonElement; Messages = DaemonElement; ts_format = DaemonElement; messages_screen_kv = DaemonElement; plyer = DaemonElement; multilingual_markup = DaemonElement; ContentNavigationDrawer = DaemonElement; DrawerList = DaemonElement; IconListItem = DaemonElement; escape_markup = DaemonElement; - SoundLoader = DaemonElement; + SoundLoader = DaemonElement; BoxLayout = DaemonElement; else: + apply_ui_scale() + from kivymd.app import MDApp app_superclass = MDApp from kivy.core.window import Window @@ -157,6 +204,7 @@ else: from kivy.effects.scroll import ScrollEffect from kivy.uix.screenmanager import ScreenManager from kivy.uix.screenmanager import FadeTransition, NoTransition, SlideTransition + from kivy.uix.boxlayout import BoxLayout from kivymd.uix.list import OneLineIconListItem, IconLeftWidget from kivy.properties import StringProperty from kivymd.uix.button import BaseButton, MDIconButton @@ -2729,6 +2777,72 @@ class SidebandApp(MDApp): if no_transition: self.root.ids.screen_manager.transition = self.slide_transition + def configure_ui_scaling_action(self, sender=None): + global app_ui_scaling_path + try: + cancel_button = MDRectangleFlatButton(text="Cancel",font_size=dp(18)) + set_button = MDRectangleFlatButton(text="Set",font_size=dp(18), theme_text_color="Custom", line_color=self.color_accept, text_color=self.color_accept) + + dialog_content = UIScaling() + dialog = MDDialog( + title="UI Scaling", + type="custom", + content_cls=dialog_content, + buttons=[ set_button, cancel_button ], + # elevation=0, + ) + dialog.d_content = dialog_content + dialog.d_content.ids["scaling_factor"].text = os.environ["KIVY_METRICS_DENSITY"] if "KIVY_METRICS_DENSITY" in os.environ else "0.0" + def dl_yes(s): + new_sf = 1.0 + scaling_ok = False + try: + si = dialog.d_content.ids["scaling_factor"].text + sf = float(si) + if (sf >= 0.3 and sf <= 5.0) or sf == 0.0: + new_sf = sf + scaling_ok = True + + except Exception as e: + RNS.log("Error while getting scaling factor from user: "+str(e), RNS.LOG_ERROR) + + if scaling_ok: + dialog.d_content.ids["scaling_factor"].helper_text = "" + dialog.d_content.ids["scaling_factor"].helper_text_mode = "on_focus" + dialog.d_content.ids["scaling_factor"].error = False + dialog.dismiss() + if app_ui_scaling_path == None: + RNS.log("No path to UI scaling factor file could be found, cannot save scaling factor", RNS.LOG_ERROR) + else: + try: + with open(app_ui_scaling_path, "w") as sfile: + sfile.write(str(new_sf)) + RNS.log(f"Saved configured scaling factor {new_sf} to {app_ui_scaling_path}", RNS.LOG_DEBUG) + except Exception as e: + RNS.log(f"Error while saving scaling factor {new_sf} to {app_ui_scaling_path}: {e}", RNS.LOG_ERROR) + + else: + dialog.d_content.ids["scaling_factor"].helper_text = "Invalid scale factor, check your input" + dialog.d_content.ids["scaling_factor"].helper_text_mode = "persistent" + dialog.d_content.ids["scaling_factor"].error = True + + def dl_no(s): + dialog.dismiss() + + def dl_ds(s): + self.dialog_open = False + + set_button.bind(on_release=dl_yes) + cancel_button.bind(on_release=dl_no) + + dialog.bind(on_dismiss=dl_ds) + dialog.open() + self.dialog_open = True + + except Exception as e: + RNS.log("Error while creating UI scaling dialog: "+str(e), RNS.LOG_ERROR) + + def settings_action(self, sender=None, direction="left"): if self.settings_ready: self.settings_open(direction=direction) @@ -5966,6 +6080,9 @@ class DialogItem(OneLineIconListItem): class MDMapIconButton(MDIconButton): pass +class UIScaling(BoxLayout): + pass + if not args.daemon: from kivy.base import ExceptionManager, ExceptionHandler class SidebandExceptionHandler(ExceptionHandler): diff --git a/sbapp/ui/layouts.py b/sbapp/ui/layouts.py index 6a466f0..fe9f2ab 100644 --- a/sbapp/ui/layouts.py +++ b/sbapp/ui/layouts.py @@ -1260,6 +1260,28 @@ MDScreen: """ layout_settings_screen = """ + + orientation: "vertical" + spacing: "24dp" + size_hint_y: None + height: self.minimum_height+dp(0) + + MDLabel: + id: scaling_info + markup: True + text: "You can scale the entire Sideband UI by specifying a scaling factor in the field below. After setting it, restart sideband for the scaling to take effect.\\n\\nSet to 0.0 to disable scaling adjustments." + size_hint_y: None + text_size: self.width, None + height: self.texture_size[1] + + MDTextField: + id: scaling_factor + hint_text: "Scaling Factor" + helper_text: "From 0.3 to 5.0" + helper_text_mode: "on_focus" + text: "" + font_size: dp(24) + MDScreen: name: "settings_screen" @@ -1399,11 +1421,21 @@ MDScreen: size_hint_y: None height: self.texture_size[1] + MDRectangleFlatIconButton: + id: appearance_ui_scaling + icon: "relative-scale" + text: "Configure UI Scaling" + padding: [dp(0), dp(14), dp(0), dp(14)] + icon_size: dp(24) + font_size: dp(16) + size_hint: [1.0, None] + on_release: root.app.configure_ui_scaling_action(self) + MDBoxLayout: orientation: "horizontal" size_hint_y: None - padding: [0,0,dp(24),dp(0)] - height: dp(48) + padding: [0,dp(14),dp(24),dp(0)] + height: dp(62) MDLabel: text: "Notifications" From 10b073d1c2e1c2a1c356319c6ec7b8b1dc730d6a Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Mon, 25 Nov 2024 21:47:15 +0100 Subject: [PATCH 14/30] Updated readme --- README.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index c1d8288..2a429fb 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,7 @@ Sideband can run on most computing devices, but installation methods vary by dev ## On Android -For your Android devices, you can install Sideband through F-Droid, by adding the [Between the Borders Repo](https://reticulum.betweentheborders.com/fdroid/repo/), or you can download an [APK on the latest release](https://github.com/markqvist/Sideband/releases/latest) page. Both sources are signed with the same release keys, and can be used interchangably. +For your Android devices, you can install Sideband through F-Droid, by adding the [Between the Borders Repo](https://reticulum.betweentheborders.com/fdroid/repo/), or you can download an [APK on the latest release page](https://github.com/markqvist/Sideband/releases/latest). Both sources are signed with the same release keys, and can be used interchangably. After the application is installed on your Android device, it is also possible to pull updates directly through the **Repository** section of the application. @@ -154,13 +154,18 @@ sideband ## On macOS -On macOS, you can install Sideband with `pip3` or `pipx`. Due to the many different potential Python versions and install paths across macOS versions, the easiest install method is to use `pipx`. +You can download a DMG with Sideband for macOS (ARM and Intel) from the [latest release page](https://github.com/markqvist/Sideband/releases/latest). If you install Sideband from the DMG file, it is still recommended to install the `rns` package via the `pip` or `pipx` package manager, so you can use the RNS utility programs, like `rnstatus` to see interface and connectivity status from the terminal. If you don't already have the `pipx` package manager installed, it can be installed via [Homebrew](https://brew.sh/) with `brew install pipx`. ```bash # Install Sideband and dependencies on macOS using pipx: pipx install sbapp + +# It's recommended to also install the rns package for utilites: +pipx install rns + +# Make sure programs installed by pipx are runnable by the user pipx ensurepath # Run it From 32b6fd0a819fb9b10e37ed003a20901e9ebdfaa3 Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Tue, 26 Nov 2024 14:46:01 +0100 Subject: [PATCH 15/30] Fixed Windows install dependencies --- setup.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index 55d83d6..1b330e6 100644 --- a/setup.py +++ b/setup.py @@ -108,10 +108,10 @@ setuptools.setup( "ffpyplayer", "sh", "numpy<=1.26.4", - "pycodec2;sys.platform!='Windows' and sys.platform!='darwin'", + "pycodec2;sys.platform!='Windows' and sys.platform!='win32' and sys.platform!='darwin'", "pyaudio;sys.platform=='linux'", "pyobjus;sys.platform=='darwin'", - "pyogg;sys.platform=='Windows'", + "pyogg;sys.platform=='Windows' and sys.platform!='win32'", ], python_requires='>=3.7', ) From 364463c541a48028e0f2deaf33dc64bca194a827 Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Tue, 26 Nov 2024 14:46:05 +0100 Subject: [PATCH 16/30] Updated readme --- README.md | 48 ++++++++++++++++++++++++++---------------------- 1 file changed, 26 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index 2a429fb..4c0715a 100644 --- a/README.md +++ b/README.md @@ -47,6 +47,8 @@ After the application is installed on your Android device, it is also possible t On all Linux-based operating systems, Sideband is available as a `pipx`/`pip` package. This installation method **includes desktop integration**, so that Sideband will show up in your applications menu and launchers. Below are install steps for the most common recent Linux distros. For Debian 11, see the end of this section. +**Please note!** The very latest Python release, Python 3.13 is currently **not** compatible with the Kivy framework, that Sideband uses to render its user interface. If your Linux distribution uses Python 3.13 as its default Python installation, you will need to install an earlier version as well. Using [the latest release of Python 3.12](https://www.python.org/downloads/release/python-3127/) is recommended. + You will first need to install a few dependencies for audio messaging and Codec2 support to work: ```bash @@ -66,6 +68,10 @@ Once those are installed, install the Sideband application itself: ```bash # Finally, install Sideband using pipx: pipx install sbapp + +# If you need to specify a specific Python version, +# use something like the following: +pipx install sbapp --python python3.12 ``` After installation, you can now run Sideband in a number of different ways: @@ -109,8 +115,9 @@ pip install sbapp --break-system-packages # any of the normal UI dependencies: pip install sbapp --no-dependencies -# In the above case, you will still need to -# manually install the RNS and LXMF dependencies: +# In the case of using --no-dependencies, you +# will still need to manually install the RNS +# and LXMF dependencies: pip install rns lxmf # Install Sideband on Debian 11 and derivatives: @@ -156,31 +163,26 @@ sideband You can download a DMG with Sideband for macOS (ARM and Intel) from the [latest release page](https://github.com/markqvist/Sideband/releases/latest). If you install Sideband from the DMG file, it is still recommended to install the `rns` package via the `pip` or `pipx` package manager, so you can use the RNS utility programs, like `rnstatus` to see interface and connectivity status from the terminal. -If you don't already have the `pipx` package manager installed, it can be installed via [Homebrew](https://brew.sh/) with `brew install pipx`. +**Please note!** The very latest Python release, Python 3.13 is currently **not** compatible with the Kivy framework, that Sideband uses to render its user interface. If your version of macOS uses Python 3.13 as its default Python installation, you will need to install an earlier version as well. Using [the latest release of Python 3.12](https://www.python.org/downloads/release/python-3127/) is recommended. -```bash -# Install Sideband and dependencies on macOS using pipx: -pipx install sbapp - -# It's recommended to also install the rns package for utilites: -pipx install rns - -# Make sure programs installed by pipx are runnable by the user -pipx ensurepath - -# Run it -sideband -``` - -Or, if you prefer to use `pip` directly, follow the instructions below. In this case, if you have not already installed Python and `pip3` on your macOS system, [download and install](https://www.python.org/downloads/) the latest version first. +To install Sideband via `pip`, follow these instructions: ```bash # Install Sideband and dependencies on macOS using pip: pip3 install sbapp --user --break-system-packages -# Run it: +# Optionally install RNS command line utilities: +pip3 install rns + +# Run Sideband from the terminal: python3 -m sbapp.main +# Enable debug logging: +python3 -m sbapp.main -v + +# Start Sideband in daemon mode: +python3 -m sbapp.main -d + # If you add your pip install location to # the PATH environment variable, you can # also run Sideband simply using: @@ -190,11 +192,13 @@ sideband ## On Windows -Even though there is currently not an automated installer, or packaged `.exe` file for Sideband on Windows, you can still install it through `pip`. If you don't already have Python installed, [download and install](https://www.python.org/downloads/) the latest version of Python. +Even though there is currently not an automated installer, or packaged `.exe` file for Sideband on Windows, you can install Sideband with the `pip` package manager. -Please note that audio messaging functionality isn't supported on Windows yet. Please support the development if you'd like to see this feature added faster. +If you don't already have Python installed, [download and install the latest supported version of Python](https://www.python.org/downloads/release/python-3127/) (currently Python 3.12.7). -**Important!** When asked by the installer, make sure to add the Python program to your PATH environment variables. If you don't do this, you will not be able to use the `pip` installer, or run the `sideband` command. +**Please note!** The very latest Python release, Python 3.13 is currently **not** compatible with the Kivy framework, that Sideband uses to render its user interface. If you are running Python 3.13, you will need to install an earlier version as well. Using the latest release of Python 3.12 is recommended. + +**Important!** When asked by the installer, make sure to add the Python program to your PATH environment variables. If you don't do this, you will not be able to use the `pip` installer, run the `sideband` command, or create a desktop shortcut for Sideband. When Python has been installed, you can open a command prompt and install sideband via `pip`: From 2e5d557aa798537ae11fe9acc9f4f5aba054402a Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Wed, 27 Nov 2024 13:13:50 +0100 Subject: [PATCH 17/30] Updated freeze file --- sbapp/freeze.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/sbapp/freeze.py b/sbapp/freeze.py index 81f5ad8..8a7c498 100644 --- a/sbapp/freeze.py +++ b/sbapp/freeze.py @@ -30,10 +30,13 @@ def get_variant() -> str: version = re.findall(version_regex, version_file_data, re.M)[0] return version except IndexError: - raise ValueError(f"Unable to find version string in {version_file}.") + return None __version__ = get_version() __variant__ = get_variant() +variant_str = "" +if __variant__: + variant_str = " "+__variant__ def glob_paths(pattern): out_files = [] @@ -60,14 +63,14 @@ package_data = { ] } -print("Freezing Sideband "+__version__+" "+__variant__) +print("Freezing Sideband "+__version__+" "+variant_str) if build_appimage: global_excludes = [".buildozer", "build", "dist"] # Dependencies are automatically detected, but they might need fine-tuning. appimage_options = { "target_name": "Sideband", - "target_version": __version__+" "+__variant__, + "target_version": __version__+" "+variant_str, "include_files": [], "excludes": [], "packages": ["kivy"], From ee18dcab315fa32693dad4eb6b5d217e29841ee3 Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Wed, 27 Nov 2024 13:14:46 +0100 Subject: [PATCH 18/30] Preparation for Windows build --- main.py | 1 + sbapp/main.py | 3 ++ sbapp/sideband/core.py | 8 +++-- sideband.spec | 67 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 76 insertions(+), 3 deletions(-) create mode 100644 main.py create mode 100644 sideband.spec diff --git a/main.py b/main.py new file mode 100644 index 0000000..8518858 --- /dev/null +++ b/main.py @@ -0,0 +1 @@ +import sbapp.main \ No newline at end of file diff --git a/sbapp/main.py b/sbapp/main.py index a8446c2..aa7ca6c 100644 --- a/sbapp/main.py +++ b/sbapp/main.py @@ -6118,3 +6118,6 @@ def run(): if __name__ == "__main__": run() + +if __name__ == "sbapp.main": + run() \ No newline at end of file diff --git a/sbapp/sideband/core.py b/sbapp/sideband/core.py index 137dc8d..48ce9ee 100644 --- a/sbapp/sideband/core.py +++ b/sbapp/sideband/core.py @@ -178,19 +178,21 @@ class SidebandCore(): self.cache_dir = self.app_dir+"/cache" self.rns_configdir = None + + core_path = os.path.abspath(__file__) + if "core.pyc" in core_path: + core_path = core_path.replace("core.pyc", "core.py") + if RNS.vendor.platformutils.is_android(): self.app_dir = android_app_dir+"/io.unsigned.sideband/files/" self.cache_dir = self.app_dir+"/cache" self.rns_configdir = self.app_dir+"/app_storage/reticulum" self.asset_dir = self.app_dir+"/app/assets" elif RNS.vendor.platformutils.is_darwin(): - core_path = os.path.abspath(__file__) self.asset_dir = core_path.replace("/sideband/core.py", "/assets") elif RNS.vendor.platformutils.get_platform() == "linux": - core_path = os.path.abspath(__file__) self.asset_dir = core_path.replace("/sideband/core.py", "/assets") elif RNS.vendor.platformutils.is_windows(): - core_path = os.path.abspath(__file__) self.asset_dir = core_path.replace("\\sideband\\core.py", "\\assets") else: self.asset_dir = plyer.storagepath.get_application_dir()+"/sbapp/assets" diff --git a/sideband.spec b/sideband.spec new file mode 100644 index 0000000..eb01ad4 --- /dev/null +++ b/sideband.spec @@ -0,0 +1,67 @@ +# -*- mode: python ; coding: utf-8 -*- + +from kivy_deps import sdl2, glew + +a = Analysis( + ['main.py'], + pathex=[], + binaries=[], + datas=[], + hiddenimports=[], + hookspath=[], + hooksconfig={}, + runtime_hooks=[], + excludes=[], + noarchive=False, + optimize=0, +) +pyz = PYZ(a.pure) + +def extra_datas(mydir): + def rec_glob(p, files): + import os + import glob + for d in glob.glob(p): + if os.path.isfile(d): + files.append(d) + rec_glob("%s/*" % d, files) + files = [] + rec_glob("%s/*" % mydir, files) + extra_datas = [] + for f in files: + extra_datas.append((f, f, 'DATA')) + + return extra_datas + +a.datas += extra_datas('sbapp') +a.datas += extra_datas('RNS') +a.datas += extra_datas('LXMF') + +exe = EXE( + pyz, + a.scripts, + [], + exclude_binaries=True, + name='main', + debug=False, + bootloader_ignore_signals=False, + strip=False, + upx=True, + console=True, + disable_windowed_traceback=False, + argv_emulation=False, + target_arch=None, + codesign_identity=None, + entitlements_file=None, +) + +coll = COLLECT( + exe, + a.binaries, + a.datas, + *[Tree(p) for p in (sdl2.dep_bins + glew.dep_bins)], + strip=False, + upx=True, + upx_exclude=[], + name='main', +) From 3979a806b0036884b8828e9792809ccba7a3c47a Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Wed, 27 Nov 2024 14:07:48 +0100 Subject: [PATCH 19/30] Enable windows build --- Makefile | 3 +++ sideband.spec | 5 +++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 5b8cd31..6f5ed59 100644 --- a/Makefile +++ b/Makefile @@ -31,6 +31,9 @@ preparewheel: build_wheel: python3 setup.py sdist bdist_wheel +build_win_exe: + python -m PyInstaller sideband.spec --noconfirm + release: build_wheel apk fetchapk upload: diff --git a/sideband.spec b/sideband.spec index eb01ad4..f0395be 100644 --- a/sideband.spec +++ b/sideband.spec @@ -42,12 +42,13 @@ exe = EXE( a.scripts, [], exclude_binaries=True, - name='main', + name='Sideband', + icon="sbapp\\assets\\icon.png", debug=False, bootloader_ignore_signals=False, strip=False, upx=True, - console=True, + console=False, disable_windowed_traceback=False, argv_emulation=False, target_arch=None, From 63a96dea3774064b05139cb8787042b0a080a8e6 Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Thu, 28 Nov 2024 16:57:00 +0100 Subject: [PATCH 20/30] Fixed desktop entry icon reference for RasPi OS --- sbapp/assets/io.unsigned.sideband.desktop | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sbapp/assets/io.unsigned.sideband.desktop b/sbapp/assets/io.unsigned.sideband.desktop index 6a7d437..c2d8223 100644 --- a/sbapp/assets/io.unsigned.sideband.desktop +++ b/sbapp/assets/io.unsigned.sideband.desktop @@ -1,4 +1,4 @@ -# Entry version 20240630 +# Entry version 20241128 [Desktop Entry] Comment[en_US]=Messaging, telemetry and remote control over LXMF Comment=Messaging, telemetry and remote control over LXMF @@ -6,7 +6,7 @@ Encoding=UTF-8 Exec=sideband GenericName[en_US]=LXMF client GenericName=LXMF client -Icon=io.unsigned.sideband.png +Icon=io.unsigned.sideband Categories=Utility MimeType= Name[en_US]=Sideband From 970ec7b3b35efe70a4fcf9dfd621299f516cf2f1 Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Fri, 29 Nov 2024 13:50:26 +0100 Subject: [PATCH 21/30] Updated Windows install instructions --- README.md | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 4c0715a..c6beb7b 100644 --- a/README.md +++ b/README.md @@ -192,23 +192,35 @@ sideband ## On Windows -Even though there is currently not an automated installer, or packaged `.exe` file for Sideband on Windows, you can install Sideband with the `pip` package manager. +To install Sideband on Windows, you have two options available: An easy to install pre-built executable package, or a source package install for more advanced setups. -If you don't already have Python installed, [download and install the latest supported version of Python](https://www.python.org/downloads/release/python-3127/) (currently Python 3.12.7). +#### Prebuilt Executable -**Please note!** The very latest Python release, Python 3.13 is currently **not** compatible with the Kivy framework, that Sideband uses to render its user interface. If you are running Python 3.13, you will need to install an earlier version as well. Using the latest release of Python 3.12 is recommended. +Simply download the packaged Windows ZIP file from the [latest release page](https://github.com/markqvist/Sideband/releases/latest), unzip the file, and run `Sideband.exe` from the unzipped directory. You can create desktop or start menu shortcuts from this executable if needed. -**Important!** When asked by the installer, make sure to add the Python program to your PATH environment variables. If you don't do this, you will not be able to use the `pip` installer, run the `sideband` command, or create a desktop shortcut for Sideband. +When running Sideband for the first time, a default Reticulum configuration file will be created, if you don't already have one. If you don't have any existing Reticulum connectivity available locally, you may want to edit the file, located at `C:\Users\USERNAME\.reticulum\config` and manually add an interface that provides connectivity to a wider network. If you just want to connect over the Internet, you can add one of the public hubs on the [Reticulum Testnet](https://reticulum.network/connect.html). -When Python has been installed, you can open a command prompt and install sideband via `pip`: +Though the ZIP file contains everything necessary to run Sideband, it is also recommended to install the Reticulum command line utilities separately, so that you can use commands like `rnstatus` and `rnsd` from the command line. This will make it easier to manage Reticulum connectivity on your system. If you do not already have Python installed on your system, [download and install it](https://www.python.org/downloads/) first. + +**Important!** When asked by the installer, make sure to add the Python program to your `PATH` environment variables. If you don't do this, you will not be able to use the `pip` installer, or run any of the installed commands. When Python has been installed, you can open a command prompt and install the Reticulum package via `pip`: + +```bash +pip install rns +``` + +#### Source Package Install + +For more advanced setups, including the ability to run Sideband in headless daemon mode, enable debug logging output, configuration import and export and more, you may want to install it from the source package via `pip` instead. + +In this case, you will need to [download and install the latest supported version of Python](https://www.python.org/downloads/release/python-3127/) (currently Python 3.12.7), since very latest Python release, Python 3.13 is currently **not** compatible with the Kivy framework, that Sideband uses to render its user interface. The binary package already includes a compatible Python version, so if you are running Sideband from that, there is no need to install a specific version. + +When Python has been installed, you can open a command prompt and install Sideband via `pip`: ```bash pip install sbapp ``` -The Sideband application can now be launched by running the command `sideband` in the command prompt. If needed, you can create a shortcut for Sideband on your desktop or in the start menu. - -When running Sideband for the first time, a default Reticulum configuration file will be created, if you don't already have one. If you don't have any existing Reticulum connectivity available locally, you may want to edit the file, located at `C:\Users\USERNAME\.reticulum\config` and manually add an interface that provides connectivity to a wider network. If you just want to connect over the Internet, you can add one of the public hubs on the [Reticulum Testnet](https://reticulum.network/connect.html). +The Sideband application can now be launched by running the command `sideband` in the command prompt. If needed, you can create a shortcut for Sideband on your desktop or in the start menu. Since this installation method automatically installs the `rns` and `lxmf` packages as well, you will also have access to using all the included RNS and LXMF utilities like `rnstatus`, `rnsd` and `lxmfd` on your system. # Paper Messaging Example From be8051240f177f3dc55cc1a467c4e5dedfeaaca6 Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Fri, 29 Nov 2024 13:53:47 +0100 Subject: [PATCH 22/30] Updated Windows install instructions --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index c6beb7b..15985c5 100644 --- a/README.md +++ b/README.md @@ -212,7 +212,7 @@ pip install rns For more advanced setups, including the ability to run Sideband in headless daemon mode, enable debug logging output, configuration import and export and more, you may want to install it from the source package via `pip` instead. -In this case, you will need to [download and install the latest supported version of Python](https://www.python.org/downloads/release/python-3127/) (currently Python 3.12.7), since very latest Python release, Python 3.13 is currently **not** compatible with the Kivy framework, that Sideband uses to render its user interface. The binary package already includes a compatible Python version, so if you are running Sideband from that, there is no need to install a specific version. +In this case, you will need to [download and install the latest supported version of Python](https://www.python.org/downloads/release/python-3127/) (currently Python 3.12.7), since very latest Python release, Python 3.13 is currently **not** compatible with the Kivy framework, that Sideband uses to render its user interface. The binary package already includes a compatible Python version, so if you are running Sideband from that, there is no need to install a specific version of Python. When Python has been installed, you can open a command prompt and install Sideband via `pip`: @@ -220,7 +220,9 @@ When Python has been installed, you can open a command prompt and install Sideba pip install sbapp ``` -The Sideband application can now be launched by running the command `sideband` in the command prompt. If needed, you can create a shortcut for Sideband on your desktop or in the start menu. Since this installation method automatically installs the `rns` and `lxmf` packages as well, you will also have access to using all the included RNS and LXMF utilities like `rnstatus`, `rnsd` and `lxmfd` on your system. +The Sideband application can now be launched by running the command `sideband` in the command prompt. If needed, you can create a shortcut for Sideband on your desktop or in the start menu. + +Since this installation method automatically installs the `rns` and `lxmf` packages as well, you will also have access to using all the included RNS and LXMF utilities like `rnstatus`, `rnsd` and `lxmd` on your system. # Paper Messaging Example From 7f54cbfb1765bc4179b23d6e776f1823ac48473d Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Fri, 29 Nov 2024 13:55:45 +0100 Subject: [PATCH 23/30] Updated Windows install instructions --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 15985c5..88c1b7a 100644 --- a/README.md +++ b/README.md @@ -192,7 +192,10 @@ sideband ## On Windows -To install Sideband on Windows, you have two options available: An easy to install pre-built executable package, or a source package install for more advanced setups. +To install Sideband on Windows, you have two options available: + +1. An easy to install pre-built executable package +2. A source package install for more advanced setups. #### Prebuilt Executable From 5d64fe1f8d3f4f59c572990148ca4e6de5478052 Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Fri, 29 Nov 2024 14:02:08 +0100 Subject: [PATCH 24/30] Updated macOS install instructions --- README.md | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 88c1b7a..de5ae4c 100644 --- a/README.md +++ b/README.md @@ -161,7 +161,22 @@ sideband ## On macOS -You can download a DMG with Sideband for macOS (ARM and Intel) from the [latest release page](https://github.com/markqvist/Sideband/releases/latest). If you install Sideband from the DMG file, it is still recommended to install the `rns` package via the `pip` or `pipx` package manager, so you can use the RNS utility programs, like `rnstatus` to see interface and connectivity status from the terminal. +To install Sideband on macOS, you have two options available: + +1. An easy to install pre-built disk image package +2. A source package install for more advanced setups. + +#### Prebuilt Executable + +You can download a disk image with Sideband for macOS (ARM and Intel) from the [latest release page](https://github.com/markqvist/Sideband/releases/latest). Simply mount the downloaded disk image, drag `Sideband` to your applications folder, and run it. + +**Please note!** If you have application install restrictions enabled on your macOS install, or have restricted your system to only allow installation of application from the Apple App Store, you will need to create an exception for Sideband. The Sideband application will *never* be distributed with an Apple-controlled digital signature, as this will allow Apple to simply disable Sideband from running on your system if they decide to do so, or are forced to by authorities or other circumstances. + +If you install Sideband from the DMG file, it is still recommended to install the `rns` package via the `pip` or `pipx` package manager, so you can use the RNS utility programs, like `rnstatus` to see interface and connectivity status from the terminal. + +#### Source Package Install + +For more advanced setups, including the ability to run Sideband in headless daemon mode, enable debug logging output, configuration import and export and more, you may want to install it from the source package via `pip` instead. **Please note!** The very latest Python release, Python 3.13 is currently **not** compatible with the Kivy framework, that Sideband uses to render its user interface. If your version of macOS uses Python 3.13 as its default Python installation, you will need to install an earlier version as well. Using [the latest release of Python 3.12](https://www.python.org/downloads/release/python-3127/) is recommended. From dcfc76e459173bd1943e52566be9e0579f0a1068 Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Fri, 29 Nov 2024 14:02:32 +0100 Subject: [PATCH 25/30] Updated install instructions --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index de5ae4c..8eeb808 100644 --- a/README.md +++ b/README.md @@ -164,7 +164,7 @@ sideband To install Sideband on macOS, you have two options available: 1. An easy to install pre-built disk image package -2. A source package install for more advanced setups. +2. A source package install for more advanced setups #### Prebuilt Executable @@ -210,7 +210,7 @@ sideband To install Sideband on Windows, you have two options available: 1. An easy to install pre-built executable package -2. A source package install for more advanced setups. +2. A source package install for more advanced setups #### Prebuilt Executable From 25e31d8d9d95bf4fd5ec08ccbaaf41c9cc60f9bc Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Fri, 29 Nov 2024 14:15:43 +0100 Subject: [PATCH 26/30] Updated install instructions --- README.md | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 8eeb808..6f2f51e 100644 --- a/README.md +++ b/README.md @@ -172,7 +172,18 @@ You can download a disk image with Sideband for macOS (ARM and Intel) from the [ **Please note!** If you have application install restrictions enabled on your macOS install, or have restricted your system to only allow installation of application from the Apple App Store, you will need to create an exception for Sideband. The Sideband application will *never* be distributed with an Apple-controlled digital signature, as this will allow Apple to simply disable Sideband from running on your system if they decide to do so, or are forced to by authorities or other circumstances. -If you install Sideband from the DMG file, it is still recommended to install the `rns` package via the `pip` or `pipx` package manager, so you can use the RNS utility programs, like `rnstatus` to see interface and connectivity status from the terminal. +If you install Sideband from the DMG file, it is still recommended to install the `rns` package via the `pip` or `pipx` package manager, so you can use the RNS utility programs, like `rnstatus` to see interface and connectivity status from the terminal. If you already have Python and `pip` installed on your system, simply open a terminal window and use one of the following commands: + +```bash +# Install Reticulum and utilities with pip: +pip3 install rns + +# On some versions, you may need to use the +# flag --break-system-packages to install: +pip3 install rns --break-system-packages +``` + +If you do not have Python and `pip` available, [download and install it](https://www.python.org/downloads/) first. #### Source Package Install From ba0c80dd8001d1c3acae32adadd9b9f234b4d1a1 Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Sat, 30 Nov 2024 01:08:10 +0100 Subject: [PATCH 27/30] Updated readme --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 6f2f51e..77d4cae 100644 --- a/README.md +++ b/README.md @@ -288,7 +288,7 @@ You can help support the continued development of open, free and private communi
-# Development Roadmap +# Planned Features - Secure and private location and telemetry sharing - Including images in messages @@ -298,15 +298,15 @@ You can help support the continued development of open, free and private communi - Using Sideband as a Reticulum Transport Instance - Encryption keys export and import - Plugin support for commands, services and telemetry -- Adding Linux .desktop file integration - Sending voice messages (using Codec2 and Opus) -- Implementing the Local Broadcasts feature +- Adding a Linux desktop integration +- Adding prebuilt Windows binaries to the releases +- Adding prebuilt macOS binaries to the releases +- Adding a Nomad Net page browser - LXMF sneakernet functionality - Network visualisation and test tools -- A debug log viewer - Better message sorting mechanism -- Fix I2P status not being displayed correctly when the I2P router disappears unexpectedly -- Adding a Nomad Net page browser +- A debug log viewer # License Unless otherwise noted, this work is licensed under a [Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License][cc-by-nc-sa]. From 211a0ad16bf0c8731d54c91b29e43cb55805ca96 Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Sat, 7 Dec 2024 21:28:51 +0100 Subject: [PATCH 28/30] Added utilities section --- sbapp/main.py | 57 +++++++++++- sbapp/ui/layouts.py | 18 ++++ sbapp/ui/utilities.py | 205 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 277 insertions(+), 3 deletions(-) create mode 100644 sbapp/ui/utilities.py diff --git a/sbapp/main.py b/sbapp/main.py index aa7ca6c..3ef5efe 100644 --- a/sbapp/main.py +++ b/sbapp/main.py @@ -229,6 +229,7 @@ else: from ui.layouts import * from ui.conversations import Conversations, MsgSync, NewConv from ui.telemetry import Telemetry + from ui.utilities import Utilities from ui.objectdetails import ObjectDetails from ui.announces import Announces from ui.messages import Messages, ts_format, messages_screen_kv @@ -256,6 +257,7 @@ else: from .ui.conversations import Conversations, MsgSync, NewConv from .ui.announces import Announces from .ui.telemetry import Telemetry + from .ui.utilities import Utilities from .ui.objectdetails import ObjectDetails from .ui.messages import Messages, ts_format, messages_screen_kv from .ui.helpers import ContentNavigationDrawer, DrawerList, IconListItem @@ -342,6 +344,7 @@ class SidebandApp(MDApp): self.sync_dialog = None self.settings_ready = False self.telemetry_ready = False + self.utilities_ready = False self.connectivity_ready = False self.hardware_ready = False self.repository_ready = False @@ -1297,6 +1300,8 @@ class SidebandApp(MDApp): self.close_sub_telemetry_action() elif self.root.ids.screen_manager.current == "icons_screen": self.close_sub_telemetry_action() + elif self.root.ids.screen_manager.current == "utilities_screen": + self.close_sub_utilities_action() else: self.open_conversations(direction="right") @@ -1336,9 +1341,12 @@ class SidebandApp(MDApp): else: self.telemetry_action(self) - if text == "u": + if text == "y": self.map_display_own_telemetry() + if text == "u": + self.utilities_action() + if text == "o": self.objects_action() @@ -1350,6 +1358,8 @@ class SidebandApp(MDApp): self.lxmf_sync_action(self) elif self.root.ids.screen_manager.current == "telemetry_screen": self.conversations_action(self, direction="right") + elif self.root.ids.screen_manager.current == "rnstatus_screen": + self.utilities_screen.update_rnstatus() elif self.root.ids.screen_manager.current == "object_details_screen": if not self.object_details_screen.object_hash == self.sideband.lxmf_destination.hash: self.converse_from_telemetry(self) @@ -1394,6 +1404,8 @@ class SidebandApp(MDApp): self.close_sub_telemetry_action() elif self.root.ids.screen_manager.current == "icons_screen": self.close_sub_telemetry_action() + elif self.root.ids.screen_manager.current == "rnstatus_screen": + self.close_sub_utilities_action() else: self.open_conversations(direction="right") @@ -5040,6 +5052,44 @@ class SidebandApp(MDApp): ate_dialog.open() + ### Utilities Screen + ###################################### + + def utilities_init(self): + if not self.utilities_ready: + self.utilities_screen = Utilities(self) + self.utilities_ready = True + + def utilities_open(self, sender=None, direction="left", no_transition=False): + if no_transition: + self.root.ids.screen_manager.transition = self.no_transition + else: + self.root.ids.screen_manager.transition = self.slide_transition + self.root.ids.screen_manager.transition.direction = direction + + self.root.ids.screen_manager.current = "utilities_screen" + self.root.ids.nav_drawer.set_state("closed") + self.sideband.setstate("app.displaying", self.root.ids.screen_manager.current) + + if no_transition: + self.root.ids.screen_manager.transition = self.slide_transition + + def utilities_action(self, sender=None, direction="left"): + if self.utilities_ready: + self.utilities_open(direction=direction) + else: + self.loader_action(direction=direction) + def final(dt): + self.utilities_init() + def o(dt): + self.utilities_open(no_transition=True) + Clock.schedule_once(o, ll_ot) + Clock.schedule_once(final, ll_ft) + + def close_sub_utilities_action(self, sender=None): + self.utilities_action(direction="right") + + ### Telemetry Screen ###################################### @@ -5945,7 +5995,7 @@ If you use Reticulum and LXMF on hardware that does not carry any identifiers ti [b]Quick Actions[/b] - [b]Ctrl-W[/b] Go back - - [b]Ctrl+Q[/b] Shut down Sideband + - [b]Ctrl-Q[/b] Shut down Sideband - [b]Ctrl-R[/b] Start LXMF sync (from Conversations screen) - [b]Ctrl-N[/b] Create new conversation @@ -5968,9 +6018,10 @@ If you use Reticulum and LXMF on hardware that does not carry any identifiers ti - [b]Ctrl-O[/b] Go to Objects & Devices - [b]Ctrl-L[/b] Go to Announce Stream - [b]Ctrl-M[/b] Go to Situation Map + - [b]Ctrl-U[/b] Go to Utilities - [b]Ctrl-T[/b] Go to Telemetry configuration - [b]Ctrl-G[/b] Go to Guide - - [b]Ctrl-U[/b] Display own telemetry + - [b]Ctrl-Y[/b] Display own telemetry [b]Map Controls[/b] - [b]Up[/b], [b]down[/b], [b]left[/b], [b]right[/b] Navigate diff --git a/sbapp/ui/layouts.py b/sbapp/ui/layouts.py index fe9f2ab..a4aefc0 100644 --- a/sbapp/ui/layouts.py +++ b/sbapp/ui/layouts.py @@ -80,6 +80,15 @@ MDNavigationLayout: on_release: root.ids.screen_manager.app.map_action(self) + OneLineIconListItem: + text: "Overview" + on_release: root.ids.screen_manager.app.overview_action(self) + + IconLeftWidget: + icon: "view-dashboard-outline" + on_release: root.ids.screen_manager.app.overview_action(self) + + OneLineIconListItem: text: "Announce Stream" on_release: root.ids.screen_manager.app.announces_action(self) @@ -107,6 +116,15 @@ MDNavigationLayout: on_release: root.ids.screen_manager.app.telemetry_action(self) + OneLineIconListItem: + text: "Utilities" + on_release: root.ids.screen_manager.app.utilities_action(self) + + IconLeftWidget: + icon: "tools" + on_release: root.ids.screen_manager.app.utilities_action(self) + + OneLineIconListItem: text: "Preferences" on_release: root.ids.screen_manager.app.settings_action(self) diff --git a/sbapp/ui/utilities.py b/sbapp/ui/utilities.py new file mode 100644 index 0000000..c192ed6 --- /dev/null +++ b/sbapp/ui/utilities.py @@ -0,0 +1,205 @@ +import time +import RNS + +from typing import Union +from kivy.metrics import dp,sp +from kivy.lang.builder import Builder +from kivy.core.clipboard import Clipboard +from kivy.utils import escape_markup +from kivymd.uix.recycleview import MDRecycleView +from kivymd.uix.list import OneLineIconListItem +from kivymd.uix.pickers import MDColorPicker +from kivymd.icon_definitions import md_icons +from kivy.properties import StringProperty, BooleanProperty +from kivy.effects.scroll import ScrollEffect +from kivy.clock import Clock +from sideband.sense import Telemeter +import threading +from datetime import datetime + +if RNS.vendor.platformutils.get_platform() == "android": + from ui.helpers import ts_format + from android.permissions import request_permissions, check_permission +else: + from .helpers import ts_format + +class Utilities(): + def __init__(self, app): + self.app = app + self.screen = None + self.rnstatus_screen = None + self.rnstatus_instance = None + + if not self.app.root.ids.screen_manager.has_screen("utilities_screen"): + self.screen = Builder.load_string(layout_utilities_screen) + self.screen.app = self.app + self.screen.delegate = self + self.app.root.ids.screen_manager.add_widget(self.screen) + + self.screen.ids.telemetry_scrollview.effect_cls = ScrollEffect + info = "\nYou can use various RNS utilities from Sideband. " + info += "" + + if self.app.theme_cls.theme_style == "Dark": + info = "[color=#"+self.app.dark_theme_text_color+"]"+info+"[/color]" + + self.screen.ids.telemetry_info.text = info + + + ### rnstatus screen + ###################################### + + def rnstatus_action(self, sender=None): + if not self.app.root.ids.screen_manager.has_screen("rnstatus_screen"): + self.rnstatus_screen = Builder.load_string(layout_rnstatus_screen) + self.rnstatus_screen.app = self.app + self.rnstatus_screen.delegate = self + self.app.root.ids.screen_manager.add_widget(self.rnstatus_screen) + + self.app.root.ids.screen_manager.transition.direction = "left" + self.app.root.ids.screen_manager.current = "rnstatus_screen" + self.app.sideband.setstate("app.displaying", self.app.root.ids.screen_manager.current) + + self.update_rnstatus() + + def update_rnstatus(self, sender=None): + threading.Thread(target=self.update_rnstatus_job, daemon=True).start() + + def update_rnstatus_job(self, sender=None): + if self.rnstatus_instance == None: + import RNS.Utilities.rnstatus as rnstatus + self.rnstatus_instance = rnstatus + + import io + from contextlib import redirect_stdout + output_marker = "===begin rnstatus output===" + output = "None" + with io.StringIO() as buffer, redirect_stdout(buffer): + print(output_marker, end="") + self.rnstatus_instance.main(rns_instance=RNS.Reticulum.get_instance()) + output = buffer.getvalue() + + remainder = output[:output.find(output_marker)] + output = output[output.find(output_marker)+len(output_marker):] + print(remainder, end="") + + def cb(dt): + self.rnstatus_screen.ids.rnstatus_output.text = f"[font=RobotoMono-Regular]{output}[/font]" + Clock.schedule_once(cb, 0.2) + + if self.app.root.ids.screen_manager.current == "rnstatus_screen": + Clock.schedule_once(self.update_rnstatus, 1) + + +layout_utilities_screen = """ +MDScreen: + name: "utilities_screen" + + BoxLayout: + orientation: "vertical" + + MDTopAppBar: + title: "Utilities" + anchor_title: "left" + elevation: 0 + left_action_items: + [['menu', lambda x: root.app.nav_drawer.set_state("open")]] + right_action_items: + [ + ['close', lambda x: root.app.close_any_action(self)], + ] + + ScrollView: + id: telemetry_scrollview + + MDBoxLayout: + orientation: "vertical" + size_hint_y: None + height: self.minimum_height + padding: [dp(28), dp(48), dp(28), dp(16)] + + MDLabel: + text: "Utilities & Tools" + font_style: "H6" + + MDLabel: + id: telemetry_info + markup: True + text: "" + size_hint_y: None + text_size: self.width, None + height: self.texture_size[1] + + MDBoxLayout: + orientation: "vertical" + spacing: "24dp" + size_hint_y: None + height: self.minimum_height + padding: [dp(0), dp(35), dp(0), dp(35)] + + MDRectangleFlatIconButton: + id: rnstatus_button + icon: "wifi-check" + text: "Reticulum Status" + padding: [dp(0), dp(14), dp(0), dp(14)] + icon_size: dp(24) + font_size: dp(16) + size_hint: [1.0, None] + on_release: root.delegate.rnstatus_action(self) + disabled: False + + MDRectangleFlatIconButton: + id: logview_button + icon: "list-box-outline" + text: "Log Viewer" + padding: [dp(0), dp(14), dp(0), dp(14)] + icon_size: dp(24) + font_size: dp(16) + size_hint: [1.0, None] + on_release: root.delegate.rnstatus_action(self) + disabled: True + +""" + +layout_rnstatus_screen = """ +MDScreen: + name: "rnstatus_screen" + + BoxLayout: + orientation: "vertical" + + MDTopAppBar: + id: top_bar + title: "Reticulum Status" + anchor_title: "left" + elevation: 0 + left_action_items: + [['menu', lambda x: root.app.nav_drawer.set_state("open")]] + right_action_items: + [ + ['refresh', lambda x: root.delegate.update_rnstatus()], + ['close', lambda x: root.app.close_sub_utilities_action(self)], + ] + + MDScrollView: + id: sensors_scrollview + size_hint_x: 1 + size_hint_y: None + size: [root.width, root.height-root.ids.top_bar.height] + do_scroll_x: False + do_scroll_y: True + + MDGridLayout: + cols: 1 + padding: [dp(28), dp(14), dp(28), dp(28)] + size_hint_y: None + height: self.minimum_height + + MDLabel: + id: rnstatus_output + markup: True + text: "" + size_hint_y: None + text_size: self.width, None + height: self.texture_size[1] +""" \ No newline at end of file From 83b8130311fe89f35c30e1f222bc42318bd7ddde Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Sat, 7 Dec 2024 21:28:59 +0100 Subject: [PATCH 29/30] Updated dependencies --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 1b330e6..4e2c5b7 100644 --- a/setup.py +++ b/setup.py @@ -99,7 +99,7 @@ setuptools.setup( ] }, install_requires=[ - "rns>=0.8.6", + "rns>=0.8.7", "lxmf>=0.5.8", "kivy>=2.3.0", "pillow>=10.2.0", From 922107df50b0dcaefabe457a501ffcc8c8bee797 Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Sat, 7 Dec 2024 21:29:49 +0100 Subject: [PATCH 30/30] Updated version --- sbapp/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sbapp/main.py b/sbapp/main.py index 3ef5efe..5b8c918 100644 --- a/sbapp/main.py +++ b/sbapp/main.py @@ -1,6 +1,6 @@ __debug_build__ = False __disable_shaders__ = False -__version__ = "1.1.4" +__version__ = "1.2.0" __variant__ = "" import sys