From 54e5256ea40abcdd100e9881a7a74bd815153e41 Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Sat, 28 Oct 2023 22:27:56 +0200 Subject: [PATCH] Implemented UI flows for telemetry, own telemetry, maps and conversations. --- sbapp/main.py | 61 +++++++++++------ sbapp/sideband/core.py | 20 ++++++ sbapp/ui/objectdetails.py | 136 ++++++++++++++++++++++++++++++++++++-- 3 files changed, 193 insertions(+), 24 deletions(-) diff --git a/sbapp/main.py b/sbapp/main.py index 6a9793b..9537d38 100644 --- a/sbapp/main.py +++ b/sbapp/main.py @@ -811,6 +811,9 @@ class SidebandApp(MDApp): if self.root.ids.screen_manager.current == "messages_screen": context_dest = self.messages_view.ids.messages_scrollview.active_conversation self.map_show_peer_location(context_dest) + elif self.root.ids.screen_manager.current == "object_details_screen": + context_dest = self.object_details_screen.object_hash + self.map_show_peer_location(context_dest) else: self.map_action(self) @@ -823,16 +826,27 @@ class SidebandApp(MDApp): if text == "t": if self.root.ids.screen_manager.current == "messages_screen": self.object_details_action(self.messages_view, from_conv=True) + elif self.root.ids.screen_manager.current == "object_details_screen": + self.object_details_screen.reload_telemetry() else: self.telemetry_action(self) + if text == "o": + # if self.root.ids.screen_manager.current == "telemetry_screen": + self.map_display_own_telemetry() + if text == "r": if self.root.ids.screen_manager.current == "conversations_screen": self.lxmf_sync_action(self) elif self.root.ids.screen_manager.current == "telemetry_screen": - self.converse_from_telemetry(self) + self.conversations_action(self, direction="right") + elif self.root.ids.screen_manager.current == "object_details_screen": + if not self.object_details_screen.object_hash == self.sideband.lxmf_destination.hash: + self.converse_from_telemetry(self) + else: + self.conversations_action(self, direction="right") else: - self.conversations_action(self) + self.conversations_action(self, direction="right") if len(modifiers) > 0 and modifiers[0] == 'ctrl' and (text == "g"): self.guide_action(self) @@ -1063,6 +1077,9 @@ class SidebandApp(MDApp): if self.root.ids.screen_manager.current == "messages_screen": context_dest = self.messages_view.ids.messages_scrollview.active_conversation self.map_show_peer_location(context_dest) + if self.root.ids.screen_manager.current == "object_details_screen": + context_dest = self.object_details_screen.object_hash + self.map_show_peer_location(context_dest) def peer_show_telemetry_action(self, sender): if self.root.ids.screen_manager.current == "messages_screen": @@ -1124,8 +1141,8 @@ class SidebandApp(MDApp): ### Conversations screen ###################################### - def conversations_action(self, sender=None): - self.open_conversations() + def conversations_action(self, sender=None, direction="left"): + self.open_conversations(direction=direction) def open_conversations(self, direction="left"): self.root.ids.screen_manager.transition.direction = direction @@ -3252,8 +3269,11 @@ class SidebandApp(MDApp): self.root.ids.nav_drawer.set_state("closed") self.sideband.setstate("app.displaying", self.root.ids.screen_manager.current) - def converse_from_telemetry(self): - pass + def converse_from_telemetry(self, sender=None): + if self.object_details_screen != None: + context_dest = self.object_details_screen.object_hash + if not self.object_details_screen.object_hash == self.sideband.lxmf_destination.hash: + self.open_conversation(context_dest) def telemetry_copy(self, sender=None): Clipboard.copy(str(self.sideband.get_telemetry())) @@ -3425,10 +3445,10 @@ class SidebandApp(MDApp): maxz = source.max_zoom minz = source.min_zoom if self.map.zoom > maxz: - self.map.zoom = maxz + mz = maxz; px, py = self.map_get_zoom_center(); self.map.set_zoom_at(mz, px, py) if self.map.zoom < minz: - self.map.zoom = minz + mz = minz; px, py = self.map_get_zoom_center(); self.map.set_zoom_at(mz, px, py) m = self.map nlat = self.map.lat @@ -3552,24 +3572,24 @@ class SidebandApp(MDApp): delta = (-span/self.map_nav_divisor)*modifier self.map.center_on(self.map.lat+delta, self.map.lon) + def map_get_zoom_center(self): + bb = self.map.get_bbox() + slat = (bb[2]-bb[0])/2; slon = (bb[3]-bb[1])/2 + zlat = bb[0]+slat; zlon = bb[1]+slon + return self.map.get_window_xy_from(zlat, zlon, self.map.zoom) + def map_nav_zoom_out(self, sender=None, modifier=1.0): if self.map != None: zd = -self.map_nav_zoom*modifier if self.map.zoom+zd > self.map.map_source.min_zoom: - bb = self.map.get_bbox() - slat = (bb[2]-bb[0])/2; slon = (bb[3]-bb[1])/2 - zlat = bb[0]+slat; zlon = bb[1]+slon - px, py = self.map.get_window_xy_from(zlat, zlon, self.map.zoom) + px, py = self.map_get_zoom_center() self.map.animated_diff_scale_at(zd, px, py) def map_nav_zoom_in(self, sender=None, modifier=1.0): if self.map != None: zd = self.map_nav_zoom*modifier if self.map.zoom+zd < self.map.map_source.max_zoom or self.map.scale < 3.0: - bb = self.map.get_bbox() - slat = (bb[2]-bb[0])/2; slon = (bb[3]-bb[1])/2 - zlat = bb[0]+slat; zlon = bb[1]+slon - px, py = self.map.get_window_xy_from(zlat, zlon, self.map.zoom) + px, py = self.map_get_zoom_center() self.map.animated_diff_scale_at(zd, px, py) def map_action(self, sender=None, direction="left"): @@ -3608,7 +3628,7 @@ class SidebandApp(MDApp): def am_job(dt): self.map_update_markers() - Clock.schedule_once(am_job, 0.6) + Clock.schedule_once(am_job, 0.15) def map_settings_load_states(self): if self.map_settings_screen != None: @@ -3709,10 +3729,13 @@ class SidebandApp(MDApp): def map_show(self, location): if hasattr(self, "map") and self.map: - self.map.center_on(location["latitude"],location["longtitude"]) # self.map.lat = location["latitude"] # self.map.lon = location["longtitude"] - self.map.zoom = 16 + mz = 16 + if mz > self.map.map_source.max_zoom: mz = self.map.map_source.max_zoom + if mz < self.map.map_source.min_zoom: mz = self.map.map_source.min_zoom + self.map.center_on(location["latitude"],location["longtitude"]) + px, py = self.map_get_zoom_center(); self.map.set_zoom_at(mz, px, py) self.map.trigger_update(True) def map_show_peer_location(self, context_dest): diff --git a/sbapp/sideband/core.py b/sbapp/sideband/core.py index 5e1a7d1..56e0341 100644 --- a/sbapp/sideband/core.py +++ b/sbapp/sideband/core.py @@ -846,7 +846,27 @@ class SidebandCore(): return [] + def owm_location(self): + return self.peer_location(self.lxmf_destination.hash) + def peer_location(self, context_dest): + if context_dest == None: + return None + + if context_dest == self.lxmf_destination.hash: + try: + if self.latest_telemetry != None: + lt = self.latest_telemetry + if "location" in lt and lt["location"] != None: + l = lt["location"] + if "latitude" in l and "longtitude" in l: + if l["latitude"] != None and l["longtitude"] != None: + return l + return None + + except Exception as e: + RNS.log("Error while getting own location: "+str(e), RNS.LOG_ERROR) + after_time = time.time()-3*30*24*60*60 pts = self.peer_telemetry(context_dest, after=after_time) for pt in pts: diff --git a/sbapp/ui/objectdetails.py b/sbapp/ui/objectdetails.py index 67f951a..34838db 100644 --- a/sbapp/ui/objectdetails.py +++ b/sbapp/ui/objectdetails.py @@ -14,6 +14,10 @@ from sideband.sense import Telemeter import threading import webbrowser +from kivymd.uix.button import MDRectangleFlatButton +from kivymd.uix.dialog import MDDialog +from kivymd.toast import toast + from datetime import datetime @@ -27,11 +31,13 @@ class ObjectDetails(): self.app = app self.widget = None self.object_hash = object_hash + self.lastest_timestamp = 0 self.coords = None self.raw_telemetry = None self.from_telemetry = False self.from_conv = False self.viewing_self = False + self.delete_dialog = None if not self.app.root.ids.screen_manager.has_screen("object_details_screen"): self.screen = Builder.load_string(layou_object_details) @@ -46,6 +52,16 @@ class ObjectDetails(): self.telemetry_list.app = self.app self.screen.ids.object_details_container.add_widget(self.telemetry_list) + Clock.schedule_interval(self.reload_job, 2) + + def reload_job(self, dt=None): + if self.app.root.ids.screen_manager.current == "object_details_screen": + latest_telemetry = self.app.sideband.peer_telemetry(self.object_hash, limit=1) + if latest_telemetry != None and len(latest_telemetry) > 0: + telemetry_timestamp = latest_telemetry[0][0] + if telemetry_timestamp > self.lastest_timestamp: + self.reload_telemetry(notoast=True) + def close_action(self, sender=None): if self.from_telemetry: self.app.telemetry_action(direction="right") @@ -55,7 +71,41 @@ class ObjectDetails(): else: self.app.close_sub_map_action() - def set_source(self, source_dest, from_conv=False, from_telemetry=False): + def confirm_delete_telemetry(self, sender=None): + self.app.sideband.clear_telemetry(self.object_hash) + + def delete_telemetry_action(self, sender=None): + if self.delete_dialog == None: + yes_button = MDRectangleFlatButton(text="Yes",font_size=dp(18), theme_text_color="Custom", line_color=self.app.color_reject, text_color=self.app.color_reject) + no_button = MDRectangleFlatButton(text="No",font_size=dp(18)) + self.delete_dialog = MDDialog( + title="Clear telemetry?", + text="This will permanently delete all collected telemetry for this object.", + buttons=[ yes_button, no_button ], + ) + def dl_yes(s): + self.delete_dialog.dismiss() + self.confirm_delete_telemetry() + + def cb(dt): + self.reload_telemetry(notoast=True) + Clock.schedule_once(cb, 0.2) + + def dl_no(s): + self.delete_dialog.dismiss() + + yes_button.bind(on_release=dl_yes) + no_button.bind(on_release=dl_no) + + self.delete_dialog.open() + + def reload_telemetry(self, sender=None, notoast=False): + if self.object_hash != None: + self.set_source(self.object_hash, from_conv=self.from_conv, from_telemetry=self.from_telemetry) + if not notoast: + toast("Reloaded telemetry for object") + + def set_source(self, source_dest, from_conv=False, from_telemetry=False, prefetched=None): self.object_hash = source_dest if from_telemetry: @@ -76,9 +126,27 @@ class ObjectDetails(): self.screen.ids.object_appearance.icon = appearance[0] self.screen.ids.object_appearance.icon_color = appearance[1] self.screen.ids.object_appearance.md_bg_color = appearance[2] + # self.screen.ids.delete_button.line_color = self.app.color_reject + # self.screen.ids.delete_button.text_color = self.app.color_reject + # self.screen.ids.delete_button.icon_color = self.app.color_reject + def djob(dt): + if self.viewing_self: + self.screen.ids.request_button.disabled = True + self.screen.ids.send_button.disabled = True + else: + self.screen.ids.request_button.disabled = False + self.screen.ids.send_button.disabled = False + Clock.schedule_once(djob, 0.0) + + if prefetched != None: + latest_telemetry = prefetched + else: + latest_telemetry = self.app.sideband.peer_telemetry(source_dest, limit=1) - latest_telemetry = self.app.sideband.peer_telemetry(source_dest, limit=1) if latest_telemetry != None and len(latest_telemetry) > 0: + telemetry_timestamp = latest_telemetry[0][0] + self.lastest_timestamp = telemetry_timestamp + own_address = self.app.sideband.lxmf_destination.hash telemeter = Telemeter.from_packed(latest_telemetry[0][1]) self.raw_telemetry = telemeter.read_all() @@ -428,6 +496,9 @@ MDScreen: [['menu', lambda x: root.app.nav_drawer.set_state("open")]] right_action_items: [ + ['map-search', lambda x: root.app.peer_show_location_action(root.delegate)], + ['refresh', lambda x: root.delegate.reload_telemetry()], + ['trash-can-outline', lambda x: root.delegate.delete_telemetry_action()], ['close', lambda x: root.delegate.close_action()], ] @@ -446,6 +517,7 @@ MDScreen: md_bg_color: [1,1,1,1] theme_icon_color: "Custom" icon_size: dp(32) + on_release: root.app.converse_from_telemetry() MDLabel: id: name_label @@ -454,12 +526,11 @@ MDScreen: font_style: "H6" MDBoxLayout: - id: object_header orientation: "horizontal" spacing: dp(24) size_hint_y: None height: self.minimum_height - padding: [dp(24), dp(0), dp(24), dp(12)] + padding: [dp(24), dp(0), dp(24), dp(24)] MDRectangleFlatIconButton: id: telemetry_button @@ -482,9 +553,64 @@ MDScreen: size_hint: [1.0, None] on_release: root.delegate.copy_coordinates(self) disabled: False - + + MDSeparator: + orientation: "horizontal" + height: dp(1) + MDBoxLayout: orientation: "vertical" id: object_details_container + MDSeparator: + orientation: "horizontal" + height: dp(1) + + MDBoxLayout: + orientation: "horizontal" + spacing: dp(24) + size_hint_y: None + height: self.minimum_height + padding: [dp(24), dp(24), dp(24), dp(24)] + + MDRectangleFlatIconButton: + id: request_button + icon: "arrow-down-bold-hexagon-outline" + text: "Request Update" + padding: [dp(0), dp(14), dp(0), dp(14)] + icon_size: dp(24) + font_size: dp(16) + size_hint: [1.0, None] + on_release: root.delegate.copy_telemetry(self) + disabled: False + + MDRectangleFlatIconButton: + id: send_button + icon: "upload-lock" + text: "Send Update Now" + padding: [dp(0), dp(14), dp(0), dp(14)] + icon_size: dp(24) + font_size: dp(16) + size_hint: [1.0, None] + on_release: root.delegate.copy_coordinates(self) + disabled: False + + # MDBoxLayout: + # orientation: "horizontal" + # spacing: dp(16) + # size_hint_y: None + # height: self.minimum_height + # padding: [dp(24), dp(16), dp(24), dp(24)] + + # MDRectangleFlatIconButton: + # id: delete_button + # icon: "trash-can-outline" + # text: "Delete All Telemetry" + # padding: [dp(0), dp(14), dp(0), dp(14)] + # icon_size: dp(24) + # font_size: dp(16) + # size_hint: [1.0, None] + # on_release: root.delegate.copy_telemetry(self) + # disabled: False + """ \ No newline at end of file