From f299e0c0edee9382499522804b8718656bc75396 Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Sat, 21 Sep 2024 19:08:11 +0200 Subject: [PATCH 01/37] Ask for power optimisation disabling on Android --- sbapp/buildozer.spec | 2 +- sbapp/main.py | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/sbapp/buildozer.spec b/sbapp/buildozer.spec index 13a7cdf..35b1d19 100644 --- a/sbapp/buildozer.spec +++ b/sbapp/buildozer.spec @@ -29,7 +29,7 @@ android.presplash_color = #00000000 orientation = portrait fullscreen = 0 -android.permissions = INTERNET,POST_NOTIFICATIONS,WAKE_LOCK,FOREGROUND_SERVICE,CHANGE_WIFI_MULTICAST_STATE,BLUETOOTH_CONNECT,ACCESS_NETWORK_STATE,ACCESS_FINE_LOCATION,ACCESS_COARSE_LOCATION,MANAGE_EXTERNAL_STORAGE,ACCESS_BACKGROUND_LOCATION,RECORD_AUDIO +android.permissions = INTERNET,POST_NOTIFICATIONS,WAKE_LOCK,FOREGROUND_SERVICE,CHANGE_WIFI_MULTICAST_STATE,BLUETOOTH_CONNECT,ACCESS_NETWORK_STATE,ACCESS_FINE_LOCATION,ACCESS_COARSE_LOCATION,MANAGE_EXTERNAL_STORAGE,ACCESS_BACKGROUND_LOCATION,RECORD_AUDIO,REQUEST_IGNORE_BATTERY_OPTIMIZATIONS android.api = 31 android.minapi = 24 diff --git a/sbapp/main.py b/sbapp/main.py index 456d15b..027ca8d 100644 --- a/sbapp/main.py +++ b/sbapp/main.py @@ -406,6 +406,20 @@ class SidebandApp(MDApp): else: self.open_conversations() + if RNS.vendor.platformutils.is_android(): + if self.sideband.getstate("android.power_restricted", allow_cache=False): + RNS.log("Android power restrictions detected, background connectivity will not work. Asking for permissions.", RNS.LOG_DEBUG) + def pm_job(dt): + Settings = autoclass("android.provider.Settings") + Intent = autoclass("android.content.Intent") + Uri = autoclass("android.net.Uri") + + requestIntent = Intent() + requestIntent.setAction(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS) + requestIntent.setData(Uri.parse("package:io.unsigned.sideband")) + mActivity.startActivity(requestIntent) + Clock.schedule_once(pm_job, 1.5) + if not self.root.ids.screen_manager.has_screen("messages_screen"): self.messages_screen = Builder.load_string(messages_screen_kv) self.messages_screen.app = self From 92ccbbec0a3e331578faeb30dd9664d8cd123d9d Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Sat, 21 Sep 2024 20:53:13 +0200 Subject: [PATCH 02/37] Fixed invalid log output --- sbapp/sideband/core.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sbapp/sideband/core.py b/sbapp/sideband/core.py index dd5f0c6..be35294 100644 --- a/sbapp/sideband/core.py +++ b/sbapp/sideband/core.py @@ -1478,7 +1478,7 @@ class SidebandCore(): RNS.log("Service heartbeat did not recover after retry", RNS.LOG_DEBUG) return False else: - RNS.log("Service heartbeat recovered at"+str(time), RNS.LOG_DEBUG) + RNS.log("Service heartbeat recovered at"+str(now), RNS.LOG_DEBUG) return True else: return True @@ -4311,6 +4311,7 @@ class SidebandCore(): self.notify(title=self.peer_display_name(context_dest), content=notification_content, group="LXM", context_id=RNS.hexrep(context_dest, delimit=False)) except Exception as e: RNS.log("Could not post notification for received message: "+str(e), RNS.LOG_ERROR) + RNS.trace_exception(e) def ptt_playback(self, message): ptt_timeout = 60 From 7e6f95e96579d4b1b10f608483b76f9d13b30292 Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Sat, 21 Sep 2024 20:56:14 +0200 Subject: [PATCH 03/37] Detect Android background power restrictions --- sbapp/services/sidebandservice.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/sbapp/services/sidebandservice.py b/sbapp/services/sidebandservice.py index de4ecff..adc115b 100644 --- a/sbapp/services/sidebandservice.py +++ b/sbapp/services/sidebandservice.py @@ -42,6 +42,7 @@ if RNS.vendor.platformutils.get_platform() == "android": if android_api_version >= 26: NotificationBuilder = autoclass('android.app.Notification$Builder') NotificationChannel = autoclass('android.app.NotificationChannel') + RingtoneManager = autoclass('android.media.RingtoneManager') from usb4a import usb from usbserial4a import serial4a @@ -149,6 +150,18 @@ class SidebandService(): return True + def update_power_restrictions(self): + package_name = "io.unsigned.sideband" + if RNS.vendor.platformutils.is_android(): + if android_api_version >= 28: + if self.power_manager != None: + if self.power_manager.isIgnoringBatteryOptimizations(package_name): + self.power_restricted = False + else: + self.power_restricted = True + + self.sideband.setstate("android.power_restricted", self.power_restricted) + def android_location_callback(self, **kwargs): self._raw_gps = kwargs self._last_gps_update = time.time() @@ -200,6 +213,7 @@ class SidebandService(): self.app_context = None self.wifi_manager = None self.power_manager = None + self.power_restricted = False self.usb_devices = [] self.usb_device_filter = SidebandService.usb_device_filter @@ -235,6 +249,7 @@ class SidebandService(): self.sideband.start() self.update_connectivity_type() + self.update_power_restrictions() if RNS.vendor.platformutils.is_android(): RNS.log("Discovered USB devices: "+str(self.usb_devices), RNS.LOG_EXTREME) From 4a12c136a058d622a65d5324739a3779eafbef81 Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Sat, 21 Sep 2024 20:59:50 +0200 Subject: [PATCH 04/37] Set immutable flag on notification intent. Closes #56. --- sbapp/services/sidebandservice.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sbapp/services/sidebandservice.py b/sbapp/services/sidebandservice.py index adc115b..c73e590 100644 --- a/sbapp/services/sidebandservice.py +++ b/sbapp/services/sidebandservice.py @@ -116,7 +116,7 @@ class SidebandService(): notification_intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP) notification_intent.setAction(Intent.ACTION_MAIN) notification_intent.addCategory(Intent.CATEGORY_LAUNCHER) - self.notification_intent = PendingIntent.getActivity(self.app_context, 0, notification_intent, 0) + self.notification_intent = PendingIntent.getActivity(self.app_context, 0, notification_intent, PendingIntent.FLAG_IMMUTABLE) notification.setContentIntent(self.notification_intent) notification.setAutoCancel(True) From 30ccd645354e81bcdf9431b057031afa108bf0a2 Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Sun, 22 Sep 2024 00:10:40 +0200 Subject: [PATCH 05/37] Improved notification handling on Android --- sbapp/main.py | 18 +++++++++++++++++- sbapp/patches/PythonService.java | 6 +++--- sbapp/services/sidebandservice.py | 11 +++++++++-- sbapp/sideband/core.py | 1 + 4 files changed, 30 insertions(+), 6 deletions(-) diff --git a/sbapp/main.py b/sbapp/main.py index 027ca8d..4bda02d 100644 --- a/sbapp/main.py +++ b/sbapp/main.py @@ -820,11 +820,27 @@ class SidebandApp(MDApp): self.check_bluetooth_permissions() def on_new_intent(self, intent): - RNS.log("Received intent", RNS.LOG_DEBUG) intent_action = intent.getAction() action = None data = None + RNS.log(f"Received intent: {intent_action}", RNS.LOG_DEBUG) + + if intent_action == "android.intent.action.MAIN": + JString = autoclass('java.lang.String') + Intent = autoclass("android.content.Intent") + try: + data = intent.getExtras().getString("intent_action", "undefined") + if data.startswith("conversation."): + conv_hexhash = bytes.fromhex(data.replace("conversation.", "")) + def cb(dt): + self.open_conversation(conv_hexhash) + Clock.schedule_once(cb, 0.2) + + except Exception as e: + RNS.log(f"Error while getting intent action data: {e}", RNS.LOG_ERROR) + RNS.trace_exception(e) + if intent_action == "android.intent.action.WEB_SEARCH": SearchManager = autoclass('android.app.SearchManager') data = intent.getStringExtra(SearchManager.QUERY) diff --git a/sbapp/patches/PythonService.java b/sbapp/patches/PythonService.java index 48fc6ff..ad7142c 100644 --- a/sbapp/patches/PythonService.java +++ b/sbapp/patches/PythonService.java @@ -142,13 +142,13 @@ public class PythonService extends Service implements Runnable { manager.createNotificationChannel(chan); Notification.Builder builder = new Notification.Builder(context, NOTIFICATION_CHANNEL_ID); - builder.setContentTitle("Sideband Active"); - // builder.setContentText("Reticulum Active"); + builder.setContentTitle("Reticulum available"); + // builder.setContentText("Reticulum available"); builder.setContentIntent(pIntent); // builder.setOngoing(true); String files_path = context.getFilesDir().getPath(); - Bitmap icon_bitmap = BitmapFactory.decodeFile(files_path+"/app/assets/notification_icon.png"); + Bitmap icon_bitmap = BitmapFactory.decodeFile(files_path+"/app/assets/notification_icon_black.png"); Icon service_icon = Icon.createWithBitmap(icon_bitmap); // builder.setSmallIcon(context.getApplicationInfo().icon); builder.setSmallIcon(service_icon); diff --git a/sbapp/services/sidebandservice.py b/sbapp/services/sidebandservice.py index c73e590..8864662 100644 --- a/sbapp/services/sidebandservice.py +++ b/sbapp/services/sidebandservice.py @@ -38,6 +38,7 @@ if RNS.vendor.platformutils.get_platform() == "android": AndroidString = autoclass('java.lang.String') NotificationManager = autoclass('android.app.NotificationManager') Context = autoclass('android.content.Context') + JString = autoclass('java.lang.String') if android_api_version >= 26: NotificationBuilder = autoclass('android.app.Notification$Builder') @@ -90,22 +91,26 @@ class SidebandService(): self.notification_channel = NotificationChannel(channel_id, channel_name, NotificationManager.IMPORTANCE_DEFAULT) self.notification_channel.enableVibration(True) + self.notification_channel.setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION), None) self.notification_channel.setShowBadge(True) self.notification_service.createNotificationChannel(self.notification_channel) notification = NotificationBuilder(self.app_context, channel_id) notification.setContentTitle(title) notification.setContentText(AndroidString(content)) + notification.setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)) # if group != None: # notification.setGroup(group_id) if not self.notification_small_icon: - path = self.sideband.notification_icon + # path = self.sideband.notification_icon + path = self.sideband.notif_icon_black bitmap = BitmapFactory.decodeFile(path) self.notification_small_icon = Icon.createWithBitmap(bitmap) notification.setSmallIcon(self.notification_small_icon) + # notification.setLargeIcon(self.notification_small_icon) # large_icon_path = self.sideband.icon # bitmap_icon = BitmapFactory.decodeFile(large_icon_path) @@ -116,7 +121,9 @@ class SidebandService(): notification_intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP) notification_intent.setAction(Intent.ACTION_MAIN) notification_intent.addCategory(Intent.CATEGORY_LAUNCHER) - self.notification_intent = PendingIntent.getActivity(self.app_context, 0, notification_intent, PendingIntent.FLAG_IMMUTABLE) + if context_id != None: + notification_intent.putExtra(JString("intent_action"), JString(f"conversation.{context_id}")) + self.notification_intent = PendingIntent.getActivity(self.app_context, 0, notification_intent, PendingIntent.FLAG_MUTABLE) notification.setContentIntent(self.notification_intent) notification.setAutoCancel(True) diff --git a/sbapp/sideband/core.py b/sbapp/sideband/core.py index be35294..05009d9 100644 --- a/sbapp/sideband/core.py +++ b/sbapp/sideband/core.py @@ -198,6 +198,7 @@ class SidebandCore(): self.icon_32 = self.asset_dir+"/icon_32.png" self.icon_macos = self.asset_dir+"/icon_macos.png" self.notification_icon = self.asset_dir+"/notification_icon.png" + self.notif_icon_black = self.asset_dir+"/notification_icon_black.png" os.environ["TELEMETER_GEOID_PATH"] = os.path.join(self.asset_dir, "geoids") From 1e604f15a86e122bbd6e6625c973b39b636eed38 Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Sun, 22 Sep 2024 00:36:35 +0200 Subject: [PATCH 06/37] Open conversations from notifications on Android --- sbapp/main.py | 14 ++++++++------ sbapp/services/sidebandservice.py | 16 ++++++++-------- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/sbapp/main.py b/sbapp/main.py index 4bda02d..f428539 100644 --- a/sbapp/main.py +++ b/sbapp/main.py @@ -830,12 +830,14 @@ class SidebandApp(MDApp): JString = autoclass('java.lang.String') Intent = autoclass("android.content.Intent") try: - data = intent.getExtras().getString("intent_action", "undefined") - if data.startswith("conversation."): - conv_hexhash = bytes.fromhex(data.replace("conversation.", "")) - def cb(dt): - self.open_conversation(conv_hexhash) - Clock.schedule_once(cb, 0.2) + extras = intent.getExtras() + if extras: + data = extras.getString("intent_action", "undefined") + if data.startswith("conversation."): + conv_hexhash = bytes.fromhex(data.replace("conversation.", "")) + def cb(dt): + self.open_conversation(conv_hexhash) + Clock.schedule_once(cb, 0.2) except Exception as e: RNS.log(f"Error while getting intent action data: {e}", RNS.LOG_ERROR) diff --git a/sbapp/services/sidebandservice.py b/sbapp/services/sidebandservice.py index 8864662..b263e65 100644 --- a/sbapp/services/sidebandservice.py +++ b/sbapp/services/sidebandservice.py @@ -116,14 +116,14 @@ class SidebandService(): # bitmap_icon = BitmapFactory.decodeFile(large_icon_path) # notification.setLargeIcon(bitmap_icon) - if not self.notification_intent: - notification_intent = Intent(self.app_context, python_act) - notification_intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP) - notification_intent.setAction(Intent.ACTION_MAIN) - notification_intent.addCategory(Intent.CATEGORY_LAUNCHER) - if context_id != None: - notification_intent.putExtra(JString("intent_action"), JString(f"conversation.{context_id}")) - self.notification_intent = PendingIntent.getActivity(self.app_context, 0, notification_intent, PendingIntent.FLAG_MUTABLE) + notification_intent = Intent(self.app_context, python_act) + notification_intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP) + notification_intent.setAction(Intent.ACTION_MAIN) + notification_intent.addCategory(Intent.CATEGORY_LAUNCHER) + if context_id != None: + cstr = f"conversation.{context_id}" + notification_intent.putExtra(JString("intent_action"), JString(cstr)) + self.notification_intent = PendingIntent.getActivity(self.app_context, 0, notification_intent, PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT) notification.setContentIntent(self.notification_intent) notification.setAutoCancel(True) From 6a214294695c5c74d88714554c282303488d9624 Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Sun, 22 Sep 2024 11:42:18 +0200 Subject: [PATCH 07/37] Don't set PN until message router has been instantiated --- sbapp/sideband/core.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/sbapp/sideband/core.py b/sbapp/sideband/core.py index 05009d9..e605e05 100644 --- a/sbapp/sideband/core.py +++ b/sbapp/sideband/core.py @@ -151,6 +151,7 @@ class SidebandCore(): self.default_lxm_limit = 128*1000 self.state_db = {} self.state_lock = Lock() + self.message_router = None self.rpc_connection = None self.service_stopped = False self.service_context = service_context @@ -859,12 +860,13 @@ class SidebandCore(): RNS.log("No active propagation node configured") else: try: - self.active_propagation_node = dest - self.config["last_lxmf_propagation_node"] = dest - self.message_router.set_outbound_propagation_node(dest) - - RNS.log("Active propagation node set to: "+RNS.prettyhexrep(dest)) - self.__save_config() + if self.message_router: + self.active_propagation_node = dest + self.config["last_lxmf_propagation_node"] = dest + self.message_router.set_outbound_propagation_node(dest) + + RNS.log("Active propagation node set to: "+RNS.prettyhexrep(dest)) + self.__save_config() except Exception as e: RNS.log("Error while setting LXMF propagation node: "+str(e), RNS.LOG_ERROR) From c2dcc55d6e8a926d9992870070142c0ae5707136 Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Sun, 22 Sep 2024 11:45:26 +0200 Subject: [PATCH 08/37] Update build code --- sbapp/buildozer.spec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sbapp/buildozer.spec b/sbapp/buildozer.spec index 35b1d19..039fa45 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 = 20240920 +android.numeric_version = 20240922 requirements = kivy==2.3.0,libbz2,pillow==10.2.0,qrcode==7.3.1,usb4a,usbserial4a,libwebp,libogg,libopus,opusfile,numpy,cryptography,ffpyplayer,codec2,pycodec2,sh,pynacl From 919cd6a44951f311adb2e5eb7d9ba70df9ddd9aa Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Mon, 23 Sep 2024 00:03:13 +0200 Subject: [PATCH 09/37] Allow predictive text on Android by default, add option to block predictive text. Fixes #55. --- sbapp/main.py | 7 +++++++ sbapp/sideband/core.py | 2 ++ sbapp/ui/layouts.py | 26 +++++++++++++------------- sbapp/ui/messages.py | 10 ++++++++++ 4 files changed, 32 insertions(+), 13 deletions(-) diff --git a/sbapp/main.py b/sbapp/main.py index f428539..100d91a 100644 --- a/sbapp/main.py +++ b/sbapp/main.py @@ -2751,6 +2751,10 @@ class SidebandApp(MDApp): self.sideband.save_configuration() self.sideband._reticulum_log_debug(self.sideband.config["debug"]) + def save_block_predictive_text(sender=None, event=None): + self.sideband.config["block_predictive_text"] = self.settings_screen.ids.settings_block_predictive_text.active + self.sideband.save_configuration() + def save_print_command(sender=None, event=None): if not sender.focus: in_cmd = self.settings_screen.ids.settings_print_command.text @@ -2917,6 +2921,9 @@ class SidebandApp(MDApp): self.settings_screen.ids.settings_debug.active = self.sideband.config["debug"] self.settings_screen.ids.settings_debug.bind(active=save_debug) + self.settings_screen.ids.settings_block_predictive_text.active = self.sideband.config["block_predictive_text"] + self.settings_screen.ids.settings_block_predictive_text.bind(active=save_block_predictive_text) + self.settings_screen.ids.settings_lang_default.active = False self.settings_screen.ids.settings_lang_chinese.active = False self.settings_screen.ids.settings_lang_japanese.active = False diff --git a/sbapp/sideband/core.py b/sbapp/sideband/core.py index e605e05..5d346cd 100644 --- a/sbapp/sideband/core.py +++ b/sbapp/sideband/core.py @@ -531,6 +531,8 @@ class SidebandCore(): self.config["input_language"] = None if not "allow_predictive_text" in self.config: self.config["allow_predictive_text"] = False + if not "block_predictive_text" in self.config: + self.config["block_predictive_text"] = False if not "connect_transport" in self.config: self.config["connect_transport"] = False diff --git a/sbapp/ui/layouts.py b/sbapp/ui/layouts.py index 66acdd0..afe4f01 100644 --- a/sbapp/ui/layouts.py +++ b/sbapp/ui/layouts.py @@ -1822,20 +1822,20 @@ MDScreen: pos_hint: {"center_y": 0.3} active: False - # MDBoxLayout: - # orientation: "horizontal" - # size_hint_y: None - # padding: [0,0,dp(24),dp(0)] - # height: dp(48) + MDBoxLayout: + orientation: "horizontal" + size_hint_y: None + padding: [0,0,dp(24),dp(0)] + height: dp(48) - # MDLabel: - # text: "Allow Predictive Text" - # font_style: "H6" + MDLabel: + text: "Block Predictive Text" + font_style: "H6" - # MDSwitch: - # id: settings_allow_predictive_text - # pos_hint: {"center_y": 0.3} - # active: False + MDSwitch: + id: settings_block_predictive_text + pos_hint: {"center_y": 0.3} + active: False # MDBoxLayout: # orientation: "vertical" @@ -1844,7 +1844,7 @@ MDScreen: # padding: [0, dp(24), 0, dp(24)] # MDRectangleFlatIconButton: - # id: hardware_rnode_button + # id: input_language_button # icon: "translate" # text: "Input Languages" # padding: [dp(0), dp(14), dp(0), dp(14)] diff --git a/sbapp/ui/messages.py b/sbapp/ui/messages.py index 8798723..7a18569 100644 --- a/sbapp/ui/messages.py +++ b/sbapp/ui/messages.py @@ -194,6 +194,15 @@ class Messages(): self.details_dialog.open() def update(self, limit=8): + if self.app.sideband.config["block_predictive_text"]: + if self.ids.message_text.input_type != "null": + self.ids.message_text.input_type = "null" + self.ids.message_text.keyboard_suggestions = False + else: + if self.ids.message_text.input_type != "text": + self.ids.message_text.input_type = "text" + self.ids.message_text.keyboard_suggestions = False + for new_message in self.app.sideband.list_messages(self.context_dest, after=self.latest_message_timestamp,limit=limit): self.new_messages.append(new_message) @@ -1229,6 +1238,7 @@ MDScreen: MDTextField: id: message_text + input_type: "text" keyboard_suggestions: True multiline: True hint_text: "Write message" From 26899f8cd681b93dd238b17edddb7c7010b33ec0 Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Mon, 23 Sep 2024 00:42:17 +0200 Subject: [PATCH 10/37] Allow predictive text on Android by default, add option to block predictive text. Fixes #55. --- sbapp/ui/messages.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sbapp/ui/messages.py b/sbapp/ui/messages.py index 7a18569..2047ecf 100644 --- a/sbapp/ui/messages.py +++ b/sbapp/ui/messages.py @@ -201,7 +201,7 @@ class Messages(): else: if self.ids.message_text.input_type != "text": self.ids.message_text.input_type = "text" - self.ids.message_text.keyboard_suggestions = False + self.ids.message_text.keyboard_suggestions = True for new_message in self.app.sideband.list_messages(self.context_dest, after=self.latest_message_timestamp,limit=limit): self.new_messages.append(new_message) From 210163de0aa2c3c5b1cab4d9bf49dffcb07b716a Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Mon, 23 Sep 2024 12:05:27 +0200 Subject: [PATCH 11/37] Add option to use high-quality voice for ptt --- sbapp/main.py | 13 ++++++++++++- sbapp/sideband/core.py | 2 ++ sbapp/ui/layouts.py | 16 ++++++++++++++++ 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/sbapp/main.py b/sbapp/main.py index 100d91a..6b8ea1a 100644 --- a/sbapp/main.py +++ b/sbapp/main.py @@ -1811,7 +1811,11 @@ class SidebandApp(MDApp): return self.sideband.ui_started_recording() - self.audio_msg_mode = LXMF.AM_CODEC2_2400 + if self.sideband.config["hq_ptt"]: + self.audio_msg_mode = LXMF.AM_OPUS_OGG + else: + self.audio_msg_mode = LXMF.AM_CODEC2_2400 + self.message_attach_action(attach_type="audio", nodialog=True) if self.rec_dialog == None: self.message_init_rec_dialog() @@ -2755,6 +2759,10 @@ class SidebandApp(MDApp): self.sideband.config["block_predictive_text"] = self.settings_screen.ids.settings_block_predictive_text.active self.sideband.save_configuration() + def save_hq_ptt(sender=None, event=None): + self.sideband.config["hq_ptt"] = self.settings_screen.ids.settings_hq_ptt.active + self.sideband.save_configuration() + def save_print_command(sender=None, event=None): if not sender.focus: in_cmd = self.settings_screen.ids.settings_print_command.text @@ -2918,6 +2926,9 @@ class SidebandApp(MDApp): self.settings_screen.ids.settings_lxm_limit_1mb.active = self.sideband.config["lxm_limit_1mb"] self.settings_screen.ids.settings_lxm_limit_1mb.bind(active=save_lxm_limit_1mb) + self.settings_screen.ids.settings_hq_ptt.active = self.sideband.config["hq_ptt"] + self.settings_screen.ids.settings_hq_ptt.bind(active=save_hq_ptt) + self.settings_screen.ids.settings_debug.active = self.sideband.config["debug"] self.settings_screen.ids.settings_debug.bind(active=save_debug) diff --git a/sbapp/sideband/core.py b/sbapp/sideband/core.py index 5d346cd..c4a8938 100644 --- a/sbapp/sideband/core.py +++ b/sbapp/sideband/core.py @@ -526,6 +526,8 @@ class SidebandCore(): self.config["display_style_in_contact_list"] = False if not "lxm_limit_1mb" in self.config: self.config["lxm_limit_1mb"] = True + if not "hq_ptt" in self.config: + self.config["hq_ptt"] = False if not "input_language" in self.config: self.config["input_language"] = None diff --git a/sbapp/ui/layouts.py b/sbapp/ui/layouts.py index afe4f01..e332125 100644 --- a/sbapp/ui/layouts.py +++ b/sbapp/ui/layouts.py @@ -1678,6 +1678,22 @@ MDScreen: disabled: False active: False + MDBoxLayout: + orientation: "horizontal" + size_hint_y: None + padding: [0,0,dp(24),dp(0)] + height: dp(48) + + MDLabel: + text: "Use high-quality voice for PTT" + font_style: "H6" + + MDSwitch: + id: settings_hq_ptt + pos_hint: {"center_y": 0.3} + disabled: False + active: False + MDBoxLayout: orientation: "horizontal" size_hint_y: None From 88e03340cf0f684989d1c7ad222cb187b1790632 Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Mon, 23 Sep 2024 17:54:43 +0200 Subject: [PATCH 12/37] Fix file manager access on Android 10 --- sbapp/kivymd/uix/filemanager/filemanager.py | 3 ++- sbapp/main.py | 2 -- sbapp/patches/AndroidManifest.tmpl.xml | 1 + 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/sbapp/kivymd/uix/filemanager/filemanager.py b/sbapp/kivymd/uix/filemanager/filemanager.py index 4073764..728c796 100755 --- a/sbapp/kivymd/uix/filemanager/filemanager.py +++ b/sbapp/kivymd/uix/filemanager/filemanager.py @@ -649,7 +649,8 @@ class MDFileManager(MDRelativeLayout): return dirs, files - except OSError: + except OSError as e: + print("Filemanager OSError: "+str(e)) return None, None def close(self) -> None: diff --git a/sbapp/main.py b/sbapp/main.py index 6b8ea1a..9e3de99 100644 --- a/sbapp/main.py +++ b/sbapp/main.py @@ -1677,8 +1677,6 @@ class SidebandApp(MDApp): ate_dialog.open() else: - self.sideband.config["map_storage_path"] = None - self.sideband.save_configuration() if RNS.vendor.platformutils.get_platform() == "android": toast("No file access, check permissions!") else: diff --git a/sbapp/patches/AndroidManifest.tmpl.xml b/sbapp/patches/AndroidManifest.tmpl.xml index e7a005f..c6cdd64 100644 --- a/sbapp/patches/AndroidManifest.tmpl.xml +++ b/sbapp/patches/AndroidManifest.tmpl.xml @@ -54,6 +54,7 @@ {{ args.extra_manifest_application_arguments }} android:theme="{{args.android_apptheme}}{% if not args.window %}.Fullscreen{% endif %}" android:hardwareAccelerated="true" + android:requestLegacyExternalStorage="true" > {% for l in args.android_used_libs %} From 558771293dd12a6487dd16392301163d9806e7e3 Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Mon, 23 Sep 2024 21:54:19 +0200 Subject: [PATCH 13/37] Update version --- sbapp/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sbapp/main.py b/sbapp/main.py index 9e3de99..ca6affa 100644 --- a/sbapp/main.py +++ b/sbapp/main.py @@ -1,6 +1,6 @@ __debug_build__ = False __disable_shaders__ = False -__version__ = "0.9.7" +__version__ = "0.9.8" __variant__ = "beta" import sys From e38a7b255999fae94bae59d24606827222ea040b Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Mon, 23 Sep 2024 23:44:06 +0200 Subject: [PATCH 14/37] Update dependency --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 84c5efa..1b09e18 100644 --- a/setup.py +++ b/setup.py @@ -96,7 +96,7 @@ setuptools.setup( ] }, install_requires=[ - "rns>=0.7.8", + "rns>=0.7.9", "lxmf>=0.5.3", "kivy>=2.3.0", "pillow>=10.2.0", From a16058329b16a3d855a068ea15021e7812c58ebb Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Tue, 24 Sep 2024 13:26:43 +0200 Subject: [PATCH 15/37] Updated guide --- sbapp/main.py | 25 ++++++++++++++++--------- sbapp/ui/layouts.py | 8 ++++++++ 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/sbapp/main.py b/sbapp/main.py index ca6affa..4007b69 100644 --- a/sbapp/main.py +++ b/sbapp/main.py @@ -5601,7 +5601,7 @@ class SidebandApp(MDApp): threading.Thread(target=lj, daemon=True).start() guide_text1 = """ -[size=18dp][b]Introduction[/b][/size][size=5dp]\n \n[/size]Welcome to [i]Sideband[/i], an LXMF client for Android, Linux and macOS. With Sideband, you can communicate with other people or LXMF-compatible systems over Reticulum networks using LoRa, Packet Radio, WiFi, I2P, or anything else Reticulum supports. +[size=18dp][b]Introduction[/b][/size][size=5dp]\n \n[/size]Welcome to [i]Sideband[/i], an LXMF client for Android, Linux, macOS and Windows. With Sideband, you can communicate with other people or LXMF-compatible systems over Reticulum networks using LoRa, Packet Radio, WiFi, I2P, or anything else Reticulum supports. This short guide will give you a basic introduction to the concepts that underpin Sideband and LXMF (which is the protocol that Sideband uses to communicate). If you are not already familiar with LXMF and Reticulum, it is probably a good idea to read this guide, since Sideband is very different from other messaging apps.""" guide_text2 = """ @@ -5610,25 +5610,28 @@ This short guide will give you a basic introduction to the concepts that underpi This also means that Sideband operates differently than what you might be used to. It does not need a connection to a server on the Internet to function, and you do not have an account anywhere.""" guide_text3 = """ -[size=18dp][b]Operating Principles[/b][/size][size=5dp]\n \n[/size]When Sideband is started on your device for the first time, it randomly generates a set of cryptographic keys. These keys are then used to create an LXMF address for your use. Any other endpoint in [i]any[/i] Reticulum network will be able to send data to this address, as long as there is [i]some sort of physical connection[/i] between your device and the remote endpoint. You can also move around to other Reticulum networks with this address, even ones that were never connected to the network the address was created on, or that didn't exist when the address was created. The address is yours to keep and control for as long (or short) a time you need it, and you can always delete it and create a new one.""" +[size=18dp][b]Operating Principles[/b][/size][size=5dp]\n \n[/size]When Sideband is started on your device for the first time, it randomly generates a 512-bit Reticulum Identity Key. This cryptographic key is then used to create an LXMF address for your use, and in turn to secure any communication to your address. Any other endpoint in [i]any[/i] Reticulum network will be able to send data to your address, as long as there is [i]some sort of physical connection[/i] between your device and the remote endpoint. You can also move around to other Reticulum networks with this address, even ones that were never connected to the network the address was created on, or that didn't exist when the address was created.\n\nYour LXMF address is yours to keep and control for as long (or short) a time you need it, and you can always delete it and create a new one. You identity keys and corresponding addresses are never registered on or controlled by any external servers or services, and will never leave your device, unless you manually export them for backup.""" + + guide_text10 = """ +[size=18dp][b]Getting Connected[/b][/size][size=5dp]\n \n[/size]If you already have Reticulum connectivity set up on the device you are running Sideband on, no further configuration should be necessary, and Sideband will simply use the available Reticulum connectivity.\n\nIf you are running Sideband on a computer, you can configure interfaces in the Reticulum configuration file ([b]~/.reticulum/config[/b] by default). If you are running Sideband on an Android device, you can configure various interface types in the [b]Connectivity[/b] section. By default, only an [i]AutoInterface[/i] is enabled, which will connect you automatically with any other local devices on the same WiFi and/or Ethernet networks. This may or may not include Reticulum Transport Nodes, which can route your traffic to wider networks.\n\nYou can enable any or all of the other available interface types to gain wider connectivity. For more specific information on interface types, configuration options, and how to effectively build your own Reticulum networks, see the [b]Reticulum Manual[b].""" guide_text4 = """ -[size=18dp][b]Becoming Reachable[/b][/size][size=5dp]\n \n[/size]To establish reachability for any Reticulum address on a network, an [i]announce[/i] must be sent. Sideband does not do this automatically by default, but can be configured to do so every time the program starts. To send an announce manually, press the [i]Announce[/i] button in the [i]Conversations[/i] section of the program. When you send an announce, you make your LXMF address reachable for real-time messaging to the entire network you are connected to. Even in very large networks, you can expect global reachability for your address to be established in under a minute. +[size=18dp][b]Becoming Reachable[/b][/size][size=5dp]\n \n[/size]To establish reachability for any Reticulum destination on a network, an [i]announce[/i] must be sent. By default, Sideband will announce automatically when necessary, but if you want to stay silent, automatic announces can be disabled in [b]Preferences[/b].\n\nTo send an announce manually, press the [i]Announce[/i] button in the [i]Conversations[/i] section of the program. When you send an announce, you make your LXMF address reachable for real-time messaging to the entire network you are connected to. Even in very large networks, you can expect global reachability for your address to be established in under a minute. If you don't move to other places in the network, and keep connected through the same hubs or gateways, it is generally not necessary to send an announce more often than once every week. If you change your entry point to the network, you may want to send an announce, or you may just want to stay quiet.""" guide_text5 = """ [size=18dp][b]Relax & Disconnect[/b][/size][size=5dp]\n \n[/size]If you are not connected to the network, it is still possible for other people to message you, as long as one or more [i]Propagation Nodes[/i] exist on the network. These nodes pick up and hold encrypted in-transit messages for offline users. Messages are always encrypted before leaving the originators device, and nobody else than the intended recipient can decrypt messages in transit. -The Propagation Nodes also distribute copies of messages between each other, such that even the failure of almost every node in the network will still allow users to sync their waiting messages. If all Propagation Nodes disappear or are destroyed, users can still communicate directly. Reticulum and LXMF will degrade gracefully all the way down to single users communicating directly via long-range data radios. Anyone can start up new propagation nodes and integrate them into existing networks without permission or coordination. Even a small and cheap device like a Rasperry Pi can handle messages for millions of users. LXMF networks are designed to be quite resilient, as long as there are people using them.""" +The Propagation Nodes also distribute copies of messages between each other, such that even the failure of almost every node in the network will still allow users to sync their waiting messages. If all Propagation Nodes disappear or are destroyed, users can still communicate directly.\n\nReticulum and LXMF will degrade gracefully all the way down to single users communicating directly via long-range data radios. Anyone can start up new propagation nodes and integrate them into existing networks without permission or coordination. Even a small and cheap device like a Rasperry Pi can handle messages for millions of users. LXMF networks are designed to be quite resilient, as long as there are people using them.""" guide_text6 = """ -[size=18dp][b]Packets Find A Way[/b][/size][size=5dp]\n \n[/size]Connections in Reticulum networks can be wired or wireless, span many intermediary hops, run over fast links or ultra-low bandwidth radio, tunnel over the Invisible Internet (I2P), private networks, satellite connections, serial lines or anything else that Reticulum can carry data over. In most cases it will not be possible to know what path data takes in a Reticulum network, and no transmitted packets carries any identifying characteristics, apart from a destination address. There is no source addresses in Reticulum. As long as you do not reveal any connecting details between your person and your LXMF address, you can remain anonymous. Sending messages to others does not reveal [i]your[/i] address to anyone else than the intended recipient.""" +[size=18dp][b]Packets Find A Way[/b][/size][size=5dp]\n \n[/size]Connections in Reticulum networks can be wired or wireless, span many intermediary hops, run over fast links or ultra-low bandwidth radio, tunnel over the Invisible Internet (I2P), private networks, satellite connections, serial lines or anything else that Reticulum can carry data over.\n\nIn most cases it will not be possible to know what path packets takes in a Reticulum network, and apart from a destination hash, no transmitted packets carries any identifying characteristics. In Reticulum, [i]there is no source addresses[/i].\n\nAs long as you do not reveal any connecting details between your person and your LXMF address, you can remain anonymous. Sending messages to others does not reveal [i]your[/i] address to anyone else than the intended recipient.""" guide_text7 = """ -[size=18dp][b]Be Yourself, Be Unknown, Stay Free[/b][/size][size=5dp]\n \n[/size]Even with the above characteristics in mind, you [b]must remember[/b] that LXMF and Reticulum is not a technology that can guarantee anonymising connections that are already de-anonymised! If you use Sideband to connect to TCP Reticulum hubs over the clear Internet, from a network that can be tied to your personal identity, an adversary may learn that you are generating LXMF traffic. If you want to avoid this, it is recommended to use I2P to connect to Reticulum hubs on the Internet. Or only connecting from within pure Reticulum networks, that take one or more hops to reach connections that span the Internet. This is a complex topic, with many more nuances than can be covered here. You are encouraged to ask on the various Reticulum discussion forums if you are in doubt. +[size=18dp][b]Be Yourself, Be Unknown, Stay Free[/b][/size][size=5dp]\n \n[/size]Even with the above characteristics in mind, you [b]must remember[/b] that LXMF and Reticulum is not a technology that can guarantee anonymising connections that are already de-anonymised! If you use Sideband to connect to TCP Reticulum hubs over the clear Internet, from a network that can be tied to your personal identity, an adversary may learn that you are generating LXMF traffic.\n\nIf you want to avoid this, it is recommended to use I2P to connect to Reticulum hubs on the Internet. Or only connecting from within pure Reticulum networks, that take one or more hops to reach connections that span the Internet. This is a complex topic, with many more nuances than can be covered here. You are encouraged to ask on the various Reticulum discussion forums if you are in doubt. -If you use Reticulum and LXMF on hardware that does not carry any identifiers tied to you, it is possible to establish a completely free and anonymous communication system with Reticulum and LXMF clients.""" +If you use Reticulum and LXMF on hardware that does not carry any identifiers tied to you, it is possible to establish a completely free and identification-less communication system with Reticulum and LXMF clients.""" guide_text8 = """ [size=18dp][b]Keyboard Shortcuts[/b][/size][size=5dp]\n \n[/size]To ease navigation and operation of the program, Sideband has keyboard shortcuts mapped to the most common actions. A reference is included below. @@ -5647,9 +5650,10 @@ If you use Reticulum and LXMF on hardware that does not carry any identifiers ti - [b]Ctrl-Shift-F[/b] add file - [b]Ctrl-D[/b] or [b]Ctrl-S[/b] Send message - [b]Voice Recording[/b] + [b]Voice & PTT[/b] - [b]Space[/b] Start/stop recording - [b]Enter[/b] Save recording to message + - With PTT enabled, hold [b]Space[/b] to talk [b]Navigation[/b] - [b]Ctrl-[i]n[/i][/b] Go to conversation number [i]n[/i] @@ -5671,7 +5675,7 @@ If you use Reticulum and LXMF on hardware that does not carry any identifiers ti - Hold [b]Alt[/b] to navigate more finely""" guide_text9 = """ -[size=18dp][b]Please Support This Project[/b][/size][size=5dp]\n \n[/size]It took me more than seven years to design and built the entire ecosystem of software and hardware that makes this possible. If this project is valuable to you, please go to [u][ref=link]https://unsigned.io/donate[/ref][/u] to support the project with a donation. Every donation directly makes the entire Reticulum project possible. +[size=18dp][b]Please Support This Project[/b][/size][size=5dp]\n \n[/size]It took me more than eight years to design and build the entire ecosystem of software and hardware that makes this possible. If this project is valuable to you, please go to [u][ref=link]https://unsigned.io/donate[/ref][/u] to support the project with a donation. Every donation directly makes the entire Reticulum project possible. Thank you very much for using Free Communications Systems. """ @@ -5679,6 +5683,7 @@ Thank you very much for using Free Communications Systems. info2 = guide_text8 info3 = guide_text2 info4 = guide_text3 + info10 = guide_text10 info5 = guide_text4 info6 = guide_text5 info7 = guide_text6 @@ -5695,6 +5700,7 @@ Thank you very much for using Free Communications Systems. info7 = "[color=#"+dark_theme_text_color+"]"+info7+"[/color]" info8 = "[color=#"+dark_theme_text_color+"]"+info8+"[/color]" info9 = "[color=#"+dark_theme_text_color+"]"+info9+"[/color]" + info10 = "[color=#"+dark_theme_text_color+"]"+info10+"[/color]" self.guide_screen.ids.guide_info1.text = info1 self.guide_screen.ids.guide_info2.text = info2 self.guide_screen.ids.guide_info3.text = info3 @@ -5704,6 +5710,7 @@ Thank you very much for using Free Communications Systems. self.guide_screen.ids.guide_info7.text = info7 self.guide_screen.ids.guide_info8.text = info8 self.guide_screen.ids.guide_info9.text = info9 + self.guide_screen.ids.guide_info10.text = info10 self.guide_screen.ids.guide_info9.bind(on_ref_press=link_exec) self.guide_screen.ids.guide_scrollview.effect_cls = ScrollEffect diff --git a/sbapp/ui/layouts.py b/sbapp/ui/layouts.py index e332125..e9d41c2 100644 --- a/sbapp/ui/layouts.py +++ b/sbapp/ui/layouts.py @@ -807,6 +807,14 @@ MDScreen: text_size: self.width, None height: self.texture_size[1] + MDLabel: + id: guide_info10 + markup: True + text: "" + size_hint_y: None + text_size: self.width, None + height: self.texture_size[1] + MDLabel: id: guide_info5 markup: True From a90364dd5b53d07f602f911ce25311519c755ea3 Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Tue, 24 Sep 2024 13:28:39 +0200 Subject: [PATCH 16/37] Updated versions --- sbapp/buildozer.spec | 2 +- sbapp/main.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/sbapp/buildozer.spec b/sbapp/buildozer.spec index 039fa45..937062b 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 = 20240922 +android.numeric_version = 20240924 requirements = kivy==2.3.0,libbz2,pillow==10.2.0,qrcode==7.3.1,usb4a,usbserial4a,libwebp,libogg,libopus,opusfile,numpy,cryptography,ffpyplayer,codec2,pycodec2,sh,pynacl diff --git a/sbapp/main.py b/sbapp/main.py index 4007b69..76889a2 100644 --- a/sbapp/main.py +++ b/sbapp/main.py @@ -1,7 +1,7 @@ __debug_build__ = False __disable_shaders__ = False -__version__ = "0.9.8" -__variant__ = "beta" +__version__ = "1.0.0" +__variant__ = "" import sys import argparse From 5aa7e914601d04ba695404babb7b9631461ec5dd Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Tue, 24 Sep 2024 13:35:56 +0200 Subject: [PATCH 17/37] Updated build --- setup.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index 1b09e18..91665ec 100644 --- a/setup.py +++ b/setup.py @@ -30,7 +30,7 @@ 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() @@ -67,7 +67,10 @@ package_data = { ] } -print("Packaging Sideband "+__version__+" "+__variant__) +variant_str = "" +if __variant__: + variant_str = " "+__variant__ +print("Packaging Sideband "+__version__+variant_str) setuptools.setup( name="sbapp", From 1d5cd89a4998c6a08e4e25352c9bee518e8e3374 Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Tue, 24 Sep 2024 13:39:08 +0200 Subject: [PATCH 18/37] Update info string --- sbapp/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sbapp/main.py b/sbapp/main.py index 76889a2..a2d76f2 100644 --- a/sbapp/main.py +++ b/sbapp/main.py @@ -2505,7 +2505,7 @@ class SidebandApp(MDApp): str_comps += "\n - [b]Kivy[/b] (MIT License)\n - [b]Codec2[/b] (LGPL License)\n - [b]PyCodec2[/b] (BSD-3 License)" str_comps += "\n - [b]PyDub[/b] (MIT License)\n - [b]PyOgg[/b] (Public Domain)" str_comps += "\n - [b]GeoidHeight[/b] (LGPL License)\n - [b]Python[/b] (PSF License)" - str_comps += "\n\nGo to [u][ref=link]https://unsigned.io/donate[/ref][/u] to support the project.\n\nThe Sideband app is Copyright (c) 2024 Mark Qvist / unsigned.io\n\nPermission is granted to freely share and distribute binary copies of Sideband v"+__version__+" "+__variant__+", so long as no payment or compensation is charged for said distribution or sharing.\n\nIf you were charged or paid anything for this copy of Sideband, please report it to [b]license@unsigned.io[/b].\n\nTHIS IS EXPERIMENTAL SOFTWARE - SIDEBAND COMES WITH ABSOLUTELY NO WARRANTY - USE AT YOUR OWN RISK AND RESPONSIBILITY" + str_comps += "\n\nGo to [u][ref=link]https://unsigned.io/donate[/ref][/u] to support the project.\n\nThe Sideband app is Copyright (c) 2024 Mark Qvist / unsigned.io\n\nPermission is granted to freely share and distribute binary copies of "+self.root.ids.app_version_info.text+", so long as no payment or compensation is charged for said distribution or sharing.\n\nIf you were charged or paid anything for this copy of Sideband, please report it to [b]license@unsigned.io[/b].\n\nTHIS IS EXPERIMENTAL SOFTWARE - SIDEBAND COMES WITH ABSOLUTELY NO WARRANTY - USE AT YOUR OWN RISK AND RESPONSIBILITY" info = "This is "+self.root.ids.app_version_info.text+", on RNS v"+RNS.__version__+" and LXMF v"+LXMF.__version__+".\n\nHumbly build using the following open components:\n\n"+str_comps self.information_screen.ids.information_info.text = info self.information_screen.ids.information_info.bind(on_ref_press=link_exec) From 0902f869e1dc40d7743f204b6d8a8b4f092f162f Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Wed, 25 Sep 2024 12:29:53 +0200 Subject: [PATCH 19/37] Fixed invalid automatic telemetry request scheduling timebase --- sbapp/sideband/core.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sbapp/sideband/core.py b/sbapp/sideband/core.py index c4a8938..59c0150 100644 --- a/sbapp/sideband/core.py +++ b/sbapp/sideband/core.py @@ -759,6 +759,7 @@ class SidebandCore(): self.update_ignore_invalid_stamps() except Exception as e: RNS.log("Error while reloading configuration: "+str(e), RNS.LOG_ERROR) + RNS.trace_exception(e) def __save_config(self): RNS.log("Saving Sideband configuration...", RNS.LOG_DEBUG) @@ -1203,7 +1204,8 @@ class SidebandCore(): self.message_router.handle_outbound(message) else: if message.state == LXMF.LXMessage.DELIVERED: - self.setpersistent(f"telemetry.{RNS.hexrep(message.destination_hash, delimit=False)}.last_request_success_timebase", message.request_timebase) + delivery_timebase = int(time.time()) + self.setpersistent(f"telemetry.{RNS.hexrep(message.destination_hash, delimit=False)}.last_request_success_timebase", delivery_timebase) self.setstate(f"telemetry.{RNS.hexrep(message.destination_hash, delimit=False)}.request_sending", False) if message.destination_hash == self.config["telemetry_collector"]: self.pending_telemetry_request = False From e98953623207872719649625a421e25f915f4212 Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Wed, 25 Sep 2024 15:51:12 +0200 Subject: [PATCH 20/37] Updated versions --- sbapp/main.py | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sbapp/main.py b/sbapp/main.py index a2d76f2..8534e40 100644 --- a/sbapp/main.py +++ b/sbapp/main.py @@ -1,6 +1,6 @@ __debug_build__ = False __disable_shaders__ = False -__version__ = "1.0.0" +__version__ = "1.0.1" __variant__ = "" import sys diff --git a/setup.py b/setup.py index 91665ec..45b6ec1 100644 --- a/setup.py +++ b/setup.py @@ -99,7 +99,7 @@ setuptools.setup( ] }, install_requires=[ - "rns>=0.7.9", + "rns>=0.8.0", "lxmf>=0.5.3", "kivy>=2.3.0", "pillow>=10.2.0", From 17180283e209129df7af229bac4e5a9cbb27f9b8 Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Wed, 25 Sep 2024 16:18:29 +0200 Subject: [PATCH 21/37] Updated Android build code --- sbapp/buildozer.spec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sbapp/buildozer.spec b/sbapp/buildozer.spec index 937062b..f664648 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 = 20240924 +android.numeric_version = 20240925 requirements = kivy==2.3.0,libbz2,pillow==10.2.0,qrcode==7.3.1,usb4a,usbserial4a,libwebp,libogg,libopus,opusfile,numpy,cryptography,ffpyplayer,codec2,pycodec2,sh,pynacl From 54cac42aed5f09a782b62406b7450706431a9680 Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Tue, 1 Oct 2024 15:46:19 +0200 Subject: [PATCH 22/37] Added RNode battery info to connectivity dialog --- sbapp/services/sidebandservice.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/sbapp/services/sidebandservice.py b/sbapp/services/sidebandservice.py index b263e65..e718a6f 100644 --- a/sbapp/services/sidebandservice.py +++ b/sbapp/services/sidebandservice.py @@ -370,7 +370,13 @@ class SidebandService(): else: rs = "Interface Down" - stat += "[b]RNode[/b]\n{rs}\n\n".format(rs=rs) + bs = "" + bat_state = self.sideband.interface_rnode.get_battery_state_string() + bat_percent = self.sideband.interface_rnode.get_battery_percent() + if bat_state != "unknown": + bs = f"\nBattery at {bat_percent}%" + + stat += f"[b]RNode[/b]\n{rs}{bs}\n\n" if self.sideband.interface_modem != None: if self.sideband.interface_modem.online: From 6b97fd8e4bdef4048a939f00b8949fec5c36e2c8 Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Tue, 1 Oct 2024 15:55:56 +0200 Subject: [PATCH 23/37] Allow connecting RNode over BLE --- sbapp/sideband/core.py | 67 +++++++++++++++++++++++++++++++----------- 1 file changed, 50 insertions(+), 17 deletions(-) diff --git a/sbapp/sideband/core.py b/sbapp/sideband/core.py index 59c0150..1055643 100644 --- a/sbapp/sideband/core.py +++ b/sbapp/sideband/core.py @@ -433,6 +433,7 @@ class SidebandCore(): self.config["hw_rnode_beacondata"] = None self.config["hw_rnode_bt_device"] = None self.config["hw_rnode_bluetooth"] = False + self.config["hw_rnode_ble"] = False self.config["hw_modem_baudrate"] = 57600 self.config["hw_modem_databits"] = 8 self.config["hw_modem_stopbits"] = 1 @@ -590,6 +591,8 @@ class SidebandCore(): self.config["hw_rnode_beacondata"] = None if not "hw_rnode_bluetooth" in self.config: self.config["hw_rnode_bluetooth"] = False + if not "hw_rnode_ble" in self.config: + self.config["hw_rnode_ble"] = False if not "hw_rnode_enable_framebuffer" in self.config: self.config["hw_rnode_enable_framebuffer"] = False if not "hw_rnode_bt_device" in self.config: @@ -3620,6 +3623,7 @@ class SidebandCore(): bt_device_name = None rnode_allow_bluetooth = False + rnode_allow_ble = False if self.getpersistent("permissions.bluetooth"): if self.config["hw_rnode_bluetooth"]: RNS.log("Allowing RNode bluetooth", RNS.LOG_DEBUG) @@ -3630,6 +3634,14 @@ class SidebandCore(): else: RNS.log("Disallowing RNode bluetooth since config is disabled", RNS.LOG_DEBUG) rnode_allow_bluetooth = False + + if self.config["hw_rnode_ble"] and self.getpersistent("permissions.ble"): + RNS.log("Allowing RNode BLE", RNS.LOG_DEBUG) + # rnode_allow_ble = True + else: + RNS.log("Disallowing RNode BLE due to missing permission", RNS.LOG_DEBUG) + # rnode_allow_ble = False + else: RNS.log("Disallowing RNode bluetooth due to missing permission", RNS.LOG_DEBUG) rnode_allow_bluetooth = False @@ -3654,23 +3666,44 @@ class SidebandCore(): else: atl_long = self.config["hw_rnode_atl_long"] - 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, - ) + 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, + ) + + 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, + ) rnodeinterface.OUT = True From 2cf7d8ad84dc78ae2df25d5a50e060b98f29adfe Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Tue, 1 Oct 2024 16:22:47 +0200 Subject: [PATCH 24/37] Added thread lock to service RPC calls --- sbapp/sideband/core.py | 99 +++++++++++++----------------------------- 1 file changed, 31 insertions(+), 68 deletions(-) diff --git a/sbapp/sideband/core.py b/sbapp/sideband/core.py index 1055643..0f68a29 100644 --- a/sbapp/sideband/core.py +++ b/sbapp/sideband/core.py @@ -153,6 +153,7 @@ class SidebandCore(): self.state_lock = Lock() self.message_router = None self.rpc_connection = None + self.rpc_lock = Lock() self.service_stopped = False self.service_context = service_context self.owner_service = owner_service @@ -1223,13 +1224,8 @@ class SidebandCore(): else: if self.is_client: try: - if self.rpc_connection == None: - self.rpc_connection = multiprocessing.connection.Client(self.rpc_addr, authkey=self.rpc_key) - - self.rpc_connection.send({"request_latest_telemetry": {"from_addr": from_addr}}) - response = self.rpc_connection.recv() - return response - + return self.service_rpc_request({"request_latest_telemetry": {"from_addr": from_addr}}) + except Exception as e: RNS.log("Error while requesting latest telemetry over RPC: "+str(e), RNS.LOG_DEBUG) RNS.trace_exception(e) @@ -1302,17 +1298,12 @@ class SidebandCore(): else: if self.is_client: try: - if self.rpc_connection == None: - self.rpc_connection = multiprocessing.connection.Client(self.rpc_addr, authkey=self.rpc_key) - - self.rpc_connection.send({"send_latest_telemetry": { + return self.service_rpc_request({"send_latest_telemetry": { "to_addr": to_addr, "stream": stream, "is_authorized_telemetry_request": is_authorized_telemetry_request} }) - response = self.rpc_connection.recv() - return response - + except Exception as e: RNS.log("Error while sending latest telemetry over RPC: "+str(e), RNS.LOG_DEBUG) RNS.trace_exception(e) @@ -1520,11 +1511,7 @@ class SidebandCore(): return True else: def set(): - if self.rpc_connection == None: - self.rpc_connection = multiprocessing.connection.Client(self.rpc_addr, authkey=self.rpc_key) - self.rpc_connection.send({"setstate": (prop, val)}) - response = self.rpc_connection.recv() - return response + return self.service_rpc_request({"setstate": (prop, val)}) try: set() @@ -1546,11 +1533,7 @@ class SidebandCore(): return True else: try: - if self.rpc_connection == None: - self.rpc_connection = multiprocessing.connection.Client(self.rpc_addr, authkey=self.rpc_key) - self.rpc_connection.send({"latest_telemetry": (latest_telemetry, latest_packed_telemetry)}) - response = self.rpc_connection.recv() - return response + return self.service_rpc_request({"latest_telemetry": (latest_telemetry, latest_packed_telemetry)}) except Exception as e: RNS.log("Error while setting telemetry over RPC: "+str(e), RNS.LOG_DEBUG) return False @@ -1567,11 +1550,7 @@ class SidebandCore(): return True else: try: - if self.rpc_connection == None: - self.rpc_connection = multiprocessing.connection.Client(self.rpc_addr, authkey=self.rpc_key) - self.rpc_connection.send({"set_debug": debug}) - response = self.rpc_connection.recv() - return response + return self.service_rpc_request({"set_debug": debug}) except Exception as e: RNS.log("Error while setting log level over RPC: "+str(e), RNS.LOG_DEBUG) return False @@ -1585,15 +1564,25 @@ class SidebandCore(): return True else: try: - if self.rpc_connection == None: - self.rpc_connection = multiprocessing.connection.Client(self.rpc_addr, authkey=self.rpc_key) - self.rpc_connection.send({"set_ui_recording": recording}) - response = self.rpc_connection.recv() - return response + return self.service_rpc_request({"set_ui_recording": recording}) except Exception as e: RNS.log("Error while setting UI recording status over RPC: "+str(e), RNS.LOG_DEBUG) return False + def service_rpc_request(self, request): + # RNS.log("Running service RPC call: "+str(request), RNS.LOG_DEBUG) + try: + with self.rpc_lock: + if self.rpc_connection == None: + self.rpc_connection = multiprocessing.connection.Client(self.rpc_addr, authkey=self.rpc_key) + self.rpc_connection.send(request) + response = self.rpc_connection.recv() + return response + + except Exception as e: + RNS.log(f"An error occurred while executing the service RPC request: {request}", RNS.LOG_ERROR) + RNS.log(f"The contained exception was: {e}", RNS.LOG_ERROR) + def getstate(self, prop, allow_cache=False): with self.state_lock: if not self.service_stopped: @@ -1611,11 +1600,7 @@ class SidebandCore(): return None else: try: - if self.rpc_connection == None: - self.rpc_connection = multiprocessing.connection.Client(self.rpc_addr, authkey=self.rpc_key) - self.rpc_connection.send({"getstate": prop}) - response = self.rpc_connection.recv() - return response + return self.service_rpc_request({"getstate": prop}) except Exception as e: RNS.log("Error while retrieving state "+str(prop)+" over RPC: "+str(e), RNS.LOG_DEBUG) @@ -1650,11 +1635,7 @@ class SidebandCore(): return self._get_plugins_info() else: try: - if self.rpc_connection == None: - self.rpc_connection = multiprocessing.connection.Client(self.rpc_addr, authkey=self.rpc_key) - self.rpc_connection.send({"get_plugins_info": True}) - response = self.rpc_connection.recv() - return response + return self.service_rpc_request({"get_plugins_info": True}) except Exception as e: ed = "Error while getting plugins info over RPC: "+str(e) RNS.log(ed, RNS.LOG_DEBUG) @@ -1689,11 +1670,7 @@ class SidebandCore(): return self._get_destination_establishment_rate(destination_hash) else: try: - if self.rpc_connection == None: - self.rpc_connection = multiprocessing.connection.Client(self.rpc_addr, authkey=self.rpc_key) - self.rpc_connection.send({"get_destination_establishment_rate": destination_hash}) - response = self.rpc_connection.recv() - return response + return self.service_rpc_request({"get_destination_establishment_rate": destination_hash}) except Exception as e: ed = "Error while getting destination link etablishment rate over RPC: "+str(e) RNS.log(ed, RNS.LOG_DEBUG) @@ -1782,6 +1759,7 @@ class SidebandCore(): except Exception as e: RNS.log("Error on client RPC connection: "+str(e), RNS.LOG_ERROR) + RNS.trace_exception(e) try: connection.close() except: @@ -4001,12 +3979,7 @@ class SidebandCore(): else: if self.is_client: try: - if self.rpc_connection == None: - self.rpc_connection = multiprocessing.connection.Client(self.rpc_addr, authkey=self.rpc_key) - - self.rpc_connection.send({"get_lxm_progress": {"lxm_hash": lxm_hash}}) - response = self.rpc_connection.recv() - return response + return self.service_rpc_request({"get_lxm_progress": {"lxm_hash": lxm_hash}}) except Exception as e: RNS.log("Error while getting LXM progress over RPC: "+str(e), RNS.LOG_DEBUG) @@ -4040,10 +4013,7 @@ class SidebandCore(): else: if self.is_client: try: - if self.rpc_connection == None: - self.rpc_connection = multiprocessing.connection.Client(self.rpc_addr, authkey=self.rpc_key) - - self.rpc_connection.send({"send_message": { + return self.service_rpc_request({"send_message": { "content": content, "destination_hash": destination_hash, "propagation": propagation, @@ -4053,8 +4023,6 @@ class SidebandCore(): "image": image, "audio": audio} }) - response = self.rpc_connection.recv() - return response except Exception as e: RNS.log("Error while sending message over RPC: "+str(e), RNS.LOG_DEBUG) @@ -4069,17 +4037,12 @@ class SidebandCore(): else: if self.is_client: try: - if self.rpc_connection == None: - self.rpc_connection = multiprocessing.connection.Client(self.rpc_addr, authkey=self.rpc_key) - - self.rpc_connection.send({"send_command": { + return self.service_rpc_request({"send_command": { "content": content, "destination_hash": destination_hash, "propagation": propagation} }) - response = self.rpc_connection.recv() - return response - + except Exception as e: RNS.log("Error while sending command over RPC: "+str(e), RNS.LOG_DEBUG) RNS.trace_exception(e) From a7b581716a677abd170d0427f74880b1faa066f0 Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Tue, 1 Oct 2024 16:24:11 +0200 Subject: [PATCH 25/37] Updated build spec --- sbapp/buildozer.spec | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sbapp/buildozer.spec b/sbapp/buildozer.spec index f664648..7ab0243 100644 --- a/sbapp/buildozer.spec +++ b/sbapp/buildozer.spec @@ -10,9 +10,9 @@ source.exclude_patterns = app_storage/*,venv/*,Makefile,./Makefil*,requirements, version.regex = __version__ = ['"](.*)['"] version.filename = %(source.dir)s/main.py -android.numeric_version = 20240925 +android.numeric_version = 20241001 -requirements = kivy==2.3.0,libbz2,pillow==10.2.0,qrcode==7.3.1,usb4a,usbserial4a,libwebp,libogg,libopus,opusfile,numpy,cryptography,ffpyplayer,codec2,pycodec2,sh,pynacl +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 android.gradle_dependencies = com.android.support:support-compat:28.0.0 #android.enable_androidx = True @@ -29,7 +29,7 @@ android.presplash_color = #00000000 orientation = portrait fullscreen = 0 -android.permissions = INTERNET,POST_NOTIFICATIONS,WAKE_LOCK,FOREGROUND_SERVICE,CHANGE_WIFI_MULTICAST_STATE,BLUETOOTH_CONNECT,ACCESS_NETWORK_STATE,ACCESS_FINE_LOCATION,ACCESS_COARSE_LOCATION,MANAGE_EXTERNAL_STORAGE,ACCESS_BACKGROUND_LOCATION,RECORD_AUDIO,REQUEST_IGNORE_BATTERY_OPTIMIZATIONS +android.permissions = INTERNET,POST_NOTIFICATIONS,WAKE_LOCK,FOREGROUND_SERVICE,CHANGE_WIFI_MULTICAST_STATE,BLUETOOTH_SCAN,BLUETOOTH_ADVERTISE,BLUETOOTH_CONNECT,ACCESS_NETWORK_STATE,ACCESS_FINE_LOCATION,ACCESS_COARSE_LOCATION,MANAGE_EXTERNAL_STORAGE,ACCESS_BACKGROUND_LOCATION,RECORD_AUDIO,REQUEST_IGNORE_BATTERY_OPTIMIZATIONS,FOREGROUND_SERVICE_CONNECTED_DEVICE android.api = 31 android.minapi = 24 From c12a6ce0ec09cdd16f37a59511183de9e5249e3a Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Tue, 1 Oct 2024 17:00:38 +0200 Subject: [PATCH 26/37] Added BLE connection toggle to RNode hardware config --- sbapp/main.py | 32 +++++++++++++++++++++++++++----- sbapp/sideband/core.py | 13 +++++++------ sbapp/ui/layouts.py | 15 +++++++++++++++ 3 files changed, 49 insertions(+), 11 deletions(-) diff --git a/sbapp/main.py b/sbapp/main.py index 8534e40..216ea54 100644 --- a/sbapp/main.py +++ b/sbapp/main.py @@ -1,6 +1,6 @@ __debug_build__ = False __disable_shaders__ = False -__version__ = "1.0.1" +__version__ = "1.1.0" __variant__ = "" import sys @@ -722,7 +722,18 @@ class SidebandApp(MDApp): if check_permission(bt_permission_name): RNS.log("Have bluetooth connect permissions", RNS.LOG_DEBUG) - self.sideband.setpersistent("permissions.bluetooth", True) + + if android_api_version > 30: + if check_permission("android.permission.BLUETOOTH_SCAN"): + RNS.log("Have bluetooth scan permissions", RNS.LOG_DEBUG) + self.sideband.setpersistent("permissions.bluetooth", True) + + else: + RNS.log("Do not have bluetooth scan permissions") + self.sideband.setpersistent("permissions.bluetooth", False) + + else: + self.sideband.setpersistent("permissions.bluetooth", True) else: RNS.log("Do not have bluetooth connect permissions") self.sideband.setpersistent("permissions.bluetooth", False) @@ -813,9 +824,9 @@ class SidebandApp(MDApp): def request_bluetooth_permissions(self): if RNS.vendor.platformutils.get_platform() == "android": - if not check_permission("android.permission.BLUETOOTH_CONNECT"): - RNS.log("Requesting bluetooth permission", RNS.LOG_DEBUG) - request_permissions(["android.permission.BLUETOOTH_CONNECT"]) + if not check_permission("android.permission.BLUETOOTH_CONNECT") or not check_permission("android.permission.BLUETOOTH_SCAN"): + RNS.log("Requesting Bluetooth permissions", RNS.LOG_DEBUG) + request_permissions(["android.permission.BLUETOOTH_CONNECT", "android.permission.BLUETOOTH_SCAN"]) self.check_bluetooth_permissions() @@ -3710,6 +3721,15 @@ class SidebandApp(MDApp): self.sideband.save_configuration() + def hardware_rnode_ble_toggle_action(self, sender=None, event=None): + if sender.active: + self.sideband.config["hw_rnode_ble"] = True + self.request_bluetooth_permissions() + else: + self.sideband.config["hw_rnode_ble"] = False + + self.sideband.save_configuration() + def hardware_rnode_framebuffer_toggle_action(self, sender=None, event=None): if sender.active: self.sideband.config["hw_rnode_enable_framebuffer"] = True @@ -3777,6 +3797,7 @@ class SidebandApp(MDApp): t_atl = "" self.hardware_rnode_screen.ids.hardware_rnode_bluetooth.active = self.sideband.config["hw_rnode_bluetooth"] + self.hardware_rnode_screen.ids.hardware_rnode_ble.active = self.sideband.config["hw_rnode_ble"] self.hardware_rnode_screen.ids.hardware_rnode_framebuffer.active = self.sideband.config["hw_rnode_enable_framebuffer"] self.hardware_rnode_screen.ids.hardware_rnode_frequency.text = t_freq self.hardware_rnode_screen.ids.hardware_rnode_bandwidth.text = t_bw @@ -3806,6 +3827,7 @@ class SidebandApp(MDApp): self.hardware_rnode_screen.ids.hardware_rnode_atl_short.bind(on_text_validate=save_connectivity) self.hardware_rnode_screen.ids.hardware_rnode_atl_long.bind(on_text_validate=save_connectivity) self.hardware_rnode_screen.ids.hardware_rnode_bluetooth.bind(active=self.hardware_rnode_bt_toggle_action) + self.hardware_rnode_screen.ids.hardware_rnode_ble.bind(active=self.hardware_rnode_ble_toggle_action) self.hardware_rnode_screen.ids.hardware_rnode_framebuffer.bind(active=self.hardware_rnode_framebuffer_toggle_action) self.hardware_rnode_ready = True diff --git a/sbapp/sideband/core.py b/sbapp/sideband/core.py index 0f68a29..fc7dd4d 100644 --- a/sbapp/sideband/core.py +++ b/sbapp/sideband/core.py @@ -1580,8 +1580,9 @@ class SidebandCore(): return response except Exception as e: - RNS.log(f"An error occurred while executing the service RPC request: {request}", RNS.LOG_ERROR) - RNS.log(f"The contained exception was: {e}", RNS.LOG_ERROR) + if not type(e) == ConnectionRefusedError: + RNS.log(f"An error occurred while executing the service RPC request: {request}", RNS.LOG_ERROR) + RNS.log(f"The contained exception was: {e}", RNS.LOG_ERROR) def getstate(self, prop, allow_cache=False): with self.state_lock: @@ -3613,12 +3614,12 @@ class SidebandCore(): RNS.log("Disallowing RNode bluetooth since config is disabled", RNS.LOG_DEBUG) rnode_allow_bluetooth = False - if self.config["hw_rnode_ble"] and self.getpersistent("permissions.ble"): + if self.config["hw_rnode_ble"] and self.getpersistent("permissions.bluetooth"): RNS.log("Allowing RNode BLE", RNS.LOG_DEBUG) - # rnode_allow_ble = True + rnode_allow_ble = True else: - RNS.log("Disallowing RNode BLE due to missing permission", RNS.LOG_DEBUG) - # rnode_allow_ble = False + RNS.log("Disallowing RNode BLE", RNS.LOG_DEBUG) + rnode_allow_ble = False else: RNS.log("Disallowing RNode bluetooth due to missing permission", RNS.LOG_DEBUG) diff --git a/sbapp/ui/layouts.py b/sbapp/ui/layouts.py index e9d41c2..7160441 100644 --- a/sbapp/ui/layouts.py +++ b/sbapp/ui/layouts.py @@ -2391,6 +2391,21 @@ MDScreen: pos_hint: {"center_y": 0.3} active: False + MDBoxLayout: + orientation: "horizontal" + size_hint_y: None + padding: [0,0,dp(24),dp(0)] + height: dp(48) + + MDLabel: + text: "Device requires BLE" + font_style: "H6" + + MDSwitch: + id: hardware_rnode_ble + pos_hint: {"center_y": 0.3} + active: False + MDLabel: id: hardware_rnode_info markup: True From 0d0267cf667acac45b0a7ad714597d384b54a67f Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Tue, 1 Oct 2024 17:30:29 +0200 Subject: [PATCH 27/37] Updated versions --- setup.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index 45b6ec1..35df592 100644 --- a/setup.py +++ b/setup.py @@ -99,8 +99,8 @@ setuptools.setup( ] }, install_requires=[ - "rns>=0.8.0", - "lxmf>=0.5.3", + "rns>=0.8.1", + "lxmf>=0.5.4", "kivy>=2.3.0", "pillow>=10.2.0", "qrcode", From ca21e4791088fa31f974c134dede2c3619c69923 Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Tue, 1 Oct 2024 23:50:58 +0200 Subject: [PATCH 28/37] Handle BLE MTU errors --- sbapp/main.py | 20 +++ sbapp/sideband/core.py | 293 ++++++++++++++++++++++------------------- sbapp/ui/layouts.py | 12 +- 3 files changed, 187 insertions(+), 138 deletions(-) diff --git a/sbapp/main.py b/sbapp/main.py index 216ea54..8ca6cf4 100644 --- a/sbapp/main.py +++ b/sbapp/main.py @@ -933,6 +933,26 @@ class SidebandApp(MDApp): else: self.service_last_available = time.time() + if RNS.vendor.platformutils.is_android(): + rnode_errors = self.sideband.getpersistent("runtime.errors.rnode") + if rnode_errors != None: + description = rnode_errors["description"] + self.sideband.setpersistent("runtime.errors.rnode", None) + yes_button = MDRectangleFlatButton( + text="OK", + font_size=dp(18), + ) + self.hw_error_dialog = MDDialog( + title="Hardware Error", + text="While connecting an RNode, Reticulum reported the following error:\n\n[i]"+str(description)+"[/i]", + buttons=[ yes_button ], + # elevation=0, + ) + def dl_yes(s): + self.hw_error_dialog.dismiss() + yes_button.bind(on_release=dl_yes) + self.hw_error_dialog.open() + if self.root.ids.screen_manager.current == "messages_screen": self.messages_view.update() diff --git a/sbapp/sideband/core.py b/sbapp/sideband/core.py index fc7dd4d..359eedb 100644 --- a/sbapp/sideband/core.py +++ b/sbapp/sideband/core.py @@ -221,6 +221,7 @@ class SidebandCore(): self.last_lxmf_announce = 0 self.last_if_change_announce = 0 self.interface_local_adding = False + self.interface_rnode_adding = False self.next_auto_announce = time.time() + 60*(random.random()*(SidebandCore.AUTO_ANNOUNCE_RANDOM_MAX-SidebandCore.AUTO_ANNOUNCE_RANDOM_MIN)+SidebandCore.AUTO_ANNOUNCE_RANDOM_MIN) try: @@ -3109,6 +3110,25 @@ class SidebandCore(): self.lxmf_announce(attached_interface=self.interface_local) threading.Thread(target=job, daemon=True).start() + if hasattr(self, "interface_rnode") and self.interface_rnode != None: + if len(self.interface_rnode.hw_errors) > 0: + self.setpersistent("runtime.errors.rnode", self.interface_rnode.hw_errors[0]) + self.interface_rnode.hw_errors = [] + + # if not self.interface_rnode_adding: + # RNS.log("Hardware error on RNodeInterface, scheduling re-init", RNS.LOG_DEBUG) + # if self.interface_rnode in RNS.Transport.interfaces: + # RNS.Transport.interfaces.remove(self.interface_rnode) + # del self.interface_rnode + # self.interface_rnode = None + # self.interface_rnode_adding = True + # def job(): + # self.__add_rnodeinterface(delay=5) + # if self.config["start_announce"] == True: + # time.sleep(12) + # self.lxmf_announce(attached_interface=self.interface_rnode) + # threading.Thread(target=job, daemon=True).start() + if (now - last_multicast_lock_check > 120): RNS.log("Checking multicast and wake locks", RNS.LOG_DEBUG) self.owner_service.take_locks() @@ -3427,6 +3447,146 @@ class SidebandCore(): self.interface_local = None self.interface_local_adding = False + def __add_rnodeinterface(self, delay=None): + self.interface_rnode_adding = True + if delay: + time.sleep(delay) + + try: + RNS.log("Adding RNode Interface...", RNS.LOG_DEBUG) + target_device = None + if len(self.owner_app.usb_devices) > 0: + # TODO: Add more intelligent selection here + target_device = self.owner_app.usb_devices[0] + + # if target_device or self.config["hw_rnode_bluetooth"]: + if target_device != None: + target_port = target_device["port"] + else: + target_port = None + + bt_device_name = None + rnode_allow_bluetooth = False + rnode_allow_ble = False + if self.getpersistent("permissions.bluetooth"): + if self.config["hw_rnode_bluetooth"]: + RNS.log("Allowing RNode bluetooth", RNS.LOG_DEBUG) + rnode_allow_bluetooth = True + if self.config["hw_rnode_bt_device"] != None: + bt_device_name = self.config["hw_rnode_bt_device"] + + else: + RNS.log("Disallowing RNode bluetooth since config is disabled", RNS.LOG_DEBUG) + rnode_allow_bluetooth = False + + if self.config["hw_rnode_ble"] and self.getpersistent("permissions.bluetooth"): + RNS.log("Allowing RNode BLE", RNS.LOG_DEBUG) + rnode_allow_ble = True + else: + RNS.log("Disallowing RNode BLE", RNS.LOG_DEBUG) + rnode_allow_ble = False + + else: + RNS.log("Disallowing RNode bluetooth due to missing permission", RNS.LOG_DEBUG) + rnode_allow_bluetooth = False + + if self.config["connect_rnode_ifac_netname"] == "": + ifac_netname = None + else: + ifac_netname = self.config["connect_rnode_ifac_netname"] + + if self.config["connect_rnode_ifac_passphrase"] == "": + ifac_netkey = None + else: + ifac_netkey = self.config["connect_rnode_ifac_passphrase"] + + if self.config["hw_rnode_atl_short"] == "": + atl_short = None + else: + atl_short = self.config["hw_rnode_atl_short"] + + if self.config["hw_rnode_atl_long"] == "": + atl_long = None + else: + atl_long = self.config["hw_rnode_atl_long"] + + 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, + ) + + 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, + ) + + rnodeinterface.OUT = True + + if RNS.Reticulum.transport_enabled(): + if_mode = Interface.Interface.MODE_FULL + if self.config["connect_ifmode_rnode"] == "gateway": + if_mode = Interface.Interface.MODE_GATEWAY + elif self.config["connect_ifmode_rnode"] == "access point": + if_mode = Interface.Interface.MODE_ACCESS_POINT + elif self.config["connect_ifmode_rnode"] == "roaming": + if_mode = Interface.Interface.MODE_ROAMING + elif self.config["connect_ifmode_rnode"] == "boundary": + if_mode = Interface.Interface.MODE_BOUNDARY + else: + if_mode = None + + self.reticulum._add_interface(rnodeinterface, mode = if_mode, ifac_netname = ifac_netname, ifac_netkey = ifac_netkey) + self.interface_rnode = rnodeinterface + self.interface_rnode_adding = False + + if rnodeinterface != None: + if len(rnodeinterface.hw_errors) > 0: + self.setpersistent("startup.errors.rnode", rnodeinterface.hw_errors[0]) + + if self.config["hw_rnode_enable_framebuffer"] == True: + if self.interface_rnode.online: + self.interface_rnode.display_image(sideband_fb_data) + self.interface_rnode.enable_external_framebuffer() + else: + self.interface_rnode.last_imagedata = sideband_fb_data + else: + if self.interface_rnode.online: + self.interface_rnode.disable_external_framebuffer() + + except Exception as e: + RNS.log("Error while adding RNode Interface. The contained exception was: "+str(e)) + self.interface_rnode = None + self.interface_rnode_adding = False + def _reticulum_log_debug(self, debug=False): self.log_verbose = debug if self.log_verbose: @@ -3587,138 +3747,7 @@ class SidebandCore(): if self.config["connect_rnode"]: self.setstate("init.loadingstate", "Starting RNode") - try: - RNS.log("Adding RNode Interface...", RNS.LOG_DEBUG) - target_device = None - if len(self.owner_app.usb_devices) > 0: - # TODO: Add more intelligent selection here - target_device = self.owner_app.usb_devices[0] - - # if target_device or self.config["hw_rnode_bluetooth"]: - if target_device != None: - target_port = target_device["port"] - else: - target_port = None - - bt_device_name = None - rnode_allow_bluetooth = False - rnode_allow_ble = False - if self.getpersistent("permissions.bluetooth"): - if self.config["hw_rnode_bluetooth"]: - RNS.log("Allowing RNode bluetooth", RNS.LOG_DEBUG) - rnode_allow_bluetooth = True - if self.config["hw_rnode_bt_device"] != None: - bt_device_name = self.config["hw_rnode_bt_device"] - - else: - RNS.log("Disallowing RNode bluetooth since config is disabled", RNS.LOG_DEBUG) - rnode_allow_bluetooth = False - - if self.config["hw_rnode_ble"] and self.getpersistent("permissions.bluetooth"): - RNS.log("Allowing RNode BLE", RNS.LOG_DEBUG) - rnode_allow_ble = True - else: - RNS.log("Disallowing RNode BLE", RNS.LOG_DEBUG) - rnode_allow_ble = False - - else: - RNS.log("Disallowing RNode bluetooth due to missing permission", RNS.LOG_DEBUG) - rnode_allow_bluetooth = False - - if self.config["connect_rnode_ifac_netname"] == "": - ifac_netname = None - else: - ifac_netname = self.config["connect_rnode_ifac_netname"] - - if self.config["connect_rnode_ifac_passphrase"] == "": - ifac_netkey = None - else: - ifac_netkey = self.config["connect_rnode_ifac_passphrase"] - - if self.config["hw_rnode_atl_short"] == "": - atl_short = None - else: - atl_short = self.config["hw_rnode_atl_short"] - - if self.config["hw_rnode_atl_long"] == "": - atl_long = None - else: - atl_long = self.config["hw_rnode_atl_long"] - - 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, - ) - - 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, - ) - - rnodeinterface.OUT = True - - if RNS.Reticulum.transport_enabled(): - if_mode = Interface.Interface.MODE_FULL - if self.config["connect_ifmode_rnode"] == "gateway": - if_mode = Interface.Interface.MODE_GATEWAY - elif self.config["connect_ifmode_rnode"] == "access point": - if_mode = Interface.Interface.MODE_ACCESS_POINT - elif self.config["connect_ifmode_rnode"] == "roaming": - if_mode = Interface.Interface.MODE_ROAMING - elif self.config["connect_ifmode_rnode"] == "boundary": - if_mode = Interface.Interface.MODE_BOUNDARY - else: - if_mode = None - - self.reticulum._add_interface(rnodeinterface, mode = if_mode, ifac_netname = ifac_netname, ifac_netkey = ifac_netkey) - self.interface_rnode = rnodeinterface - - if rnodeinterface != None: - if len(rnodeinterface.hw_errors) > 0: - self.setpersistent("startup.errors.rnode", rnodeinterface.hw_errors[0]) - - if self.config["hw_rnode_enable_framebuffer"] == True: - if self.interface_rnode.online: - self.interface_rnode.display_image(sideband_fb_data) - self.interface_rnode.enable_external_framebuffer() - else: - self.interface_rnode.last_imagedata = sideband_fb_data - else: - if self.interface_rnode.online: - self.interface_rnode.disable_external_framebuffer() - - except Exception as e: - RNS.log("Error while adding RNode Interface. The contained exception was: "+str(e)) - self.interface_rnode = None + self.__add_rnodeinterface() elif self.config["connect_serial"]: self.setstate("init.loadingstate", "Starting Serial Interface") diff --git a/sbapp/ui/layouts.py b/sbapp/ui/layouts.py index 7160441..6a466f0 100644 --- a/sbapp/ui/layouts.py +++ b/sbapp/ui/layouts.py @@ -89,13 +89,13 @@ MDNavigationLayout: on_release: root.ids.screen_manager.app.announces_action(self) - OneLineIconListItem: - text: "Local Broadcasts" - on_release: root.ids.screen_manager.app.broadcasts_action(self) + # OneLineIconListItem: + # text: "Local Broadcasts" + # on_release: root.ids.screen_manager.app.broadcasts_action(self) - IconLeftWidget: - icon: "radio-tower" - on_release: root.ids.screen_manager.app.broadcasts_action(self) + # IconLeftWidget: + # icon: "radio-tower" + # on_release: root.ids.screen_manager.app.broadcasts_action(self) OneLineIconListItem: From 86c3b18f84e4e27b746defd74d702fc672716f41 Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Sat, 5 Oct 2024 15:24:59 +0200 Subject: [PATCH 29/37] Display RNode hardware errors if available --- sbapp/main.py | 71 ++++++++++++++++++++++++++++----------------------- 1 file changed, 39 insertions(+), 32 deletions(-) diff --git a/sbapp/main.py b/sbapp/main.py index 8ca6cf4..e70df27 100644 --- a/sbapp/main.py +++ b/sbapp/main.py @@ -300,6 +300,7 @@ class SidebandApp(MDApp): self.hardware_rnode_ready = False self.hardware_modem_ready = False self.hardware_serial_ready = False + self.hw_error_dialog = None self.final_load_completed = False self.service_last_available = 0 @@ -432,22 +433,25 @@ class SidebandApp(MDApp): def check_errors(dt): if self.sideband.getpersistent("startup.errors.rnode") != None: - description = self.sideband.getpersistent("startup.errors.rnode")["description"] - self.sideband.setpersistent("startup.errors.rnode", None) - yes_button = MDRectangleFlatButton( - text="OK", - font_size=dp(18), - ) - self.hw_error_dialog = MDDialog( - title="Hardware Error", - text="When starting a connected RNode, Reticulum reported the following error:\n\n[i]"+str(description)+"[/i]", - buttons=[ yes_button ], - # elevation=0, - ) - def dl_yes(s): - self.hw_error_dialog.dismiss() - yes_button.bind(on_release=dl_yes) - self.hw_error_dialog.open() + if self.hw_error_dialog == None or (self.hw_error_dialog != None and not self.hw_error_dialog.is_open): + description = self.sideband.getpersistent("startup.errors.rnode")["description"] + self.sideband.setpersistent("startup.errors.rnode", None) + yes_button = MDRectangleFlatButton( + text="OK", + font_size=dp(18), + ) + self.hw_error_dialog = MDDialog( + title="Hardware Error", + text="When starting a connected RNode, Reticulum reported the following error:\n\n[i]"+str(description)+"[/i]", + buttons=[ yes_button ], + # elevation=0, + ) + def dl_yes(s): + self.hw_error_dialog.is_open = False + self.hw_error_dialog.dismiss() + yes_button.bind(on_release=dl_yes) + self.hw_error_dialog.open() + self.hw_error_dialog.is_open = True Clock.schedule_once(check_errors, 1.5) @@ -936,22 +940,25 @@ class SidebandApp(MDApp): if RNS.vendor.platformutils.is_android(): rnode_errors = self.sideband.getpersistent("runtime.errors.rnode") if rnode_errors != None: - description = rnode_errors["description"] - self.sideband.setpersistent("runtime.errors.rnode", None) - yes_button = MDRectangleFlatButton( - text="OK", - font_size=dp(18), - ) - self.hw_error_dialog = MDDialog( - title="Hardware Error", - text="While connecting an RNode, Reticulum reported the following error:\n\n[i]"+str(description)+"[/i]", - buttons=[ yes_button ], - # elevation=0, - ) - def dl_yes(s): - self.hw_error_dialog.dismiss() - yes_button.bind(on_release=dl_yes) - self.hw_error_dialog.open() + if self.hw_error_dialog == None or (self.hw_error_dialog != None and not self.hw_error_dialog.is_open): + description = rnode_errors["description"] + self.sideband.setpersistent("runtime.errors.rnode", None) + yes_button = MDRectangleFlatButton( + text="OK", + font_size=dp(18), + ) + self.hw_error_dialog = MDDialog( + title="Hardware Error", + text="While communicating with an RNode, Reticulum reported the following error:\n\n[i]"+str(description)+"[/i]", + buttons=[ yes_button ], + # elevation=0, + ) + def dl_yes(s): + self.hw_error_dialog.dismiss() + self.hw_error_dialog.is_open = False + yes_button.bind(on_release=dl_yes) + self.hw_error_dialog.open() + self.hw_error_dialog.is_open = True if self.root.ids.screen_manager.current == "messages_screen": From 5e15c43ab79992ae36a91bdf4cbeaea5974c18a3 Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Sun, 6 Oct 2024 11:32:27 +0200 Subject: [PATCH 30/37] Updated version --- sbapp/main.py | 2 +- sbapp/sideband/core.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sbapp/main.py b/sbapp/main.py index e70df27..4a222e9 100644 --- a/sbapp/main.py +++ b/sbapp/main.py @@ -1,6 +1,6 @@ __debug_build__ = False __disable_shaders__ = False -__version__ = "1.1.0" +__version__ = "1.1.1" __variant__ = "" import sys diff --git a/sbapp/sideband/core.py b/sbapp/sideband/core.py index 359eedb..00eaa4e 100644 --- a/sbapp/sideband/core.py +++ b/sbapp/sideband/core.py @@ -4445,7 +4445,7 @@ class SidebandCore(): thread.start() self.setstate("core.started", True) - RNS.log("Sideband Core "+str(self)+" version "+str(self.version_str)+" started") + RNS.log("Sideband Core "+str(self)+" "+str(self.version_str)+" started") def stop_webshare(self): if self.webshare_server != None: From 763db207701cfbf4506a6e9b28ac848bc273f5e0 Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Wed, 9 Oct 2024 20:10:24 +0200 Subject: [PATCH 31/37] Updated version and dependencies --- sbapp/main.py | 2 +- setup.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/sbapp/main.py b/sbapp/main.py index 4a222e9..784128d 100644 --- a/sbapp/main.py +++ b/sbapp/main.py @@ -1,6 +1,6 @@ __debug_build__ = False __disable_shaders__ = False -__version__ = "1.1.1" +__version__ = "1.1.2" __variant__ = "" import sys diff --git a/setup.py b/setup.py index 35df592..6b14f1a 100644 --- a/setup.py +++ b/setup.py @@ -99,8 +99,8 @@ setuptools.setup( ] }, install_requires=[ - "rns>=0.8.1", - "lxmf>=0.5.4", + "rns>=0.8.3", + "lxmf>=0.5.5", "kivy>=2.3.0", "pillow>=10.2.0", "qrcode", From 6654368064fb37fccd074c2a14768b162f30ca9e Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Thu, 10 Oct 2024 23:38:46 +0200 Subject: [PATCH 32/37] Updated versions --- sbapp/buildozer.spec | 2 +- sbapp/main.py | 2 +- setup.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/sbapp/buildozer.spec b/sbapp/buildozer.spec index 7ab0243..9d174fa 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 = 20241001 +android.numeric_version = 20241011 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/sbapp/main.py b/sbapp/main.py index 784128d..3184b6a 100644 --- a/sbapp/main.py +++ b/sbapp/main.py @@ -1,6 +1,6 @@ __debug_build__ = False __disable_shaders__ = False -__version__ = "1.1.2" +__version__ = "1.1.3" __variant__ = "" import sys diff --git a/setup.py b/setup.py index 6b14f1a..bd40cdc 100644 --- a/setup.py +++ b/setup.py @@ -99,7 +99,7 @@ setuptools.setup( ] }, install_requires=[ - "rns>=0.8.3", + "rns>=0.8.4", "lxmf>=0.5.5", "kivy>=2.3.0", "pillow>=10.2.0", From 605124ab13e0745cd1f8949f223d591c49992cf4 Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Fri, 11 Oct 2024 18:02:37 +0200 Subject: [PATCH 33/37] Updated Android build code --- sbapp/buildozer.spec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sbapp/buildozer.spec b/sbapp/buildozer.spec index 9d174fa..99e2d1b 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 = 20241011 +android.numeric_version = 20241012 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 From f8f1cd739b1b974d4db5d9bc3416d693bd66792d Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Fri, 11 Oct 2024 18:03:45 +0200 Subject: [PATCH 34/37] Updated versions --- sbapp/buildozer.spec | 2 +- sbapp/main.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sbapp/buildozer.spec b/sbapp/buildozer.spec index 99e2d1b..9d174fa 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 = 20241012 +android.numeric_version = 20241011 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/sbapp/main.py b/sbapp/main.py index 3184b6a..784128d 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.2" __variant__ = "" import sys From d3fec0bd741b442fb0ffec62b2574225dbbeec60 Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Fri, 11 Oct 2024 23:46:23 +0200 Subject: [PATCH 35/37] Updated Android build code --- sbapp/buildozer.spec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sbapp/buildozer.spec b/sbapp/buildozer.spec index 9d174fa..99e2d1b 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 = 20241011 +android.numeric_version = 20241012 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 From 8a8d2a77c3ac479cfe52933931ceae0938c56bb5 Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Sun, 13 Oct 2024 14:36:21 +0200 Subject: [PATCH 36/37] Updated versions --- 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 99e2d1b..4c92699 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 = 20241012 +android.numeric_version = 20241013 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 bd40cdc..bab6ed7 100644 --- a/setup.py +++ b/setup.py @@ -100,7 +100,7 @@ setuptools.setup( }, install_requires=[ "rns>=0.8.4", - "lxmf>=0.5.5", + "lxmf>=0.5.7", "kivy>=2.3.0", "pillow>=10.2.0", "qrcode", From 046cbbc30c482987d647ddd5093cbafab81301a2 Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Sun, 13 Oct 2024 14:37:45 +0200 Subject: [PATCH 37/37] Fixed stamp generation status not being displayed in some cases --- sbapp/main.py | 2 +- sbapp/sideband/core.py | 38 +++++++++++++++++++++++++++++++++----- 2 files changed, 34 insertions(+), 6 deletions(-) diff --git a/sbapp/main.py b/sbapp/main.py index 784128d..3184b6a 100644 --- a/sbapp/main.py +++ b/sbapp/main.py @@ -1,6 +1,6 @@ __debug_build__ = False __disable_shaders__ = False -__version__ = "1.1.2" +__version__ = "1.1.3" __variant__ = "" import sys diff --git a/sbapp/sideband/core.py b/sbapp/sideband/core.py index 00eaa4e..d8c7c1a 100644 --- a/sbapp/sideband/core.py +++ b/sbapp/sideband/core.py @@ -1756,6 +1756,9 @@ class SidebandCore(): elif "get_lxm_progress" in call: args = call["get_lxm_progress"] connection.send(self.get_lxm_progress(args["lxm_hash"])) + elif "get_lxm_stamp_cost" in call: + args = call["get_lxm_stamp_cost"] + connection.send(self.get_lxm_stamp_cost(args["lxm_hash"])) else: connection.send(None) @@ -4030,12 +4033,37 @@ class SidebandCore(): RNS.log("An error occurred while getting message transfer progress: "+str(e), RNS.LOG_ERROR) return None + def _service_get_lxm_stamp_cost(self, lxm_hash): + if not RNS.vendor.platformutils.is_android(): + return False + else: + if self.is_client: + try: + return self.service_rpc_request({"get_lxm_stamp_cost": { "lxm_hash": lxm_hash } }) + + except Exception as e: + RNS.log("Error while sending message over RPC: "+str(e), RNS.LOG_DEBUG) + RNS.trace_exception(e) + return False + else: + return False + def get_lxm_stamp_cost(self, lxm_hash): - try: - return self.message_router.get_outbound_lxm_stamp_cost(lxm_hash) - except Exception as e: - RNS.log("An error occurred while getting message transfer stamp cost: "+str(e), RNS.LOG_ERROR) - return None + if self.allow_service_dispatch and self.is_client: + try: + return self._service_get_lxm_stamp_cost(lxm_hash) + + except Exception as e: + RNS.log("Error while getting message transfer stamp cost: "+str(e), RNS.LOG_ERROR) + RNS.trace_exception(e) + return False + + else: + try: + return self.message_router.get_outbound_lxm_stamp_cost(lxm_hash) + except Exception as e: + RNS.log("An error occurred while getting message transfer stamp cost: "+str(e), RNS.LOG_ERROR) + return None def _service_send_message(self, content, destination_hash, propagation, skip_fields=False, no_display=False, attachment = None, image = None, audio = None): if not RNS.vendor.platformutils.is_android():