From 18cbb5dfe2d6b91ff1313a402ed5afbd625aac6b Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Fri, 30 Aug 2024 21:17:22 +0200 Subject: [PATCH] Added PTT mode. Improved notification display. --- sbapp/main.py | 364 +++++++++++-------- sbapp/plyer/platforms/linux/notification.py | 2 +- sbapp/plyer/platforms/macosx/notification.py | 2 +- sbapp/sideband/core.py | 129 ++++++- sbapp/ui/conversations.py | 28 +- sbapp/ui/messages.py | 37 +- 6 files changed, 402 insertions(+), 160 deletions(-) diff --git a/sbapp/main.py b/sbapp/main.py index 148179e..3b21980 100644 --- a/sbapp/main.py +++ b/sbapp/main.py @@ -1742,162 +1742,214 @@ class SidebandApp(MDApp): RNS.log("Error while playing message audio:"+str(e)) RNS.trace_exception(e) + def message_ptt_down_action(self, sender=None): + 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() + self.rec_dialog.recording = True + el_button = self.messages_view.ids.message_ptt_button + el_icon = self.messages_view.ids.message_ptt_button.children[0].children[1] + el_button.theme_text_color="Custom" + el_button.text_color=mdc("Orange","400") + el_button.line_color=mdc("Orange","400") + el_icon.theme_text_color="Custom" + el_icon.text_color=mdc("Orange","400") + def cb(dt): + self.msg_audio.start() + Clock.schedule_once(cb, 0.15) + + + def message_ptt_up_action(self, sender=None): + self.rec_dialog.recording = False + el_button = self.messages_view.ids.message_ptt_button + el_icon = self.messages_view.ids.message_ptt_button.children[0].children[1] + el_button.theme_text_color="Custom" + el_button.text_color=mdc("BlueGray","500") + el_button.line_color=mdc("BlueGray","500") + el_icon.theme_text_color="Custom" + el_icon.text_color=mdc("BlueGray","500") + def cb_s(dt): + self.msg_audio.stop() + self.message_process_audio() + self.message_send_action() + Clock.schedule_once(cb_s, 0.25) + + def message_process_audio(self): + if self.audio_msg_mode == LXMF.AM_OPUS_OGG: + from sideband.audioproc import voice_processing + proc_path = voice_processing(self.msg_audio._file_path) + if proc_path: + self.attach_path = proc_path + os.unlink(self.msg_audio._file_path) + RNS.log("Using voice-processed OPUS data in OGG container", RNS.LOG_DEBUG) + else: + self.attach_path = self.msg_audio._file_path + RNS.log("Using unmodified OPUS data in OGG container", RNS.LOG_DEBUG) + else: + ap_start = time.time() + from sideband.audioproc import voice_processing + proc_path = voice_processing(self.msg_audio._file_path) + + if proc_path: + opus_file = pyogg.OpusFile(proc_path) + RNS.log("Using voice-processed audio for codec2 encoding", RNS.LOG_DEBUG) + else: + opus_file = pyogg.OpusFile(self.msg_audio._file_path) + RNS.log("Using unprocessed audio data for codec2 encoding", RNS.LOG_DEBUG) + + RNS.log(f"OPUS LOAD {opus_file.frequency}Hz {opus_file.bytes_per_sample*8}bit {opus_file.channels}ch") + + audio = AudioSegment( + bytes(opus_file.as_array()), + frame_rate=opus_file.frequency, + sample_width=opus_file.bytes_per_sample, + channels=opus_file.channels, + ) + audio = audio.split_to_mono()[0] + audio = audio.apply_gain(-audio.max_dBFS) + + if self.audio_msg_mode >= LXMF.AM_CODEC2_700C and self.audio_msg_mode <= LXMF.AM_CODEC2_3200: + audio = audio.set_frame_rate(8000) + audio = audio.set_sample_width(2) + samples = audio.get_array_of_samples() + + from sideband.audioproc import encode_codec2, detect_codec2 + if detect_codec2(): + encoded = encode_codec2(samples, self.audio_msg_mode) + + ap_duration = time.time() - ap_start + RNS.log("Audio processing complete in "+RNS.prettytime(ap_duration), RNS.LOG_DEBUG) + + export_path = self.sideband.rec_cache+"/recording.enc" + with open(export_path, "wb") as export_file: + export_file.write(encoded) + self.attach_path = export_path + os.unlink(self.msg_audio._file_path) + else: + self.display_codec2_error() + return + + def message_init_rec_dialog(self): + ss = int(dp(18)) + if RNS.vendor.platformutils.is_android(): + from plyer import audio + self.request_microphone_permission() + else: + from sbapp.plyer import audio + + self.msg_audio = audio + self.msg_audio._file_path = self.sideband.rec_cache+"/recording.ogg" + + def a_rec_action(sender): + if not self.rec_dialog.recording: + RNS.log("Starting recording...") # TODO: Remove + self.rec_dialog.recording = True + el = self.rec_dialog.rec_item.children[0].children[0] + el.ttc = el.theme_text_color; el.tc = el.text_color + el.theme_text_color="Custom" + el.text_color=mdc("Red","400") + el.icon = "stop-circle" + self.rec_dialog.rec_item.text = "[size="+str(ss)+"]Stop Recording[/size]" + def cb(dt): + self.msg_audio.start() + Clock.schedule_once(cb, 0.10) + + else: + RNS.log("Stopping recording...") # TODO: Remove + self.rec_dialog.recording = False + self.rec_dialog.rec_item.text = "[size="+str(ss)+"]Start Recording[/size]" + el = self.rec_dialog.rec_item.children[0].children[0] + el.icon = "record" + el.text_color = self.theme_cls._get_text_color() + self.rec_dialog.play_item.disabled = False + self.rec_dialog.save_item.disabled = False + self.msg_audio.stop() + + self.msg_rec_a_rec = a_rec_action + + def a_play(sender): + if self.rec_dialog.recording: + a_rec_action(sender) + + if not self.rec_dialog.playing: + RNS.log("Playing recording...", RNS.LOG_DEBUG) + self.rec_dialog.playing = True + self.rec_dialog.play_item.children[0].children[0].icon = "stop" + self.rec_dialog.play_item.text = "[size="+str(ss)+"]Stop[/size]" + self.msg_audio.play() + else: + RNS.log("Stopping playback...", RNS.LOG_DEBUG) + self.rec_dialog.playing = False + self.rec_dialog.play_item.children[0].children[0].icon = "play" + self.rec_dialog.play_item.text = "[size="+str(ss)+"]Play[/size]" + self.msg_audio.stop() + + self.msg_rec_a_play = a_play + + def a_finished(sender): + RNS.log("Playback finished", RNS.LOG_DEBUG) + self.rec_dialog.playing = False + self.rec_dialog.play_item.children[0].children[0].icon = "play" + self.rec_dialog.play_item.text = "[size="+str(ss)+"]Play[/size]" + + self.msg_audio._finished_callback = a_finished + + def a_save(sender): + if self.rec_dialog.recording: + a_rec_action(sender) + self.rec_dialog_is_open = False + self.rec_dialog.dismiss() + + try: + if self.audio_msg_mode == LXMF.AM_OPUS_OGG: + from sideband.audioproc import voice_processing + proc_path = voice_processing(self.msg_audio._file_path) + if proc_path: + self.attach_path = proc_path + os.unlink(self.msg_audio._file_path) + RNS.log("Using voice-processed OPUS data in OGG container", RNS.LOG_DEBUG) + else: + self.attach_path = self.msg_audio._file_path + RNS.log("Using unmodified OPUS data in OGG container", RNS.LOG_DEBUG) + else: + self.message_process_audio() + + self.update_message_widgets() + toast("Added recorded audio to message") + + except Exception as e: + RNS.trace_exception(e) + + self.msg_rec_a_save = a_save + + cancel_button = MDRectangleFlatButton(text="Cancel", font_size=dp(18)) + rec_item = DialogItem(IconLeftWidget(icon="record", on_release=a_rec_action), text="[size="+str(ss)+"]Start Recording[/size]", on_release=a_rec_action) + play_item = DialogItem(IconLeftWidget(icon="play", on_release=a_play), text="[size="+str(ss)+"]Play[/size]", on_release=a_play, disabled=True) + save_item = DialogItem(IconLeftWidget(icon="content-save-move-outline", on_release=a_save), text="[size="+str(ss)+"]Save to message[/size]", on_release=a_save, disabled=True) + self.rec_dialog = MDDialog( + title="Record Audio", + type="simple", + # text="Test\n", + items=[ + rec_item, + play_item, + save_item, + ], + buttons=[ cancel_button ], + width_offset=dp(12), + ) + cancel_button.bind(on_release=self.rec_dialog.dismiss) + self.rec_dialog.recording = False + self.rec_dialog.playing = False + self.rec_dialog.rec_item = rec_item + self.rec_dialog.play_item = play_item + self.rec_dialog.save_item = save_item def message_record_audio_action(self): ss = int(dp(18)) if self.rec_dialog == None: - if RNS.vendor.platformutils.is_android(): - from plyer import audio - self.request_microphone_permission() - else: - from sbapp.plyer import audio - - self.msg_audio = audio - self.msg_audio._file_path = self.sideband.rec_cache+"/recording.ogg" - - def a_rec_action(sender): - if not self.rec_dialog.recording: - RNS.log("Starting recording...") # TODO: Remove - self.rec_dialog.recording = True - el = self.rec_dialog.rec_item.children[0].children[0] - el.ttc = el.theme_text_color; el.tc = el.text_color - el.theme_text_color="Custom" - el.text_color=mdc("Red","400") - el.icon = "stop-circle" - self.rec_dialog.rec_item.text = "[size="+str(ss)+"]Stop Recording[/size]" - def cb(dt): - self.msg_audio.start() - Clock.schedule_once(cb, 0.15) - - else: - RNS.log("Stopping recording...") # TODO: Remove - self.rec_dialog.recording = False - self.rec_dialog.rec_item.text = "[size="+str(ss)+"]Start Recording[/size]" - el = self.rec_dialog.rec_item.children[0].children[0] - el.icon = "record" - el.text_color = self.theme_cls._get_text_color() - self.rec_dialog.play_item.disabled = False - self.rec_dialog.save_item.disabled = False - self.msg_audio.stop() - - self.msg_rec_a_rec = a_rec_action - - def a_play(sender): - if self.rec_dialog.recording: - a_rec_action(sender) - - if not self.rec_dialog.playing: - RNS.log("Playing recording...", RNS.LOG_DEBUG) - self.rec_dialog.playing = True - self.rec_dialog.play_item.children[0].children[0].icon = "stop" - self.rec_dialog.play_item.text = "[size="+str(ss)+"]Stop[/size]" - self.msg_audio.play() - else: - RNS.log("Stopping playback...", RNS.LOG_DEBUG) - self.rec_dialog.playing = False - self.rec_dialog.play_item.children[0].children[0].icon = "play" - self.rec_dialog.play_item.text = "[size="+str(ss)+"]Play[/size]" - self.msg_audio.stop() - - self.msg_rec_a_play = a_play - - def a_finished(sender): - RNS.log("Playback finished", RNS.LOG_DEBUG) - self.rec_dialog.playing = False - self.rec_dialog.play_item.children[0].children[0].icon = "play" - self.rec_dialog.play_item.text = "[size="+str(ss)+"]Play[/size]" - - self.msg_audio._finished_callback = a_finished - - def a_save(sender): - if self.rec_dialog.recording: - a_rec_action(sender) - self.rec_dialog_is_open = False - self.rec_dialog.dismiss() - - try: - if self.audio_msg_mode == LXMF.AM_OPUS_OGG: - from sideband.audioproc import voice_processing - proc_path = voice_processing(self.msg_audio._file_path) - if proc_path: - self.attach_path = proc_path - os.unlink(self.msg_audio._file_path) - RNS.log("Using voice-processed OPUS data in OGG container", RNS.LOG_DEBUG) - else: - self.attach_path = self.msg_audio._file_path - RNS.log("Using unmodified OPUS data in OGG container", RNS.LOG_DEBUG) - else: - ap_start = time.time() - from sideband.audioproc import voice_processing - proc_path = voice_processing(self.msg_audio._file_path) - - if proc_path: - opus_file = pyogg.OpusFile(proc_path) - RNS.log("Using voice-processed audio for codec2 encoding", RNS.LOG_DEBUG) - else: - opus_file = pyogg.OpusFile(self.msg_audio._file_path) - RNS.log("Using unprocessed audio data for codec2 encoding", RNS.LOG_DEBUG) - - audio = AudioSegment( - bytes(opus_file.as_array()), - frame_rate=opus_file.frequency, - sample_width=opus_file.bytes_per_sample, - channels=opus_file.channels, - ) - audio = audio.split_to_mono()[0] - audio = audio.apply_gain(-audio.max_dBFS) - - if self.audio_msg_mode >= LXMF.AM_CODEC2_700C and self.audio_msg_mode <= LXMF.AM_CODEC2_3200: - audio = audio.set_frame_rate(8000) - audio = audio.set_sample_width(2) - samples = audio.get_array_of_samples() - - from sideband.audioproc import encode_codec2, detect_codec2 - if detect_codec2(): - encoded = encode_codec2(samples, self.audio_msg_mode) - - ap_duration = time.time() - ap_start - RNS.log("Audio processing complete in "+RNS.prettytime(ap_duration), RNS.LOG_DEBUG) - - export_path = self.sideband.rec_cache+"/recording.enc" - with open(export_path, "wb") as export_file: - export_file.write(encoded) - self.attach_path = export_path - os.unlink(self.msg_audio._file_path) - else: - self.display_codec2_error() - return - - self.update_message_widgets() - toast("Added recorded audio to message") - - except Exception as e: - RNS.trace_exception(e) - - self.msg_rec_a_save = a_save - - cancel_button = MDRectangleFlatButton(text="Cancel", font_size=dp(18)) - rec_item = DialogItem(IconLeftWidget(icon="record", on_release=a_rec_action), text="[size="+str(ss)+"]Start Recording[/size]", on_release=a_rec_action) - play_item = DialogItem(IconLeftWidget(icon="play", on_release=a_play), text="[size="+str(ss)+"]Play[/size]", on_release=a_play, disabled=True) - save_item = DialogItem(IconLeftWidget(icon="content-save-move-outline", on_release=a_save), text="[size="+str(ss)+"]Save to message[/size]", on_release=a_save, disabled=True) - self.rec_dialog = MDDialog( - title="Record Audio", - type="simple", - # text="Test\n", - items=[ - rec_item, - play_item, - save_item, - ], - buttons=[ cancel_button ], - width_offset=dp(12), - ) - cancel_button.bind(on_release=self.rec_dialog.dismiss) - self.rec_dialog.recording = False - self.rec_dialog.playing = False - self.rec_dialog.rec_item = rec_item - self.rec_dialog.play_item = play_item - self.rec_dialog.save_item = save_item + self.message_init_rec_dialog() else: self.rec_dialog.play_item.disabled = True @@ -1910,7 +1962,7 @@ class SidebandApp(MDApp): self.rec_dialog_is_open = True self.rec_dialog.update_width() - def message_attach_action(self, attach_type=None): + def message_attach_action(self, attach_type=None, nodialog=False): file_attach_types = ["lbimg", "defimg", "hqimg", "file"] rec_attach_types = ["audio"] @@ -1918,10 +1970,12 @@ class SidebandApp(MDApp): self.rec_dialog_is_open = False if attach_type in file_attach_types: self.attach_type = attach_type - self.message_select_file_action() + if not nodialog: + self.message_select_file_action() elif attach_type in rec_attach_types: self.attach_type = attach_type - self.message_record_audio_action() + if not nodialog: + self.message_record_audio_action() def message_attachment_action(self, sender): self.rec_dialog_is_open = False diff --git a/sbapp/plyer/platforms/linux/notification.py b/sbapp/plyer/platforms/linux/notification.py index 65f5131..552519e 100644 --- a/sbapp/plyer/platforms/linux/notification.py +++ b/sbapp/plyer/platforms/linux/notification.py @@ -63,7 +63,7 @@ class NotifyDbus(Notification): def _notify(self, **kwargs): summary = kwargs.get('title', "title") body = kwargs.get('message', "body") - app_name = kwargs.get('app_name', '') + app_name = "Sideband" app_icon = kwargs.get('app_icon', '') timeout = kwargs.get('timeout', 10) actions = kwargs.get('actions', []) diff --git a/sbapp/plyer/platforms/macosx/notification.py b/sbapp/plyer/platforms/macosx/notification.py index 35ce67a..6a94bbd 100644 --- a/sbapp/plyer/platforms/macosx/notification.py +++ b/sbapp/plyer/platforms/macosx/notification.py @@ -26,7 +26,7 @@ class OSXNotification(Notification): def _notify(self, **kwargs): title = kwargs.get('title', '') message = kwargs.get('message', '') - app_name = kwargs.get('app_name', '') + app_name = "Sideband" # app_icon, timeout, ticker are not supported (yet) notification = NSUserNotification.alloc().init() diff --git a/sbapp/sideband/core.py b/sbapp/sideband/core.py index fcf2512..e958380 100644 --- a/sbapp/sideband/core.py +++ b/sbapp/sideband/core.py @@ -112,6 +112,8 @@ class SidebandCore(): self.is_service = is_service self.is_client = is_client self.is_daemon = is_daemon + self.msg_audio = None + self.last_msg_audio = None self.db = None if not self.is_service and not self.is_client: @@ -938,6 +940,25 @@ class SidebandCore(): RNS.log("Error while checking trust for "+RNS.prettyhexrep(context_dest)+": "+str(e), RNS.LOG_ERROR) return False + def ptt_enabled(self, context_dest, conv_data = None): + try: + if conv_data == None: + existing_conv = self._db_conversation(context_dest) + else: + existing_conv = conv_data + + if existing_conv != None: + data_dict = existing_conv["data"] + if data_dict != None: + if "ptt_enabled" in data_dict: + return data_dict["ptt_enabled"] + + return False + + except Exception as e: + RNS.log("Error while checking PTT-enabled for "+RNS.prettyhexrep(context_dest)+": "+str(e), RNS.LOG_ERROR) + return False + def should_send_telemetry(self, context_dest, conv_data=None): try: if self.config["telemetry_enabled"]: @@ -1092,6 +1113,9 @@ class SidebandCore(): def conversation_set_object(self, context_dest, is_object): self._db_conversation_set_object(context_dest, is_object) + def conversation_set_ptt_enabled(self, context_dest, ptt_enabled): + self._db_conversation_set_ptt_enabled(context_dest, ptt_enabled) + def send_telemetry_in_conversation(self, context_dest): self._db_conversation_set_telemetry(context_dest, True) @@ -2069,6 +2093,32 @@ class SidebandCore(): RNS.log("Retrying operation...", RNS.LOG_ERROR) self._db_conversation_set_object(context_dest, is_object, is_retry=True) + def _db_conversation_set_ptt_enabled(self, context_dest, ptt_enabled=False): + conv = self._db_conversation(context_dest) + data_dict = conv["data"] + if data_dict == None: + data_dict = {} + + data_dict["ptt_enabled"] = ptt_enabled + packed_dict = msgpack.packb(data_dict) + + db = self.__db_connect() + dbc = db.cursor() + + query = "UPDATE conv set data = ? where dest_context = ?" + data = (packed_dict, context_dest) + dbc.execute(query, data) + result = dbc.fetchall() + + try: + db.commit() + except Exception as e: + RNS.log("An error occurred while updating conversation PTT option: "+str(e), RNS.LOG_ERROR) + self.__db_reconnect() + if not is_retry: + RNS.log("Retrying operation...", RNS.LOG_ERROR) + self._db_conversation_set_ptt_enabled(context_dest, ptt_enabled, is_retry=True) + def _db_conversation_set_trusted(self, context_dest, trusted): db = self.__db_connect() dbc = db.cursor() @@ -3761,6 +3811,7 @@ class SidebandCore(): def lxm_ingest(self, message, originator = False): should_notify = False is_trusted = False + ptt_enabled = False telemetry_only = False own_command = False unread_reason_tx = False @@ -3771,6 +3822,7 @@ class SidebandCore(): else: context_dest = message.source_hash is_trusted = self.is_trusted(context_dest) + ptt_enabled = self.ptt_enabled(context_dest) if originator and LXMF.FIELD_COMMANDS in message.fields: own_command = True @@ -3813,6 +3865,74 @@ class SidebandCore(): if self.gui_display() == "conversations_screen" and self.gui_foreground(): should_notify = False + ### PTT ####################################################################### + if not originator and LXMF.FIELD_AUDIO in message.fields and ptt_enabled: + if self.msg_audio == None: + if RNS.vendor.platformutils.is_android(): + from plyer import audio + else: + from sbapp.plyer import audio + + RNS.log("Audio init done") + self.msg_audio = audio + try: + temp_path = None + audio_field = message.fields[LXMF.FIELD_AUDIO] + if self.last_msg_audio != audio_field[1]: + RNS.log("Reloading audio source", RNS.LOG_DEBUG) + if len(audio_field[1]) > 10: + self.last_msg_audio = audio_field[1] + else: + self.last_msg_audio = None + return + + if audio_field[0] == LXMF.AM_OPUS_OGG: + temp_path = self.rec_cache+"/msg.ogg" + with open(temp_path, "wb") as af: + af.write(self.last_msg_audio) + + elif audio_field[0] >= LXMF.AM_CODEC2_700C and audio_field[0] <= LXMF.AM_CODEC2_3200: + temp_path = self.rec_cache+"/msg.ogg" + from sideband.audioproc import samples_to_ogg, decode_codec2, detect_codec2 + + target_rate = 8000 + if RNS.vendor.platformutils.is_linux(): + target_rate = 48000 + + if detect_codec2(): + if samples_to_ogg(decode_codec2(audio_field[1], audio_field[0]), temp_path, input_rate=8000, output_rate=target_rate): + RNS.log("Wrote OGG file to: "+temp_path, RNS.LOG_DEBUG) + else: + RNS.log("OGG write failed", RNS.LOG_DEBUG) + else: + self.last_msg_audio = None + return + + else: + # Unimplemented audio type + pass + + self.msg_sound = self.msg_audio + self.msg_sound._file_path = temp_path + self.msg_sound.reload() + + if self.msg_sound != None and self.msg_sound.playing(): + RNS.log("Stopping playback", RNS.LOG_DEBUG) + self.msg_sound.stop() + else: + if self.msg_sound != None: + RNS.log("Starting playback", RNS.LOG_DEBUG) + self.msg_sound.play() + should_notify = False + else: + RNS.log("Playback was requested, but no audio data was loaded for playback", RNS.LOG_ERROR) + + except Exception as e: + RNS.log("Error while playing message audio:"+str(e)) + RNS.trace_exception(e) + ############################################################################### + + if self.is_client: should_notify = False @@ -3824,7 +3944,14 @@ class SidebandCore(): text = message.content.decode("utf-8") notification_content = text[:nlen] if len(text) > nlen: - text += "..." + notification_content += " [...]" + + if len(text) < 2 and LXMF.FIELD_AUDIO in message.fields: + notification_content = "Audio message" + if len(text) < 2 and LXMF.FIELD_IMAGE in message.fields: + notification_content = "Image" + if len(text) < 2 and LXMF.FIELD_FILE_ATTACHMENTS in message.fields: + notification_content = "File attachment" self.notify(title=self.peer_display_name(context_dest), content=notification_content, group="LXM", context_id=RNS.hexrep(context_dest, delimit=False)) diff --git a/sbapp/ui/conversations.py b/sbapp/ui/conversations.py index c48d5bf..4427af3 100644 --- a/sbapp/ui/conversations.py +++ b/sbapp/ui/conversations.py @@ -36,6 +36,7 @@ class ConvSettings(BoxLayout): telemetry = BooleanProperty() allow_requests = BooleanProperty() is_object = BooleanProperty() + ptt_enabled = BooleanProperty() class Conversations(): def __init__(self, app): @@ -192,6 +193,7 @@ class Conversations(): if not context_dest in self.added_item_dests: existing_conv = self.app.sideband._db_conversation(context_dest) is_object = self.app.sideband.is_object(context_dest, conv_data=existing_conv) + ptt_enabled = self.app.sideband.ptt_enabled(context_dest, conv_data=existing_conv) iconl = self.get_icon(conv) item = OneLineAvatarIconListItem(text=peer_disp_name, on_release=self.app.conversation_action) item.add_widget(iconl) @@ -210,13 +212,14 @@ class Conversations(): disp_name = self.app.sideband.raw_display_name(dest) is_trusted = self.app.sideband.is_trusted(dest, conv_data=cd) is_object = self.app.sideband.is_object(dest, conv_data=cd) + ptt_enabled = self.app.sideband.ptt_enabled(dest, conv_data=cd) send_telemetry = self.app.sideband.should_send_telemetry(dest, conv_data=cd) allow_requests = self.app.sideband.requests_allowed_from(dest, conv_data=cd) yes_button = MDRectangleFlatButton(text="Save",font_size=dp(18), theme_text_color="Custom", line_color=self.app.color_accept, text_color=self.app.color_accept) no_button = MDRectangleFlatButton(text="Cancel",font_size=dp(18)) dialog_content = ConvSettings(disp_name=disp_name, context_dest=RNS.hexrep(dest, delimit=False), trusted=is_trusted, - telemetry=send_telemetry, allow_requests=allow_requests, is_object=is_object) + telemetry=send_telemetry, allow_requests=allow_requests, is_object=is_object, ptt_enabled=ptt_enabled) dialog_content.ids.name_field.font_name = self.app.input_font dialog = MDDialog( @@ -235,6 +238,7 @@ class Conversations(): telemetry = dialog.d_content.ids["telemetry_switch"].active allow_requests = dialog.d_content.ids["allow_requests_switch"].active conv_is_object = dialog.d_content.ids["is_object_switch"].active + ptt_is_enabled = dialog.d_content.ids["ptt_enabled_switch"].active if trusted: self.app.sideband.trusted_conversation(dest) else: @@ -255,6 +259,13 @@ class Conversations(): else: self.app.sideband.conversation_set_object(dest, False) + if ptt_is_enabled: + RNS.log("Setting PTT enabled") + self.app.sideband.conversation_set_ptt_enabled(dest, True) + else: + RNS.log("Setting PTT disabled") + self.app.sideband.conversation_set_ptt_enabled(dest, False) + self.app.sideband.named_conversation(name, dest) except Exception as e: @@ -604,6 +615,21 @@ Builder.load_string(""" pos_hint: {"center_y": 0.43} active: root.allow_requests + MDBoxLayout: + orientation: "horizontal" + size_hint_y: None + padding: [0,0,dp(8),0] + height: dp(32) + MDLabel: + id: ptt_enabled_label + text: "PTT Enabled" + font_style: "H6" + + MDSwitch: + id: ptt_enabled_switch + pos_hint: {"center_y": 0.43} + active: root.ptt_enabled + MDBoxLayout: orientation: "horizontal" size_hint_y: None diff --git a/sbapp/ui/messages.py b/sbapp/ui/messages.py index 6dcbd67..1d8d8b3 100644 --- a/sbapp/ui/messages.py +++ b/sbapp/ui/messages.py @@ -57,6 +57,7 @@ class Messages(): self.context_dest = context_dest self.source_dest = context_dest self.is_trusted = self.app.sideband.is_trusted(self.context_dest) + self.ptt_enabled = self.app.sideband.ptt_enabled(self.context_dest) self.screen = self.app.root.ids.screen_manager.get_screen("messages_screen") self.ids = self.screen.ids @@ -216,6 +217,15 @@ class Messages(): w.dmenu.items.append(w.dmenu.retry_item) + def hide_widget(self, wid, dohide=True): + if hasattr(wid, 'saved_attrs'): + if not dohide: + wid.height, wid.size_hint_y, wid.opacity, wid.disabled = wid.saved_attrs + del wid.saved_attrs + elif dohide: + wid.saved_attrs = wid.height, wid.size_hint_y, wid.opacity, wid.disabled + wid.height, wid.size_hint_y, wid.opacity, wid.disabled = 0, None, 0, True + def update_widget(self): if self.app.sideband.config["dark_ui"]: intensity_msgs = intensity_msgs_dark @@ -228,6 +238,11 @@ class Messages(): self.ids.message_text.font_name = self.app.input_font + if self.ptt_enabled: + self.hide_widget(self.ids.message_ptt, False) + else: + self.hide_widget(self.ids.message_ptt, True) + if self.loading_earlier_messages: self.new_messages.reverse() @@ -1020,7 +1035,27 @@ MDScreen: icon: "key-wireless" text: "Query Network For Keys" on_release: root.app.key_query_action(self) - + + BoxLayout: + id: message_ptt + padding: [dp(16), dp(8), dp(16), dp(8)] + spacing: dp(24) + size_hint_y: None + height: self.minimum_height + + MDRectangleFlatIconButton: + id: message_ptt_button + icon: "microphone" + text: "PTT" + size_hint_x: 1.0 + padding: [dp(10), dp(13), dp(10), dp(14)] + icon_size: dp(24) + font_size: dp(16) + on_press: root.app.message_ptt_down_action(self) + on_release: root.app.message_ptt_up_action(self) + _no_ripple_effect: True + background_normal: "" + background_down: "" BoxLayout: id: message_input_part