Sideband/sbapp/ui/utilities.py

294 lines
10 KiB
Python

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 kivymd.toast import toast
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
self.logviewer_screen = 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 = "This section contains various utilities and diagnostics tools, "
info += "that can be helpful while using Sideband and Reticulum."
if self.app.theme_cls.theme_style == "Dark":
info = "[color=#"+self.app.dark_theme_text_color+"]"+info+"[/color]"
self.screen.ids.utilities_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 = "None"
with io.StringIO() as buffer, redirect_stdout(buffer):
with RNS.logging_lock:
self.rnstatus_instance.main(rns_instance=RNS.Reticulum.get_instance())
output = buffer.getvalue()
def cb(dt):
self.rnstatus_screen.ids.rnstatus_output.text = f"[font=RobotoMono-Regular][size={int(dp(12))}]{output}[/size][/font]"
Clock.schedule_once(cb, 0.2)
if self.app.root.ids.screen_manager.current == "rnstatus_screen":
Clock.schedule_once(self.update_rnstatus, 1)
### Log viewer screen
######################################
def logviewer_action(self, sender=None):
if not self.app.root.ids.screen_manager.has_screen("logviewer_screen"):
self.logviewer_screen = Builder.load_string(layout_logviewer_screen)
self.logviewer_screen.app = self.app
self.logviewer_screen.delegate = self
self.app.root.ids.screen_manager.add_widget(self.logviewer_screen)
self.app.root.ids.screen_manager.transition.direction = "left"
self.app.root.ids.screen_manager.current = "logviewer_screen"
self.app.sideband.setstate("app.displaying", self.app.root.ids.screen_manager.current)
self.update_logviewer()
def update_logviewer(self, sender=None):
threading.Thread(target=self.update_logviewer_job, daemon=True).start()
def update_logviewer_job(self, sender=None):
try:
output = self.app.sideband.get_log()
except Exception as e:
output = f"An error occurred while retrieving log entries:\n{e}"
self.logviewer_screen.log_contents = output
def cb(dt):
self.logviewer_screen.ids.logviewer_output.text = f"[font=RobotoMono-Regular][size={int(dp(12))}]{output}[/size][/font]"
Clock.schedule_once(cb, 0.2)
if self.app.root.ids.screen_manager.current == "logviewer_screen":
Clock.schedule_once(self.update_logviewer, 1)
def logviewer_copy(self, sender=None):
Clipboard.copy(self.logviewer_screen.log_contents)
if True or RNS.vendor.platformutils.is_android():
toast("Log copied to clipboard")
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(32), dp(28), dp(16)]
# MDLabel:
# text: "Utilities & Tools"
# font_style: "H6"
MDLabel:
id: utilities_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.logviewer_action(self)
disabled: False
MDRectangleFlatIconButton:
id: advanced_button
icon: "network-pos"
text: "Advanced RNS Configuration"
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.advanced_action(self)
disabled: False
"""
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: rnstatus_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]
"""
layout_logviewer_screen = """
MDScreen:
name: "logviewer_screen"
BoxLayout:
orientation: "vertical"
MDTopAppBar:
id: top_bar
title: "Log Viewer"
anchor_title: "left"
elevation: 0
left_action_items:
[['menu', lambda x: root.app.nav_drawer.set_state("open")]]
right_action_items:
[
['content-copy', lambda x: root.delegate.logviewer_copy()],
['close', lambda x: root.app.close_sub_utilities_action(self)],
]
MDScrollView:
id: logviewer_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: logviewer_output
markup: True
text: ""
size_hint_y: None
text_size: self.width, None
height: self.texture_size[1]
"""