diff --git a/sbapp/main.py b/sbapp/main.py index aa7ca6c..3ef5efe 100644 --- a/sbapp/main.py +++ b/sbapp/main.py @@ -229,6 +229,7 @@ else: from ui.layouts import * from ui.conversations import Conversations, MsgSync, NewConv from ui.telemetry import Telemetry + from ui.utilities import Utilities from ui.objectdetails import ObjectDetails from ui.announces import Announces from ui.messages import Messages, ts_format, messages_screen_kv @@ -256,6 +257,7 @@ else: from .ui.conversations import Conversations, MsgSync, NewConv from .ui.announces import Announces from .ui.telemetry import Telemetry + from .ui.utilities import Utilities from .ui.objectdetails import ObjectDetails from .ui.messages import Messages, ts_format, messages_screen_kv from .ui.helpers import ContentNavigationDrawer, DrawerList, IconListItem @@ -342,6 +344,7 @@ class SidebandApp(MDApp): self.sync_dialog = None self.settings_ready = False self.telemetry_ready = False + self.utilities_ready = False self.connectivity_ready = False self.hardware_ready = False self.repository_ready = False @@ -1297,6 +1300,8 @@ class SidebandApp(MDApp): self.close_sub_telemetry_action() elif self.root.ids.screen_manager.current == "icons_screen": self.close_sub_telemetry_action() + elif self.root.ids.screen_manager.current == "utilities_screen": + self.close_sub_utilities_action() else: self.open_conversations(direction="right") @@ -1336,9 +1341,12 @@ class SidebandApp(MDApp): else: self.telemetry_action(self) - if text == "u": + if text == "y": self.map_display_own_telemetry() + if text == "u": + self.utilities_action() + if text == "o": self.objects_action() @@ -1350,6 +1358,8 @@ class SidebandApp(MDApp): self.lxmf_sync_action(self) elif self.root.ids.screen_manager.current == "telemetry_screen": self.conversations_action(self, direction="right") + elif self.root.ids.screen_manager.current == "rnstatus_screen": + self.utilities_screen.update_rnstatus() 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) @@ -1394,6 +1404,8 @@ class SidebandApp(MDApp): self.close_sub_telemetry_action() elif self.root.ids.screen_manager.current == "icons_screen": self.close_sub_telemetry_action() + elif self.root.ids.screen_manager.current == "rnstatus_screen": + self.close_sub_utilities_action() else: self.open_conversations(direction="right") @@ -5040,6 +5052,44 @@ class SidebandApp(MDApp): ate_dialog.open() + ### Utilities Screen + ###################################### + + def utilities_init(self): + if not self.utilities_ready: + self.utilities_screen = Utilities(self) + self.utilities_ready = True + + def utilities_open(self, sender=None, direction="left", no_transition=False): + if no_transition: + self.root.ids.screen_manager.transition = self.no_transition + else: + self.root.ids.screen_manager.transition = self.slide_transition + self.root.ids.screen_manager.transition.direction = direction + + self.root.ids.screen_manager.current = "utilities_screen" + self.root.ids.nav_drawer.set_state("closed") + self.sideband.setstate("app.displaying", self.root.ids.screen_manager.current) + + if no_transition: + self.root.ids.screen_manager.transition = self.slide_transition + + def utilities_action(self, sender=None, direction="left"): + if self.utilities_ready: + self.utilities_open(direction=direction) + else: + self.loader_action(direction=direction) + def final(dt): + self.utilities_init() + def o(dt): + self.utilities_open(no_transition=True) + Clock.schedule_once(o, ll_ot) + Clock.schedule_once(final, ll_ft) + + def close_sub_utilities_action(self, sender=None): + self.utilities_action(direction="right") + + ### Telemetry Screen ###################################### @@ -5945,7 +5995,7 @@ If you use Reticulum and LXMF on hardware that does not carry any identifiers ti [b]Quick Actions[/b] - [b]Ctrl-W[/b] Go back - - [b]Ctrl+Q[/b] Shut down Sideband + - [b]Ctrl-Q[/b] Shut down Sideband - [b]Ctrl-R[/b] Start LXMF sync (from Conversations screen) - [b]Ctrl-N[/b] Create new conversation @@ -5968,9 +6018,10 @@ If you use Reticulum and LXMF on hardware that does not carry any identifiers ti - [b]Ctrl-O[/b] Go to Objects & Devices - [b]Ctrl-L[/b] Go to Announce Stream - [b]Ctrl-M[/b] Go to Situation Map + - [b]Ctrl-U[/b] Go to Utilities - [b]Ctrl-T[/b] Go to Telemetry configuration - [b]Ctrl-G[/b] Go to Guide - - [b]Ctrl-U[/b] Display own telemetry + - [b]Ctrl-Y[/b] Display own telemetry [b]Map Controls[/b] - [b]Up[/b], [b]down[/b], [b]left[/b], [b]right[/b] Navigate diff --git a/sbapp/ui/layouts.py b/sbapp/ui/layouts.py index fe9f2ab..a4aefc0 100644 --- a/sbapp/ui/layouts.py +++ b/sbapp/ui/layouts.py @@ -80,6 +80,15 @@ MDNavigationLayout: on_release: root.ids.screen_manager.app.map_action(self) + OneLineIconListItem: + text: "Overview" + on_release: root.ids.screen_manager.app.overview_action(self) + + IconLeftWidget: + icon: "view-dashboard-outline" + on_release: root.ids.screen_manager.app.overview_action(self) + + OneLineIconListItem: text: "Announce Stream" on_release: root.ids.screen_manager.app.announces_action(self) @@ -107,6 +116,15 @@ MDNavigationLayout: on_release: root.ids.screen_manager.app.telemetry_action(self) + OneLineIconListItem: + text: "Utilities" + on_release: root.ids.screen_manager.app.utilities_action(self) + + IconLeftWidget: + icon: "tools" + on_release: root.ids.screen_manager.app.utilities_action(self) + + OneLineIconListItem: text: "Preferences" on_release: root.ids.screen_manager.app.settings_action(self) diff --git a/sbapp/ui/utilities.py b/sbapp/ui/utilities.py new file mode 100644 index 0000000..c192ed6 --- /dev/null +++ b/sbapp/ui/utilities.py @@ -0,0 +1,205 @@ +import time +import RNS + +from typing import Union +from kivy.metrics import dp,sp +from kivy.lang.builder import Builder +from kivy.core.clipboard import Clipboard +from kivy.utils import escape_markup +from kivymd.uix.recycleview import MDRecycleView +from kivymd.uix.list import OneLineIconListItem +from kivymd.uix.pickers import MDColorPicker +from kivymd.icon_definitions import md_icons +from kivy.properties import StringProperty, BooleanProperty +from kivy.effects.scroll import ScrollEffect +from kivy.clock import Clock +from sideband.sense import Telemeter +import threading +from datetime import datetime + +if RNS.vendor.platformutils.get_platform() == "android": + from ui.helpers import ts_format + from android.permissions import request_permissions, check_permission +else: + from .helpers import ts_format + +class Utilities(): + def __init__(self, app): + self.app = app + self.screen = None + self.rnstatus_screen = None + self.rnstatus_instance = None + + if not self.app.root.ids.screen_manager.has_screen("utilities_screen"): + self.screen = Builder.load_string(layout_utilities_screen) + self.screen.app = self.app + self.screen.delegate = self + self.app.root.ids.screen_manager.add_widget(self.screen) + + self.screen.ids.telemetry_scrollview.effect_cls = ScrollEffect + info = "\nYou can use various RNS utilities from Sideband. " + info += "" + + if self.app.theme_cls.theme_style == "Dark": + info = "[color=#"+self.app.dark_theme_text_color+"]"+info+"[/color]" + + self.screen.ids.telemetry_info.text = info + + + ### rnstatus screen + ###################################### + + def rnstatus_action(self, sender=None): + if not self.app.root.ids.screen_manager.has_screen("rnstatus_screen"): + self.rnstatus_screen = Builder.load_string(layout_rnstatus_screen) + self.rnstatus_screen.app = self.app + self.rnstatus_screen.delegate = self + self.app.root.ids.screen_manager.add_widget(self.rnstatus_screen) + + self.app.root.ids.screen_manager.transition.direction = "left" + self.app.root.ids.screen_manager.current = "rnstatus_screen" + self.app.sideband.setstate("app.displaying", self.app.root.ids.screen_manager.current) + + self.update_rnstatus() + + def update_rnstatus(self, sender=None): + threading.Thread(target=self.update_rnstatus_job, daemon=True).start() + + def update_rnstatus_job(self, sender=None): + if self.rnstatus_instance == None: + import RNS.Utilities.rnstatus as rnstatus + self.rnstatus_instance = rnstatus + + import io + from contextlib import redirect_stdout + output_marker = "===begin rnstatus output===" + output = "None" + with io.StringIO() as buffer, redirect_stdout(buffer): + print(output_marker, end="") + self.rnstatus_instance.main(rns_instance=RNS.Reticulum.get_instance()) + output = buffer.getvalue() + + remainder = output[:output.find(output_marker)] + output = output[output.find(output_marker)+len(output_marker):] + print(remainder, end="") + + def cb(dt): + self.rnstatus_screen.ids.rnstatus_output.text = f"[font=RobotoMono-Regular]{output}[/font]" + Clock.schedule_once(cb, 0.2) + + if self.app.root.ids.screen_manager.current == "rnstatus_screen": + Clock.schedule_once(self.update_rnstatus, 1) + + +layout_utilities_screen = """ +MDScreen: + name: "utilities_screen" + + BoxLayout: + orientation: "vertical" + + MDTopAppBar: + title: "Utilities" + anchor_title: "left" + elevation: 0 + left_action_items: + [['menu', lambda x: root.app.nav_drawer.set_state("open")]] + right_action_items: + [ + ['close', lambda x: root.app.close_any_action(self)], + ] + + ScrollView: + id: telemetry_scrollview + + MDBoxLayout: + orientation: "vertical" + size_hint_y: None + height: self.minimum_height + padding: [dp(28), dp(48), dp(28), dp(16)] + + MDLabel: + text: "Utilities & Tools" + font_style: "H6" + + MDLabel: + id: telemetry_info + markup: True + text: "" + size_hint_y: None + text_size: self.width, None + height: self.texture_size[1] + + MDBoxLayout: + orientation: "vertical" + spacing: "24dp" + size_hint_y: None + height: self.minimum_height + padding: [dp(0), dp(35), dp(0), dp(35)] + + MDRectangleFlatIconButton: + id: rnstatus_button + icon: "wifi-check" + text: "Reticulum Status" + 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.rnstatus_action(self) + disabled: False + + MDRectangleFlatIconButton: + id: logview_button + icon: "list-box-outline" + text: "Log Viewer" + 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.rnstatus_action(self) + disabled: True + +""" + +layout_rnstatus_screen = """ +MDScreen: + name: "rnstatus_screen" + + BoxLayout: + orientation: "vertical" + + MDTopAppBar: + id: top_bar + title: "Reticulum Status" + anchor_title: "left" + elevation: 0 + left_action_items: + [['menu', lambda x: root.app.nav_drawer.set_state("open")]] + right_action_items: + [ + ['refresh', lambda x: root.delegate.update_rnstatus()], + ['close', lambda x: root.app.close_sub_utilities_action(self)], + ] + + MDScrollView: + id: sensors_scrollview + size_hint_x: 1 + size_hint_y: None + size: [root.width, root.height-root.ids.top_bar.height] + do_scroll_x: False + do_scroll_y: True + + MDGridLayout: + cols: 1 + padding: [dp(28), dp(14), dp(28), dp(28)] + size_hint_y: None + height: self.minimum_height + + MDLabel: + id: rnstatus_output + markup: True + text: "" + size_hint_y: None + text_size: self.width, None + height: self.texture_size[1] +""" \ No newline at end of file