From b1678a1532f6eaf584b56c7ea4e38c759965ba5f Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Mon, 10 Mar 2025 17:25:40 +0100 Subject: [PATCH] Voice call UI additions --- sbapp/main.py | 31 ++++++++++++++++++++++++++++++- sbapp/sideband/core.py | 27 ++++++++++++++++++++++++++- sbapp/sideband/voice.py | 5 ++++- sbapp/ui/voice.py | 4 +++- 4 files changed, 63 insertions(+), 4 deletions(-) diff --git a/sbapp/main.py b/sbapp/main.py index c816eb8..15f136b 100644 --- a/sbapp/main.py +++ b/sbapp/main.py @@ -1096,6 +1096,10 @@ class SidebandApp(MDApp): self.hw_error_dialog.open() self.hw_error_dialog.is_open = True + incoming_call = self.sideband.getstate("voice.incoming_call") + if incoming_call: + self.sideband.setstate("voice.incoming_call", None) + toast(f"Call from {incoming_call}", duration=7) if self.root.ids.screen_manager.current == "messages_screen": self.messages_view.update() @@ -1370,6 +1374,15 @@ class SidebandApp(MDApp): if text == "o": self.objects_action() + if text == "e": + self.voice_action() + + if text == " ": + self.voice_answer_action() + + if text == ".": + self.voice_reject_action() + if text == "r": if self.root.ids.screen_manager.current == "conversations_screen": if self.include_objects: @@ -5309,6 +5322,16 @@ class SidebandApp(MDApp): def dial_action(self, identity_hash): self.voice_action(dial_on_complete=identity_hash) + def voice_answer_action(self, sender=None): + if self.sideband.voice_running: + if self.sideband.telephone.is_ringing: self.sideband.telephone.answer() + + def voice_reject_action(self, sender=None): + if self.sideband.voice_running: + if self.sideband.telephone.is_ringing or self.sideband.telephone.is_in_call: + self.sideband.telephone.hangup() + toast("Call ended") + ### Telemetry Screen ###################################### @@ -6241,15 +6264,21 @@ 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 & PTT[/b] + [b]Voice & PTT Messages[/b] - [b]Space[/b] Start/stop recording - [b]Enter[/b] Save recording to message - With PTT enabled, hold [b]Space[/b] to talk + [b]Voice Calls[/b] + - [b]Ctrl-Space[/b] Answer incoming call + - [b]Ctrl-.[/b] Reject incoming call + - [b]Ctrl-.[/b] Hang up active call + [b]Navigation[/b] - [b]Ctrl-[i]n[/i][/b] Go to conversation number [i]n[/i] - [b]Ctrl-R[/b] Go to Conversations - [b]Ctrl-O[/b] Go to Objects & Devices + - [b]Ctrl-E[/b] Go to Voice - [b]Ctrl-L[/b] Go to Announce Stream - [b]Ctrl-M[/b] Go to Situation Map - [b]Ctrl-U[/b] Go to Utilities diff --git a/sbapp/sideband/core.py b/sbapp/sideband/core.py index 291a437..168cb7c 100644 --- a/sbapp/sideband/core.py +++ b/sbapp/sideband/core.py @@ -1224,6 +1224,27 @@ class SidebandCore(): RNS.log("Could not decode a valid peer name from data: "+str(e), RNS.LOG_DEBUG) return RNS.prettyhexrep(context_dest) + def voice_display_name(self, identity_hash): + context_dest = identity_hash + if context_dest == self.lxmf_destination.hash: + return self.config["display_name"] + + try: + lxmf_destination_hash = RNS.Destination.hash_from_name_and_identity("lxmf.delivery", identity_hash) + existing_voice = self._db_conversation(context_dest) + existing_lxmf = self._db_conversation(lxmf_destination_hash) + + print(RNS.prettyhexrep(lxmf_destination_hash)) + print(f"VOICE {existing_voice}") + print(f"LXMF {existing_lxmf}") + + if existing_lxmf: return self.peer_display_name(lxmf_destination_hash) + else: return self.peer_display_name(identity_hash) + + except Exception as e: + RNS.log("Could not decode a valid peer name from data: "+str(e), RNS.LOG_DEBUG) + return RNS.prettyhexrep(context_dest) + def clear_conversation(self, context_dest): self._db_clear_conversation(context_dest) @@ -5220,7 +5241,7 @@ class SidebandCore(): RNS.log("Starting voice service", RNS.LOG_DEBUG) self.voice_running = True from .voice import ReticulumTelephone - self.telephone = ReticulumTelephone(self.identity) + self.telephone = ReticulumTelephone(self.identity, owner=self) ringtone_path = os.path.join(self.asset_dir, "audio", "notifications", "soft1.opus") self.telephone.set_ringtone(ringtone_path) @@ -5244,6 +5265,10 @@ class SidebandCore(): RNS.log(f"An error occurred while stopping voice services, the contained exception was: {e}", RNS.LOG_ERROR) RNS.trace_exception(e) + def incoming_call(self, remote_identity): + display_name = self.voice_display_name(remote_identity.hash) + self.setstate("voice.incoming_call", display_name) + rns_config = """# This template is used to generate a # running configuration for Sideband's # internal RNS instance. Incorrect changes diff --git a/sbapp/sideband/voice.py b/sbapp/sideband/voice.py index eb346f0..3754fc1 100644 --- a/sbapp/sideband/voice.py +++ b/sbapp/sideband/voice.py @@ -22,9 +22,10 @@ class ReticulumTelephone(): WAIT_TIME = 60 PATH_TIME = 10 - def __init__(self, identity, verbosity = 0, service = False): + def __init__(self, identity, owner = None, service = False): self.identity = identity self.service = service + self.owner = owner self.config = None self.should_run = False self.telephone = None @@ -124,6 +125,8 @@ class ReticulumTelephone(): self.caller = remote_identity self.direction = "from" if self.direction == None else "to" RNS.log(f"Incoming call from {RNS.prettyhexrep(self.caller.hash)}", RNS.LOG_DEBUG) + if self.owner: + self.owner.incoming_call(remote_identity) def call_ended(self, remote_identity): if self.is_in_call or self.is_ringing or self.call_is_connecting: diff --git a/sbapp/ui/voice.py b/sbapp/ui/voice.py index f07fc04..fa00ea0 100644 --- a/sbapp/ui/voice.py +++ b/sbapp/ui/voice.py @@ -54,7 +54,9 @@ class Voice(): if self.app.root.ids.screen_manager.current == "voice_screen": if self.ui_updater == None: self.ui_updater = Clock.schedule_interval(self.update_call_status, 0.5) else: - if self.ui_updater: self.ui_updater.cancel() + if self.ui_updater: + self.ui_updater.cancel() + self.ui_updater = None db = self.screen.ids.dial_button ih = self.screen.ids.identity_hash