diff --git a/sbapp/main.py b/sbapp/main.py index 32a7738..46d351d 100644 --- a/sbapp/main.py +++ b/sbapp/main.py @@ -1570,6 +1570,11 @@ class SidebandApp(MDApp): self.sideband.save_configuration() self.update_ui_theme() + def save_display_style_in_contact_list(sender=None, event=None): + self.sideband.config["display_style_in_contact_list"] = self.settings_screen.ids.display_style_in_contact_list.active + self.sideband.save_configuration() + self.sideband.setstate("wants.viewupdate.conversations", True) + def save_advanced_stats(sender=None, event=None): self.sideband.config["advanced_stats"] = self.settings_screen.ids.settings_advanced_statistics.active self.sideband.save_configuration() @@ -1679,6 +1684,9 @@ class SidebandApp(MDApp): self.settings_screen.ids.settings_eink_mode.active = self.sideband.config["eink_mode"] self.settings_screen.ids.settings_eink_mode.bind(active=save_eink_mode) + self.settings_screen.ids.display_style_in_contact_list.active = self.sideband.config["display_style_in_contact_list"] + self.settings_screen.ids.display_style_in_contact_list.bind(active=save_display_style_in_contact_list) + self.settings_screen.ids.settings_advanced_statistics.active = self.sideband.config["advanced_stats"] self.settings_screen.ids.settings_advanced_statistics.bind(active=save_advanced_stats) diff --git a/sbapp/sideband/core.py b/sbapp/sideband/core.py index b96f6d3..627f40d 100644 --- a/sbapp/sideband/core.py +++ b/sbapp/sideband/core.py @@ -406,6 +406,8 @@ class SidebandCore(): self.config["print_command"] = "lp" if not "eink_mode" in self.config: self.config["eink_mode"] = False + if not "display_style_in_contact_list" in self.config: + self.config["display_style_in_contact_list"] = False if not "connect_transport" in self.config: self.config["connect_transport"] = False @@ -514,6 +516,8 @@ class SidebandCore(): self.config["telemetry_send_interval"] = 43200 if not "telemetry_request_interval" in self.config: self.config["telemetry_request_interval"] = 43200 + if not "telemetry_collector_enabled" in self.config: + self.config["telemetry_collector_enabled"] = False if not "telemetry_icon" in self.config: self.config["telemetry_icon"] = SidebandCore.DEFAULT_APPEARANCE[0] @@ -785,8 +789,8 @@ class SidebandCore(): RNS.log("Error while getting peer name: "+str(e), RNS.LOG_ERROR) return "" - def peer_appearance(self, context_dest): - appearance = self._db_get_appearance(context_dest) + def peer_appearance(self, context_dest, conv=None): + appearance = self._db_get_appearance(context_dest, conv=conv) if appearance == None: return SidebandCore.DEFAULT_APPEARANCE for e in appearance: @@ -924,7 +928,7 @@ class SidebandCore(): def request_latest_telemetry(self, from_addr=None): - if from_addr == None: + if from_addr == None or from_addr == self.lxmf_destination.hash: return "no_address" else: if self.getstate(f"telemetry.{RNS.hexrep(from_addr, delimit=False)}.request_sending") == True: @@ -974,7 +978,7 @@ class SidebandCore(): def send_latest_telemetry(self, to_addr=None, stream=None): - if to_addr == None: + if to_addr == None or to_addr == self.lxmf_destination.hash: return "no_address" else: if self.getstate(f"telemetry.{RNS.hexrep(to_addr, delimit=False)}.update_sending") == True: @@ -1633,11 +1637,13 @@ class SidebandCore(): result = dbc.fetchall() db.commit() - def _db_get_appearance(self, context_dest): + def _db_get_appearance(self, context_dest, conv = None): if context_dest == self.lxmf_destination.hash: return [self.config["telemetry_icon"], self.config["telemetry_fg"], self.config["telemetry_bg"]] else: - conv = self._db_conversation(context_dest) + if conv == None: + conv = self._db_conversation(context_dest) + if conv != None and "data" in conv: data_dict = conv["data"] try: @@ -1728,6 +1734,11 @@ class SidebandCore(): last_rx = entry[1] last_tx = entry[2] last_activity = max(last_rx, last_tx) + data = None + try: + data = msgpack.unpackb(entry[7]) + except: + pass conv = { "dest": entry[0], @@ -1735,6 +1746,8 @@ class SidebandCore(): "last_rx": last_rx, "last_tx": last_tx, "last_activity": last_activity, + "trust": entry[5], + "data": data, } convs.append(conv) @@ -2027,7 +2040,7 @@ class SidebandCore(): packed_telemetry = self._db_save_telemetry(context_dest, lxm.fields[LXMF.FIELD_TELEMETRY], physical_link=physical_link, source_dest=context_dest) if LXMF.FIELD_TELEMETRY_STREAM in lxm.fields: - for telemetry_entry in lxm_fields[LXMF.FIELD_TELEMETRY_STREAM]: + for telemetry_entry in lxm.fields[LXMF.FIELD_TELEMETRY_STREAM]: # TODO: Implement RNS.log("TODO: Save this telemetry stream entry: "+str(telemetry_entry), RNS.LOG_WARNING) @@ -2431,8 +2444,8 @@ class SidebandCore(): lastsync = self.getpersistent("lxmf.lastsync") nextsync = lastsync+syncinterval - RNS.log("Last sync was "+RNS.prettytime(now-lastsync)+" ago", RNS.LOG_DEBUG) - RNS.log("Next sync is "+("in "+RNS.prettytime(nextsync-now) if nextsync-now > 0 else "now"), RNS.LOG_DEBUG) + RNS.log("Last LXMF sync was "+RNS.prettytime(now-lastsync)+" ago", RNS.LOG_DEBUG) + RNS.log("Next LXMF sync is "+("in "+RNS.prettytime(nextsync-now) if nextsync-now > 0 else "now"), RNS.LOG_DEBUG) if now > nextsync: if self.request_lxmf_sync(): RNS.log("Scheduled LXMF sync succeeded", RNS.LOG_DEBUG) @@ -2450,7 +2463,7 @@ class SidebandCore(): if self.config["telemetry_enabled"]: if self.config["telemetry_send_to_collector"]: - if self.config["telemetry_collector"] != None: + if self.config["telemetry_collector"] != None and self.config["telemetry_collector"] != self.lxmf_destination.hash: try: now = time.time() collector_address = self.config["telemetry_collector"] @@ -2468,7 +2481,10 @@ class SidebandCore(): if not self.pending_telemetry_send_try >= self.pending_telemetry_send_maxtries: self.pending_telemetry_send = True self.pending_telemetry_send_try += 1 - self.send_latest_telemetry(to_addr=collector_address) + if self.config["telemetry_send_all_to_collector"]: + self.create_telemetry_collector_response(to_addr=collector_address) + else: + self.send_latest_telemetry(to_addr=collector_address) else: if self.telemetry_send_blocked_until < now: next_slot = now+send_interval @@ -2481,7 +2497,7 @@ class SidebandCore(): RNS.log("An error occurred while sending scheduled telemetry to collector: "+str(e), RNS.LOG_ERROR) if self.config["telemetry_request_from_collector"]: - if self.config["telemetry_collector"] != None: + if self.config["telemetry_collector"] != None and self.config["telemetry_collector"] != self.lxmf_destination.hash: try: now = time.time() collector_address = self.config["telemetry_collector"] @@ -3385,7 +3401,12 @@ class SidebandCore(): if Commands.TELEMETRY_REQUEST in command: timebase = int(command[Commands.TELEMETRY_REQUEST]) RNS.log("Handling telemetry request with timebase "+str(timebase), RNS.LOG_DEBUG) - self.create_telemetry_response(to_addr=context_dest, timebase=timebase) + if self.config["telemetry_collector_enabled"]: + RNS.log(f"Collector requests enabled, returning complete telemetry response for all known objects since {timebase}", RNS.LOG_DEBUG) + self.create_telemetry_collector_response(to_addr=context_dest, timebase=timebase) + else: + RNS.log("Responding with own latest telemetry", RNS.LOG_DEBUG) + self.send_latest_telemetry(to_addr=context_dest) elif Commands.PING in command: RNS.log("Handling ping request", RNS.LOG_DEBUG) @@ -3415,7 +3436,7 @@ class SidebandCore(): except Exception as e: RNS.log("Error while handling commands: "+str(e), RNS.LOG_ERROR) - def create_telemetry_response(self, to_addr, timebase): + def create_telemetry_collector_response(self, to_addr, timebase): sources = {} sources = self.list_telemetry(after=timebase) only_latest = self.config["telemetry_requests_only_send_latest"] diff --git a/sbapp/ui/conversations.py b/sbapp/ui/conversations.py index 442f35e..376be2d 100644 --- a/sbapp/ui/conversations.py +++ b/sbapp/ui/conversations.py @@ -75,31 +75,74 @@ class Conversations(): self.app.sideband.setstate("app.flags.new_conversations", False) self.app.sideband.setstate("wants.viewupdate.conversations", False) - def trust_icon(self, context_dest, unread): + def trust_icon(self, conv): + context_dest = conv["dest"] + unread = conv["unread"] + appearance = self.app.sideband.peer_appearance(context_dest, conv=conv) + # is_trusted = self.app.sideband.is_trusted(context_dest) + is_trusted = conv["trust"] == 1 + trust_icon = "account-question" - is_trusted = self.app.sideband.is_trusted(context_dest) - if self.app.sideband.requests_allowed_from(context_dest): + da = self.app.sideband.DEFAULT_APPEARANCE + if is_trusted and self.app.sideband.config["display_style_in_contact_list"] and appearance != None and appearance != da: if unread: - if is_trusted: - trust_icon = "email-seal" - else: - trust_icon = "email" + trust_icon = "email" else: - trust_icon = "account-lock-open" + trust_icon = appearance[0] or da[0]; + else: - if is_trusted: + if self.app.sideband.requests_allowed_from(context_dest): if unread: - trust_icon = "email-seal" + if is_trusted: + trust_icon = "email-seal" + else: + trust_icon = "email" else: - trust_icon = "account-check" + trust_icon = "account-lock-open" else: - if unread: - trust_icon = "email" + if is_trusted: + if unread: + trust_icon = "email-seal" + else: + trust_icon = "account-check" else: - trust_icon = "account-question" + if unread: + trust_icon = "email" + else: + trust_icon = "account-question" return trust_icon + def get_icon(self, conv): + context_dest = conv["dest"] + unread = conv["unread"] + last_activity = conv["last_activity"] + trusted = conv["trust"] == 1 + appearance = self.app.sideband.peer_appearance(context_dest, conv=conv) + da = self.app.sideband.DEFAULT_APPEARANCE + ic_s = 24; ic_p = 14 + + conv_icon = self.trust_icon(conv) + fg = None; bg = None; ti_color = None + + if trusted and self.app.sideband.config["display_style_in_contact_list"] and appearance != None and appearance != da: + fg = appearance[1] or da[1]; bg = appearance[2] or da[2] + ti_color = "Custom" + else: + ti_color = None + + iconl = IconLeftWidget( + icon=conv_icon, theme_icon_color=ti_color, + icon_color=fg, md_bg_color=bg, + on_release=self.app.conversation_action) + + RNS.log("ICON "+str(iconl.md_bg_color)) + + iconl._default_icon_pad = dp(ic_p) + iconl.icon_size = dp(ic_s) + + return iconl + def update_widget(self): us = time.time() RNS.log("Updating conversation list widgets", RNS.LOG_DEBUG) @@ -123,8 +166,8 @@ class Conversations(): unread = conv["unread"] last_activity = conv["last_activity"] - if not context_dest in self.added_item_dests: - iconl = IconLeftWidget(icon=self.trust_icon(context_dest, unread), on_release=self.app.conversation_action) + if not context_dest in self.added_item_dests: + iconl = self.get_icon(conv) item = OneLineAvatarIconListItem(text=self.app.sideband.peer_display_name(context_dest), on_release=self.app.conversation_action) item.add_widget(iconl) item.last_activity = last_activity @@ -350,13 +393,28 @@ class Conversations(): for w in self.list.children: if w.sb_uid == context_dest: disp_name = self.app.sideband.peer_display_name(context_dest) - trust_icon = self.trust_icon(context_dest, unread) + trust_icon = self.trust_icon(conv) + trusted = conv["trust"] == 1 + da = self.app.sideband.DEFAULT_APPEARANCE + appearance = self.app.sideband.peer_appearance(context_dest, conv) + if trusted and self.app.sideband.config["display_style_in_contact_list"] and appearance != None and appearance != da: + fg = appearance[1] or da[1]; bg = appearance[2] or da[2] + ti_color = "Custom" + else: + ti_color = None + w.last_activity = last_activity - if w.iconl.icon != trust_icon: - w.iconl.icon = trust_icon - w.sb_unread = unread - if w.text != disp_name: - w.text = disp_name + if ti_color != None: + w.iconl.theme_icon_color = ti_color + if bg != None: w.iconl.md_bg_color = bg + if fg != None: w.iconl.icon_color = fg + else: + w.iconl.theme_icon_color = "Primary" + w.iconl.md_bg_color = [0,0,0,0] + + if w.iconl.icon != trust_icon: w.iconl.icon = trust_icon + if w.sb_unread != unread: w.sb_unread = unread + if w.text != disp_name: w.text = disp_name self.list.children.sort(key=lambda w: (w.trusted, w.last_activity)) diff --git a/sbapp/ui/layouts.py b/sbapp/ui/layouts.py index 2805634..630782e 100644 --- a/sbapp/ui/layouts.py +++ b/sbapp/ui/layouts.py @@ -1236,6 +1236,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: "Display styles in conversation list" + font_style: "H6" + + MDSwitch: + id: display_style_in_contact_list + pos_hint: {"center_y": 0.3} + active: False + MDBoxLayout: orientation: "horizontal" size_hint_y: None diff --git a/sbapp/ui/telemetry.py b/sbapp/ui/telemetry.py index 9238729..d1cdfc0 100644 --- a/sbapp/ui/telemetry.py +++ b/sbapp/ui/telemetry.py @@ -52,6 +52,9 @@ class Telemetry(): self.screen.ids.telemetry_enabled.active = self.app.sideband.config["telemetry_enabled"] self.screen.ids.telemetry_enabled.bind(active=self.telemetry_enabled_toggle) + self.screen.ids.telemetry_collector_enabled.active = self.app.sideband.config["telemetry_collector_enabled"] + self.screen.ids.telemetry_collector_enabled.bind(active=self.telemetry_save) + self.screen.ids.telemetry_send_to_trusted.active = self.app.sideband.config["telemetry_send_to_trusted"] self.screen.ids.telemetry_send_to_trusted.bind(active=self.telemetry_save) @@ -84,7 +87,18 @@ class Telemetry(): self.screen.ids.telemetry_scrollview.effect_cls = ScrollEffect - info = "\nSideband allows you to securely share telemetry, such as location and sensor data, with people, custom programs, machines or other system over LXMF. You have complete control over what kind of telemetry to send, and who you share it with.\n\nTelemetry data is never sent to, via or processed by any external services or servers, but is carried exclusively within encrypted LXMF messages over Reticulum, and only to the destinations you define.\n\nWhen telemetry is enabled, it is possible to embed telemetry data in normal messages on a per-peer basis. You can control this from the [b]Conversations[/b] list, by selecting the [b]Edit[/b] option for the relevant peer.\n\nYou can also define a [b]Telemetry Collector[/b], that Sideband can automatically send telemetry to on a periodic basis. By default, only your own telemetry will be sent to the collector, but by enabling the [b]Send all known to collector[/b] option, you can forward all known telemetry to the collector. This can also be used to aggregate telemetry from multiple different collectors, or create chains of transmission.\n" + info = "\nSideband allows you to securely share telemetry, such as location and sensor data, with people, custom programs, " + info += "machines or other systems over LXMF. You have complete control over what kind of telemetry to send, and who you share " + info += "it with.\n\nTelemetry data is never sent to, via or processed by any external services or servers, but is carried " + info += "exclusively within encrypted LXMF messages over Reticulum, and only to the destinations you define.\n\nWhen telemetry " + info += "is enabled, it is possible to embed telemetry data in normal messages on a per-peer basis. You can control this from " + info += "the [b]Conversations[/b] list, by selecting the [b]Edit[/b] option for the relevant peer.\n\nYou can also define a " + info += "[b]Telemetry Collector[/b], that Sideband can automatically send telemetry to on a periodic basis. By default, only " + info += "your own telemetry will be sent to the collector, but by enabling the [b]Send all known to collector[/b] option, you " + info += "can forward all known telemetry to the collector. This can also be used to aggregate telemetry from multiple different " + info += "collectors, or create chains of transmission.\n\nBy activating the [b]Enable collector[/b] option, this instance of " + info += "Sideband will become a Telemetry Collector, and other authorized peers will be able to query its collected data.\n" + if self.app.theme_cls.theme_style == "Dark": info = "[color=#"+self.app.dark_theme_text_color+"]"+info+"[/color]" @@ -241,6 +255,7 @@ class Telemetry(): self.app.sideband.config["telemetry_requests_only_send_latest"] = self.screen.ids.telemetry_requests_only_send_latest.active self.app.sideband.config["telemetry_allow_requests_from_trusted"] = self.screen.ids.telemetry_allow_requests_from_trusted.active self.app.sideband.config["telemetry_allow_requests_from_anyone"] = self.screen.ids.telemetry_allow_requests_from_anyone.active + self.app.sideband.config["telemetry_collector_enabled"] = self.screen.ids.telemetry_collector_enabled.active self.app.sideband.save_configuration() if run_telemetry_update: @@ -546,6 +561,21 @@ MDScreen: pos_hint: {"center_y": 0.3} active: False + MDBoxLayout: + orientation: "horizontal" + padding: [0,0,dp(24),0] + size_hint_y: None + height: dp(48) + + MDLabel: + text: "Enable collector" + font_style: "H6" + + MDSwitch: + id: telemetry_collector_enabled + pos_hint: {"center_y": 0.3} + active: False + MDBoxLayout: orientation: "horizontal" size_hint_y: None