Added option to show display styles in conversation list

This commit is contained in:
Mark Qvist 2023-10-30 16:55:55 +01:00
parent b015fd19f6
commit 66243b3bfb
5 changed files with 169 additions and 37 deletions

View File

@ -1570,6 +1570,11 @@ class SidebandApp(MDApp):
self.sideband.save_configuration() self.sideband.save_configuration()
self.update_ui_theme() 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): def save_advanced_stats(sender=None, event=None):
self.sideband.config["advanced_stats"] = self.settings_screen.ids.settings_advanced_statistics.active self.sideband.config["advanced_stats"] = self.settings_screen.ids.settings_advanced_statistics.active
self.sideband.save_configuration() 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.active = self.sideband.config["eink_mode"]
self.settings_screen.ids.settings_eink_mode.bind(active=save_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.active = self.sideband.config["advanced_stats"]
self.settings_screen.ids.settings_advanced_statistics.bind(active=save_advanced_stats) self.settings_screen.ids.settings_advanced_statistics.bind(active=save_advanced_stats)

View File

@ -406,6 +406,8 @@ class SidebandCore():
self.config["print_command"] = "lp" self.config["print_command"] = "lp"
if not "eink_mode" in self.config: if not "eink_mode" in self.config:
self.config["eink_mode"] = False 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: if not "connect_transport" in self.config:
self.config["connect_transport"] = False self.config["connect_transport"] = False
@ -514,6 +516,8 @@ class SidebandCore():
self.config["telemetry_send_interval"] = 43200 self.config["telemetry_send_interval"] = 43200
if not "telemetry_request_interval" in self.config: if not "telemetry_request_interval" in self.config:
self.config["telemetry_request_interval"] = 43200 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: if not "telemetry_icon" in self.config:
self.config["telemetry_icon"] = SidebandCore.DEFAULT_APPEARANCE[0] 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) RNS.log("Error while getting peer name: "+str(e), RNS.LOG_ERROR)
return "" return ""
def peer_appearance(self, context_dest): def peer_appearance(self, context_dest, conv=None):
appearance = self._db_get_appearance(context_dest) appearance = self._db_get_appearance(context_dest, conv=conv)
if appearance == None: if appearance == None:
return SidebandCore.DEFAULT_APPEARANCE return SidebandCore.DEFAULT_APPEARANCE
for e in appearance: for e in appearance:
@ -924,7 +928,7 @@ class SidebandCore():
def request_latest_telemetry(self, from_addr=None): 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" return "no_address"
else: else:
if self.getstate(f"telemetry.{RNS.hexrep(from_addr, delimit=False)}.request_sending") == True: 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): 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" return "no_address"
else: else:
if self.getstate(f"telemetry.{RNS.hexrep(to_addr, delimit=False)}.update_sending") == True: if self.getstate(f"telemetry.{RNS.hexrep(to_addr, delimit=False)}.update_sending") == True:
@ -1633,11 +1637,13 @@ class SidebandCore():
result = dbc.fetchall() result = dbc.fetchall()
db.commit() 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: if context_dest == self.lxmf_destination.hash:
return [self.config["telemetry_icon"], self.config["telemetry_fg"], self.config["telemetry_bg"]] return [self.config["telemetry_icon"], self.config["telemetry_fg"], self.config["telemetry_bg"]]
else: else:
conv = self._db_conversation(context_dest) if conv == None:
conv = self._db_conversation(context_dest)
if conv != None and "data" in conv: if conv != None and "data" in conv:
data_dict = conv["data"] data_dict = conv["data"]
try: try:
@ -1728,6 +1734,11 @@ class SidebandCore():
last_rx = entry[1] last_rx = entry[1]
last_tx = entry[2] last_tx = entry[2]
last_activity = max(last_rx, last_tx) last_activity = max(last_rx, last_tx)
data = None
try:
data = msgpack.unpackb(entry[7])
except:
pass
conv = { conv = {
"dest": entry[0], "dest": entry[0],
@ -1735,6 +1746,8 @@ class SidebandCore():
"last_rx": last_rx, "last_rx": last_rx,
"last_tx": last_tx, "last_tx": last_tx,
"last_activity": last_activity, "last_activity": last_activity,
"trust": entry[5],
"data": data,
} }
convs.append(conv) 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) 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: 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 # TODO: Implement
RNS.log("TODO: Save this telemetry stream entry: "+str(telemetry_entry), RNS.LOG_WARNING) 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") lastsync = self.getpersistent("lxmf.lastsync")
nextsync = lastsync+syncinterval nextsync = lastsync+syncinterval
RNS.log("Last sync was "+RNS.prettytime(now-lastsync)+" ago", RNS.LOG_DEBUG) RNS.log("Last LXMF 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("Next LXMF sync is "+("in "+RNS.prettytime(nextsync-now) if nextsync-now > 0 else "now"), RNS.LOG_DEBUG)
if now > nextsync: if now > nextsync:
if self.request_lxmf_sync(): if self.request_lxmf_sync():
RNS.log("Scheduled LXMF sync succeeded", RNS.LOG_DEBUG) RNS.log("Scheduled LXMF sync succeeded", RNS.LOG_DEBUG)
@ -2450,7 +2463,7 @@ class SidebandCore():
if self.config["telemetry_enabled"]: if self.config["telemetry_enabled"]:
if self.config["telemetry_send_to_collector"]: 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: try:
now = time.time() now = time.time()
collector_address = self.config["telemetry_collector"] collector_address = self.config["telemetry_collector"]
@ -2468,7 +2481,10 @@ class SidebandCore():
if not self.pending_telemetry_send_try >= self.pending_telemetry_send_maxtries: if not self.pending_telemetry_send_try >= self.pending_telemetry_send_maxtries:
self.pending_telemetry_send = True self.pending_telemetry_send = True
self.pending_telemetry_send_try += 1 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: else:
if self.telemetry_send_blocked_until < now: if self.telemetry_send_blocked_until < now:
next_slot = now+send_interval 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) 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_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: try:
now = time.time() now = time.time()
collector_address = self.config["telemetry_collector"] collector_address = self.config["telemetry_collector"]
@ -3385,7 +3401,12 @@ class SidebandCore():
if Commands.TELEMETRY_REQUEST in command: if Commands.TELEMETRY_REQUEST in command:
timebase = int(command[Commands.TELEMETRY_REQUEST]) timebase = int(command[Commands.TELEMETRY_REQUEST])
RNS.log("Handling telemetry request with timebase "+str(timebase), RNS.LOG_DEBUG) 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: elif Commands.PING in command:
RNS.log("Handling ping request", RNS.LOG_DEBUG) RNS.log("Handling ping request", RNS.LOG_DEBUG)
@ -3415,7 +3436,7 @@ class SidebandCore():
except Exception as e: except Exception as e:
RNS.log("Error while handling commands: "+str(e), RNS.LOG_ERROR) 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 = {}
sources = self.list_telemetry(after=timebase) sources = self.list_telemetry(after=timebase)
only_latest = self.config["telemetry_requests_only_send_latest"] only_latest = self.config["telemetry_requests_only_send_latest"]

View File

@ -75,31 +75,74 @@ class Conversations():
self.app.sideband.setstate("app.flags.new_conversations", False) self.app.sideband.setstate("app.flags.new_conversations", False)
self.app.sideband.setstate("wants.viewupdate.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" trust_icon = "account-question"
is_trusted = self.app.sideband.is_trusted(context_dest) da = self.app.sideband.DEFAULT_APPEARANCE
if self.app.sideband.requests_allowed_from(context_dest): if is_trusted and self.app.sideband.config["display_style_in_contact_list"] and appearance != None and appearance != da:
if unread: if unread:
if is_trusted: trust_icon = "email"
trust_icon = "email-seal"
else:
trust_icon = "email"
else: else:
trust_icon = "account-lock-open" trust_icon = appearance[0] or da[0];
else: else:
if is_trusted: if self.app.sideband.requests_allowed_from(context_dest):
if unread: if unread:
trust_icon = "email-seal" if is_trusted:
trust_icon = "email-seal"
else:
trust_icon = "email"
else: else:
trust_icon = "account-check" trust_icon = "account-lock-open"
else: else:
if unread: if is_trusted:
trust_icon = "email" if unread:
trust_icon = "email-seal"
else:
trust_icon = "account-check"
else: else:
trust_icon = "account-question" if unread:
trust_icon = "email"
else:
trust_icon = "account-question"
return trust_icon 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): def update_widget(self):
us = time.time() us = time.time()
RNS.log("Updating conversation list widgets", RNS.LOG_DEBUG) RNS.log("Updating conversation list widgets", RNS.LOG_DEBUG)
@ -124,7 +167,7 @@ class Conversations():
last_activity = conv["last_activity"] last_activity = conv["last_activity"]
if not context_dest in self.added_item_dests: if not context_dest in self.added_item_dests:
iconl = IconLeftWidget(icon=self.trust_icon(context_dest, unread), on_release=self.app.conversation_action) iconl = self.get_icon(conv)
item = OneLineAvatarIconListItem(text=self.app.sideband.peer_display_name(context_dest), on_release=self.app.conversation_action) item = OneLineAvatarIconListItem(text=self.app.sideband.peer_display_name(context_dest), on_release=self.app.conversation_action)
item.add_widget(iconl) item.add_widget(iconl)
item.last_activity = last_activity item.last_activity = last_activity
@ -350,13 +393,28 @@ class Conversations():
for w in self.list.children: for w in self.list.children:
if w.sb_uid == context_dest: if w.sb_uid == context_dest:
disp_name = self.app.sideband.peer_display_name(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 w.last_activity = last_activity
if w.iconl.icon != trust_icon: if ti_color != None:
w.iconl.icon = trust_icon w.iconl.theme_icon_color = ti_color
w.sb_unread = unread if bg != None: w.iconl.md_bg_color = bg
if w.text != disp_name: if fg != None: w.iconl.icon_color = fg
w.text = disp_name 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)) self.list.children.sort(key=lambda w: (w.trusted, w.last_activity))

View File

@ -1236,6 +1236,21 @@ MDScreen:
pos_hint: {"center_y": 0.3} pos_hint: {"center_y": 0.3}
active: False 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: MDBoxLayout:
orientation: "horizontal" orientation: "horizontal"
size_hint_y: None size_hint_y: None

View File

@ -52,6 +52,9 @@ class Telemetry():
self.screen.ids.telemetry_enabled.active = self.app.sideband.config["telemetry_enabled"] 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_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.active = self.app.sideband.config["telemetry_send_to_trusted"]
self.screen.ids.telemetry_send_to_trusted.bind(active=self.telemetry_save) 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 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": if self.app.theme_cls.theme_style == "Dark":
info = "[color=#"+self.app.dark_theme_text_color+"]"+info+"[/color]" 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_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_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_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() self.app.sideband.save_configuration()
if run_telemetry_update: if run_telemetry_update:
@ -546,6 +561,21 @@ MDScreen:
pos_hint: {"center_y": 0.3} pos_hint: {"center_y": 0.3}
active: False 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: MDBoxLayout:
orientation: "horizontal" orientation: "horizontal"
size_hint_y: None size_hint_y: None