mirror of
https://github.com/markqvist/Sideband.git
synced 2025-11-27 10:50:39 -05:00
752 lines
No EOL
32 KiB
Python
752 lines
No EOL
32 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.uix.button import MDRectangleFlatButton
|
|
from kivymd.uix.button import MDRectangleFlatIconButton
|
|
from kivymd.uix.dialog import MDDialog
|
|
from kivy.properties import StringProperty, BooleanProperty, OptionProperty, ColorProperty, Property
|
|
from kivymd.uix.list import MDList, IconLeftWidget, IconRightWidget, OneLineAvatarIconListItem
|
|
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_date
|
|
from android.permissions import request_permissions, check_permission
|
|
else:
|
|
from .helpers import ts_format_date
|
|
|
|
from kivy.utils import escape_markup
|
|
if RNS.vendor.platformutils.get_platform() == "android":
|
|
from ui.helpers import multilingual_markup
|
|
else:
|
|
from .helpers import multilingual_markup
|
|
|
|
from LXST.Primitives.Telephony import Telephone, Profiles
|
|
|
|
class Voice():
|
|
def __init__(self, app):
|
|
self.app = app
|
|
self.screen = None
|
|
self.settings_screen = None
|
|
self.dial_target = None
|
|
self.ui_updater = None
|
|
self.path_requesting = None
|
|
self.output_devices = []
|
|
self.input_devices = []
|
|
self.log_list = None
|
|
self.last_log_update = 0
|
|
self.log_name_cache = {}
|
|
self.listed_output_devices = []
|
|
self.listed_input_devices = []
|
|
self.listed_ringer_devices = []
|
|
self.call_profile = Profiles.DEFAULT_PROFILE
|
|
|
|
if not self.app.root.ids.screen_manager.has_screen("voice_screen"):
|
|
self.screen = Builder.load_string(layout_voice_screen)
|
|
self.screen.app = self.app
|
|
self.screen.delegate = self
|
|
self.app.root.ids.screen_manager.add_widget(self.screen)
|
|
self.update_call_log()
|
|
|
|
def update_call_status(self, dt=None):
|
|
if self.app.root.ids.screen_manager.current == "voice_screen":
|
|
if self.ui_updater == None: self.ui_updater = Clock.schedule_interval(self.update_call_status, 0.5)
|
|
else:
|
|
if self.ui_updater:
|
|
self.ui_updater.cancel()
|
|
self.ui_updater = None
|
|
|
|
db = self.screen.ids.dial_button
|
|
rb = self.screen.ids.reject_button
|
|
ih = self.screen.ids.identity_hash
|
|
pb = self.screen.ids.call_profile_button
|
|
if self.app.sideband.voice_running:
|
|
telephone = self.app.sideband.telephone
|
|
if self.path_requesting:
|
|
db.disabled = True
|
|
rb.disabled = True
|
|
ih.disabled = True
|
|
|
|
else:
|
|
if telephone.is_available:
|
|
ih.disabled = False
|
|
rb.disabled = True
|
|
pb.disabled = False
|
|
self.target_input_action(ih)
|
|
else:
|
|
ih.disabled = True
|
|
rb.disabled = True
|
|
pb.disabled = True
|
|
|
|
if telephone.is_in_call or telephone.call_is_connecting:
|
|
ih.disabled = True
|
|
rb.disabled = True
|
|
db.disabled = False
|
|
if telephone.call_is_connecting: pb.disabled = True
|
|
if telephone.is_in_call: pb.disabled = False
|
|
db.text = "Hang up"
|
|
db.icon = "phone-hangup"
|
|
if telephone.active_profile: self.call_profile = telephone.active_profile
|
|
|
|
elif telephone.is_ringing:
|
|
ih.disabled = True
|
|
rb.disabled = False
|
|
db.disabled = False
|
|
pb.disabled = True
|
|
db.text = "Answer"
|
|
db.icon = "phone-ring"
|
|
if telephone.caller: ih.text = RNS.hexrep(telephone.caller.hash, delimit=False)
|
|
if telephone.active_profile: self.call_profile = telephone.active_profile
|
|
|
|
if self.app.sideband.getstate("voice.connection_failure"):
|
|
self.app.sideband.setstate("voice.connection_failure", False)
|
|
toast("Could not connect call", duration=5)
|
|
|
|
else:
|
|
db.disabled = True; db.text = "Voice calls disabled"
|
|
ih.disabled = True
|
|
rb.disabled = True
|
|
pb.disabled = True
|
|
|
|
pb.text = Profiles.profile_abbrevation(self.call_profile)
|
|
|
|
if time.time() > self.last_log_update+3: self.update_call_log()
|
|
|
|
def target_valid(self):
|
|
if self.app.sideband.voice_running:
|
|
db = self.screen.ids.dial_button
|
|
db.disabled = False; db.text = "Call"
|
|
db.icon = "phone-outgoing"
|
|
|
|
def target_invalid(self):
|
|
if self.app.sideband.voice_running:
|
|
db = self.screen.ids.dial_button
|
|
db.disabled = True; db.text = "Call"
|
|
db.icon = "phone-outgoing"
|
|
|
|
def target_input_action(self, sender):
|
|
if sender:
|
|
target_hash = sender.text
|
|
if len(target_hash) == RNS.Reticulum.TRUNCATED_HASHLENGTH//8*2:
|
|
try:
|
|
identity_hash = bytes.fromhex(target_hash)
|
|
self.dial_target = identity_hash
|
|
self.target_valid()
|
|
|
|
except Exception as e: self.target_invalid()
|
|
else: self.target_invalid()
|
|
|
|
def request_path(self, destination_hash):
|
|
if not self.path_requesting:
|
|
self.app.sideband.telephone.set_busy(True)
|
|
toast("Requesting path...")
|
|
self.screen.ids.dial_button.disabled = True
|
|
self.path_requesting = destination_hash
|
|
RNS.Transport.request_path(destination_hash)
|
|
threading.Thread(target=self._path_wait_job, daemon=True).start()
|
|
|
|
else:
|
|
toast("Waiting for path request answer...")
|
|
|
|
def _path_wait_job(self):
|
|
timeout = time.time()+self.app.sideband.telephone.PATH_TIME
|
|
while not RNS.Transport.has_path(self.path_requesting) and time.time() < timeout:
|
|
time.sleep(0.25)
|
|
|
|
self.app.sideband.telephone.set_busy(False)
|
|
if RNS.Transport.has_path(self.path_requesting):
|
|
RNS.log(f"Calling {RNS.prettyhexrep(self.dial_target)}...", RNS.LOG_DEBUG)
|
|
self.app.sideband.telephone.dial(self.dial_target, profile=self.call_profile)
|
|
Clock.schedule_once(self.update_call_status, 0.1)
|
|
|
|
else:
|
|
Clock.schedule_once(self._path_request_failed, 0.05)
|
|
Clock.schedule_once(self.update_call_status, 0.1)
|
|
|
|
self.path_requesting = None
|
|
self.update_call_status()
|
|
|
|
def _path_request_failed(self, dt):
|
|
toast("Path request timed out")
|
|
|
|
def call_profile_action(self, sender=None):
|
|
if self.app.sideband.telephone.is_in_call: self.switch_profile_action()
|
|
else:
|
|
pb = self.screen.ids.call_profile_button
|
|
self.call_profile = Profiles.next_profile(self.call_profile)
|
|
pb.text = Profiles.profile_abbrevation(self.call_profile)
|
|
toast(f"Call Profile: {Profiles.profile_name(self.call_profile)}")
|
|
|
|
def switch_profile_action(self, sender=None):
|
|
if self.initial_call_profile == None: self.initial_call_profile = self.call_profile
|
|
if self.initial_call_profile < Profiles.QUALITY_MEDIUM: alt_profile = Profiles.QUALITY_MEDIUM
|
|
else: alt_profile = Profiles.BANDWIDTH_LOW
|
|
switch_profiles = [alt_profile, self.initial_call_profile]
|
|
if self.call_profile == switch_profiles[0]:
|
|
RNS.log(f"Switching to {Profiles.profile_name(switch_profiles[1])}", RNS.LOG_DEBUG)
|
|
self.app.sideband.telephone.switch_profile(switch_profiles[1])
|
|
else:
|
|
RNS.log(f"Switching to {Profiles.profile_name(switch_profiles[0])}", RNS.LOG_DEBUG)
|
|
self.app.sideband.telephone.switch_profile(switch_profiles[0])
|
|
|
|
def clear_log_action(self, sender=None):
|
|
self.app.sideband.telephone.clear_call_log()
|
|
self.update_call_log()
|
|
|
|
def log_dial_action(self, sender=None):
|
|
def job(dt=None):
|
|
if not self.app.root.ids.screen_manager.current == "voice_screen": return
|
|
if sender and self.app.sideband.voice_running and self.app.sideband.telephone != None:
|
|
telephone = self.app.sideband.telephone
|
|
if not telephone.is_ringing and not telephone.is_in_call and not telephone.call_is_connecting:
|
|
call_dialog_text = f"[b]Initiate Voice Call?[/b]\n\nDestination Identity:\n{RNS.prettyhexrep(sender.identity)}"
|
|
if hasattr(self.app, "confirm_call_dialog"): self.app.confirm_call_dialog.text = call_dialog_text
|
|
else: self.app.init_confirm_call_dialog(call_dialog_text)
|
|
self.app.confirm_call_dialog.dest_identity_hash = sender.identity
|
|
self.app.confirm_call_dialog.open()
|
|
|
|
Clock.schedule_once(job, 0.1)
|
|
|
|
def reject_action(self, sender=None):
|
|
if self.app.sideband.voice_running:
|
|
if self.app.sideband.telephone.is_ringing:
|
|
self.app.sideband.telephone.hangup()
|
|
self.initial_call_profile = None
|
|
|
|
def dial_action(self, sender=None):
|
|
if self.app.sideband.voice_running:
|
|
if self.app.sideband.telephone.is_available:
|
|
|
|
destination_hash = RNS.Destination.hash_from_name_and_identity("lxst.telephony", self.dial_target)
|
|
if not RNS.Transport.has_path(destination_hash): self.request_path(destination_hash)
|
|
else:
|
|
RNS.log(f"Calling {RNS.prettyhexrep(self.dial_target)}...", RNS.LOG_DEBUG)
|
|
if self.app.sideband.telephone.dial(self.dial_target, profile=self.call_profile) == "no_path":
|
|
self.request_path(destination_hash)
|
|
self.update_call_status()
|
|
|
|
elif self.app.sideband.telephone.is_in_call or self.app.sideband.telephone.call_is_connecting:
|
|
RNS.log(f"Hanging up", RNS.LOG_DEBUG)
|
|
self.app.sideband.telephone.hangup()
|
|
self.update_call_status()
|
|
|
|
elif self.app.sideband.telephone.is_ringing:
|
|
RNS.log(f"Answering", RNS.LOG_DEBUG)
|
|
self.app.sideband.telephone.answer()
|
|
self.update_call_status()
|
|
|
|
self.initial_call_profile = None
|
|
|
|
|
|
### Settings screen
|
|
######################################
|
|
|
|
def settings_action(self, sender=None):
|
|
if not self.app.root.ids.screen_manager.has_screen("voice_settings_screen"):
|
|
self.voice_settings_screen = Builder.load_string(layout_voice_settings_screen)
|
|
self.voice_settings_screen.app = self.app
|
|
self.voice_settings_screen.delegate = self
|
|
self.app.root.ids.screen_manager.add_widget(self.voice_settings_screen)
|
|
|
|
self.app.root.ids.screen_manager.transition.direction = "left"
|
|
self.app.root.ids.screen_manager.current = "voice_settings_screen"
|
|
self.voice_settings_screen.ids.voice_settings_scrollview.effect_cls = ScrollEffect
|
|
self.app.sideband.setstate("app.displaying", self.app.root.ids.screen_manager.current)
|
|
|
|
self.update_settings_screen()
|
|
|
|
def update_devices(self):
|
|
import LXST
|
|
self.output_devices = []; self.input_devices = []
|
|
for device in LXST.Sources.Backend().soundcard.all_speakers(): self.output_devices.append(device.name)
|
|
for device in LXST.Sinks.Backend().soundcard.all_microphones(): self.input_devices.append(device.name)
|
|
if self.app.sideband.config["voice_output"] != None:
|
|
if not self.app.sideband.config["voice_output"] in self.output_devices: self.output_devices.append(self.app.sideband.config["voice_output"])
|
|
if self.app.sideband.config["voice_input"] != None:
|
|
if not self.app.sideband.config["voice_input"] in self.input_devices: self.input_devices.append(self.app.sideband.config["voice_input"])
|
|
if self.app.sideband.config["voice_ringer"] != None:
|
|
if not self.app.sideband.config["voice_ringer"] in self.output_devices: self.output_devices.append(self.app.sideband.config["voice_ringer"])
|
|
|
|
def update_settings_screen(self, sender=None):
|
|
self.voice_settings_screen.ids.voice_trusted_only.active = self.app.sideband.config["voice_trusted_only"]
|
|
self.voice_settings_screen.ids.voice_trusted_only.bind(active=self.settings_save_action)
|
|
self.voice_settings_screen.ids.voice_low_latency.active = self.app.sideband.config["voice_low_latency"]
|
|
self.voice_settings_screen.ids.voice_low_latency.bind(active=self.settings_save_action)
|
|
|
|
if not RNS.vendor.platformutils.is_android(): self.voice_settings_screen.ids.voice_low_latency.disabled = True
|
|
|
|
bp = 6; ml = 38; fs = 16; ics = 14
|
|
self.update_devices()
|
|
|
|
# Output devices
|
|
if not "system_default" in self.listed_output_devices:
|
|
default_output_button = MDRectangleFlatIconButton(text="System Default", font_size=dp(fs), icon_size=dp(ics), on_release=self.output_device_action)
|
|
default_output_button.device = None; default_output_button.size_hint = [1.0, None]
|
|
if self.app.sideband.config["voice_output"] == None: default_output_button.icon = "check"
|
|
self.voice_settings_screen.ids.output_devices.add_widget(default_output_button)
|
|
self.listed_output_devices.append("system_default")
|
|
|
|
for device in self.output_devices:
|
|
if not device in self.listed_output_devices:
|
|
device_str = device.replace("[", "").replace("]", "")
|
|
label = device_str if len(device_str) < ml else device_str[:ml-3]+"..."
|
|
device_button = MDRectangleFlatIconButton(text=label, font_size=dp(fs), icon_size=dp(ics), on_release=self.output_device_action)
|
|
device_button.padding = [dp(bp), dp(bp), dp(bp), dp(bp)]; device_button.size_hint = [1.0, None]
|
|
if self.app.sideband.config["voice_output"] == device: device_button.icon = "check"
|
|
device_button.device = device
|
|
self.voice_settings_screen.ids.output_devices.add_widget(device_button)
|
|
self.listed_output_devices.append(device)
|
|
|
|
# Input devices
|
|
if not "system_default" in self.listed_input_devices:
|
|
default_input_button = MDRectangleFlatIconButton(text="System Default", font_size=dp(fs), icon_size=dp(ics), on_release=self.input_device_action)
|
|
default_input_button.device = None; default_input_button.size_hint = [1.0, None]
|
|
if self.app.sideband.config["voice_output"] == None: default_input_button.icon = "check"
|
|
self.voice_settings_screen.ids.input_devices.add_widget(default_input_button)
|
|
self.listed_input_devices.append("system_default")
|
|
|
|
for device in self.input_devices:
|
|
if not device in self.listed_input_devices:
|
|
device_str = device.replace("[", "").replace("]", "")
|
|
label = device_str if len(device_str) < ml else device_str[:ml-3]+"..."
|
|
device_button = MDRectangleFlatIconButton(text=label, font_size=dp(fs), icon_size=dp(ics), on_release=self.input_device_action)
|
|
device_button.padding = [dp(bp), dp(bp), dp(bp), dp(bp)]; device_button.size_hint = [1.0, None]
|
|
if self.app.sideband.config["voice_input"] == device: device_button.icon = "check"
|
|
device_button.device = device
|
|
self.voice_settings_screen.ids.input_devices.add_widget(device_button)
|
|
self.listed_input_devices.append(device)
|
|
|
|
# Ringer devices
|
|
if not "system_default" in self.listed_ringer_devices:
|
|
default_ringer_button = MDRectangleFlatIconButton(text="System Default", font_size=dp(fs), icon_size=dp(ics), on_release=self.ringer_device_action)
|
|
default_ringer_button.device = None; default_ringer_button.size_hint = [1.0, None]
|
|
if self.app.sideband.config["voice_ringer"] == None: default_ringer_button.icon = "check"
|
|
self.voice_settings_screen.ids.ringer_devices.add_widget(default_ringer_button)
|
|
self.listed_ringer_devices.append("system_default")
|
|
|
|
for device in self.output_devices:
|
|
if not device in self.listed_ringer_devices:
|
|
device_str = device.replace("[", "").replace("]", "")
|
|
label = device_str if len(device_str) < ml else device_str[:ml-3]+"..."
|
|
device_button = MDRectangleFlatIconButton(text=label, font_size=dp(fs), icon_size=dp(ics), on_release=self.ringer_device_action)
|
|
device_button.padding = [dp(bp), dp(bp), dp(bp), dp(bp)]; device_button.size_hint = [1.0, None]
|
|
if self.app.sideband.config["voice_ringer"] == device: device_button.icon = "check"
|
|
device_button.device = device
|
|
self.voice_settings_screen.ids.ringer_devices.add_widget(device_button)
|
|
self.listed_ringer_devices.append(device)
|
|
|
|
def settings_save_action(self, sender=None, event=None):
|
|
self.app.sideband.config["voice_trusted_only"] = self.voice_settings_screen.ids.voice_trusted_only.active
|
|
self.app.sideband.config["voice_low_latency"] = self.voice_settings_screen.ids.voice_low_latency.active
|
|
self.app.sideband.save_configuration()
|
|
if self.app.sideband.telephone:
|
|
self.app.sideband.telephone.set_low_latency_output(self.app.sideband.config["voice_low_latency"])
|
|
|
|
def output_device_action(self, sender=None):
|
|
self.app.sideband.config["voice_output"] = sender.device
|
|
self.app.sideband.save_configuration()
|
|
for w in self.voice_settings_screen.ids.output_devices.children: w.icon = ""
|
|
sender.icon = "check"
|
|
if self.app.sideband.telephone:
|
|
self.app.sideband.telephone.set_speaker(self.app.sideband.config["voice_output"])
|
|
|
|
def input_device_action(self, sender=None):
|
|
self.app.sideband.config["voice_input"] = sender.device
|
|
self.app.sideband.save_configuration()
|
|
for w in self.voice_settings_screen.ids.input_devices.children: w.icon = ""
|
|
sender.icon = "check"
|
|
if self.app.sideband.telephone:
|
|
self.app.sideband.telephone.set_microphone(self.app.sideband.config["voice_input"])
|
|
|
|
def ringer_device_action(self, sender=None):
|
|
self.app.sideband.config["voice_ringer"] = sender.device
|
|
self.app.sideband.save_configuration()
|
|
for w in self.voice_settings_screen.ids.ringer_devices.children: w.icon = ""
|
|
sender.icon = "check"
|
|
if self.app.sideband.telephone:
|
|
self.app.sideband.telephone.set_ringer(self.app.sideband.config["voice_ringer"])
|
|
|
|
|
|
### Call log
|
|
######################################
|
|
|
|
def update_call_log(self):
|
|
if self.log_list == None:
|
|
self.log_list = CallList()
|
|
self.screen.ids.log_list_container.add_widget(self.log_list)
|
|
|
|
self.update_log_list()
|
|
self.last_log_update = time.time()
|
|
|
|
def update_log_list(self):
|
|
if not self.app.sideband.telephone: self.log_list.data = []
|
|
else:
|
|
LogEntry.owner = self
|
|
call_log = self.app.sideband.telephone.get_call_log()
|
|
call_log.sort(key=lambda e: e["time"], reverse=True)
|
|
data = []
|
|
for entry in call_log:
|
|
try:
|
|
at = entry["time"]
|
|
td = int(time.time())-int(at)
|
|
evt = entry["event"]
|
|
idnt = entry["identity"]
|
|
|
|
if not idnt in self.log_name_cache: self.log_name_cache[idnt] = self.app.sideband.voice_display_name(idnt)
|
|
name = multilingual_markup(escape_markup(str(self.log_name_cache[idnt])).encode("utf-8")).decode("utf-8")
|
|
|
|
icon = None
|
|
if evt == "incoming-missed": icon = "phone-missed"
|
|
elif evt == "outgoing-failure": icon = "phone-remove"
|
|
elif evt == "incoming-success": icon = "phone-incoming"
|
|
elif evt == "outgoing-success": icon = "phone-outgoing"
|
|
|
|
time_str = None
|
|
if td < 60: time_str = "Just now"
|
|
elif td < 60*60: td = int((td//60)*60)
|
|
elif td < 60*60*24: td = int((td//60)*60)
|
|
elif td < 60*60*24*7: td = int((td//(60*60*24))*(60*60*24))
|
|
else: time_str = time.strftime(ts_format_date, time.localtime(at))
|
|
|
|
if time_str == None: time_str = f"{RNS.prettytime(td)} ago"
|
|
|
|
if icon:
|
|
info = f"{name} • [i]{time_str}[/i]"
|
|
entry = {"icon": icon, "text": f"{info}", "identity": idnt}
|
|
data.append(entry)
|
|
|
|
except Exception as e:
|
|
RNS.log(f"An error occurred while updating the call log list: {e}", RNS.LOG_ERROR)
|
|
RNS.trace_exception(e)
|
|
|
|
self.log_list.data = data
|
|
|
|
class LogEntry(OneLineAvatarIconListItem):
|
|
owner = None
|
|
|
|
icon = StringProperty()
|
|
# ti_color = OptionProperty(None, options=theme_text_color_options)
|
|
# icon_fg = Property(None, allownone=True)
|
|
# icon_bg = Property(None, allownone=True)
|
|
|
|
def __init__(self):
|
|
super().__init__()
|
|
self.bind(on_release=self.dial_action)
|
|
# self.ids.left_icon.bind(on_release=self.left_icon_action)
|
|
# self.ids.right_icon.bind(on_release=self.right_icon_action)
|
|
|
|
def dial_action(self, sender=None):
|
|
self.owner.log_dial_action(self)
|
|
|
|
def left_icon_action(self, sender):
|
|
pass
|
|
|
|
def right_icon_action(self, sender):
|
|
pass
|
|
|
|
class CallList(MDRecycleView):
|
|
def __init__(self):
|
|
super().__init__()
|
|
self.data = []
|
|
|
|
Builder.load_string("""
|
|
<LogEntry>
|
|
IconLeftWidget:
|
|
id: left_icon
|
|
# theme_icon_color: root.ti_color
|
|
# icon_color: root.icon_fg
|
|
# md_bg_color: root.icon_bg
|
|
icon: root.icon
|
|
_default_icon_pad: dp(14)
|
|
icon_size: dp(24)
|
|
|
|
# IconRightWidget:
|
|
# id: right_icon
|
|
# icon: "dots-vertical"
|
|
|
|
<CallList>:
|
|
id: calls_scrollview
|
|
viewclass: "LogEntry"
|
|
effect_cls: "ScrollEffect"
|
|
|
|
RecycleBoxLayout:
|
|
default_size: None, dp(57)
|
|
default_size_hint: 1, None
|
|
size_hint_y: None
|
|
height: self.minimum_height
|
|
orientation: "vertical"
|
|
""")
|
|
|
|
layout_voice_screen = """
|
|
MDScreen:
|
|
name: "voice_screen"
|
|
|
|
BoxLayout:
|
|
orientation: "vertical"
|
|
|
|
MDTopAppBar:
|
|
title: "Voice"
|
|
anchor_title: "left"
|
|
elevation: 0
|
|
left_action_items:
|
|
[['menu', lambda x: root.app.nav_drawer.set_state("open")]]
|
|
right_action_items:
|
|
[
|
|
['wrench-cog', lambda x: root.delegate.settings_action(self)],
|
|
['close', lambda x: root.app.close_any_action(self)],
|
|
]
|
|
|
|
MDBoxLayout:
|
|
orientation: "vertical"
|
|
size_hint_y: None
|
|
height: self.minimum_height
|
|
padding: [dp(28), dp(12), dp(28), dp(16)]
|
|
|
|
MDBoxLayout:
|
|
orientation: "vertical"
|
|
# spacing: "24dp"
|
|
size_hint_y: None
|
|
height: self.minimum_height
|
|
padding: [dp(0), dp(12), dp(0), dp(0)]
|
|
|
|
MDTextField:
|
|
id: identity_hash
|
|
hint_text: "Identity hash"
|
|
mode: "rectangle"
|
|
# size_hint: [1.0, None]
|
|
pos_hint: {"center_x": .5}
|
|
max_text_length: 32
|
|
on_text: root.delegate.target_input_action(self)
|
|
|
|
MDBoxLayout:
|
|
orientation: "vertical"
|
|
spacing: "24dp"
|
|
size_hint_y: None
|
|
height: self.minimum_height
|
|
padding: [dp(0), dp(35), dp(0), dp(14)]
|
|
|
|
MDBoxLayout:
|
|
orientation: "horizontal"
|
|
spacing: "24dp"
|
|
size_hint_y: None
|
|
height: self.minimum_height
|
|
padding: [dp(0), dp(0), dp(0), dp(0)]
|
|
|
|
MDRectangleFlatIconButton:
|
|
id: call_profile_button
|
|
icon: "account-voice"
|
|
text: "HQ"
|
|
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.call_profile_action(self)
|
|
disabled: False
|
|
|
|
MDRectangleFlatIconButton:
|
|
id: dial_button
|
|
icon: "phone-outgoing"
|
|
text: "Call"
|
|
padding: [dp(0), dp(14), dp(0), dp(14)]
|
|
icon_size: dp(24)
|
|
font_size: dp(16)
|
|
size_hint: [2.0, None]
|
|
on_release: root.delegate.dial_action(self)
|
|
disabled: True
|
|
|
|
MDRectangleFlatIconButton:
|
|
id: reject_button
|
|
icon: "phone-cancel"
|
|
text: "Reject"
|
|
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.reject_action(self)
|
|
disabled: True
|
|
|
|
MDSeparator:
|
|
orientation: "horizontal"
|
|
height: dp(1)
|
|
|
|
MDBoxLayout:
|
|
orientation: "vertical"
|
|
id: log_list_container
|
|
|
|
MDSeparator:
|
|
orientation: "horizontal"
|
|
height: dp(1)
|
|
|
|
MDBoxLayout:
|
|
orientation: "vertical"
|
|
size_hint_y: None
|
|
height: self.minimum_height
|
|
padding: [dp(28), dp(24), dp(28), dp(24)]
|
|
|
|
MDRectangleFlatIconButton:
|
|
id: clear_log_button
|
|
icon: "playlist-remove"
|
|
text: "Clear Log"
|
|
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.clear_log_action(self)
|
|
"""
|
|
|
|
layout_voice_settings_screen = """
|
|
MDScreen:
|
|
name: "voice_settings_screen"
|
|
|
|
BoxLayout:
|
|
orientation: "vertical"
|
|
|
|
MDTopAppBar:
|
|
id: top_bar
|
|
title: "Voice Configuration"
|
|
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_sub_voice_action(self)],
|
|
]
|
|
|
|
MDScrollView:
|
|
id: voice_settings_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
|
|
|
|
MDBoxLayout:
|
|
orientation: "vertical"
|
|
size_hint_y: None
|
|
height: self.minimum_height
|
|
padding: [dp(28), dp(48), dp(28), dp(16)]
|
|
|
|
MDLabel:
|
|
text: "Call Handling"
|
|
font_style: "H6"
|
|
height: self.texture_size[1]
|
|
padding: [dp(0), dp(0), dp(0), dp(12)]
|
|
|
|
MDLabel:
|
|
id: voice_settings_info
|
|
markup: True
|
|
text: "You can block calls from all other callers than contacts marked as trusted, by enabling the following option."
|
|
size_hint_y: None
|
|
text_size: self.width, None
|
|
height: self.texture_size[1]
|
|
padding: [dp(0), dp(16), dp(0), dp(16)]
|
|
|
|
MDBoxLayout:
|
|
orientation: "horizontal"
|
|
padding: [0,0,dp(24),0]
|
|
size_hint_y: None
|
|
height: dp(48)
|
|
|
|
MDLabel:
|
|
text: "Block non-trusted callers"
|
|
font_style: "H6"
|
|
|
|
MDSwitch:
|
|
id: voice_trusted_only
|
|
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: "Low-latency output"
|
|
font_style: "H6"
|
|
|
|
MDSwitch:
|
|
id: voice_low_latency
|
|
pos_hint: {"center_y": 0.3}
|
|
active: False
|
|
|
|
MDLabel:
|
|
text: "Audio Devices"
|
|
font_style: "H6"
|
|
padding: [dp(0), dp(96), dp(0), dp(12)]
|
|
|
|
MDLabel:
|
|
id: voice_settings_info
|
|
markup: True
|
|
text: "You can configure which audio devices Sideband will use for voice calls, by selecting either the system default device, or specific audio devices available."
|
|
size_hint_y: None
|
|
text_size: self.width, None
|
|
height: self.texture_size[1]
|
|
padding: [dp(0), dp(64), dp(0), dp(32)]
|
|
|
|
MDLabel:
|
|
text: "[b]Output[/b]"
|
|
font_size: dp(18)
|
|
markup: True
|
|
|
|
MDBoxLayout:
|
|
id: output_devices
|
|
orientation: "vertical"
|
|
spacing: "12dp"
|
|
size_hint_y: None
|
|
height: self.minimum_height
|
|
padding: [dp(0), dp(24), dp(0), dp(48)]
|
|
|
|
# MDRectangleFlatIconButton:
|
|
# id: output_default_button
|
|
# text: "System Default"
|
|
# 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.output_device_action(self)
|
|
# disabled: False
|
|
|
|
MDLabel:
|
|
text: "[b]Input[/b]"
|
|
font_size: dp(18)
|
|
markup: True
|
|
|
|
MDBoxLayout:
|
|
id: input_devices
|
|
orientation: "vertical"
|
|
spacing: "12dp"
|
|
size_hint_y: None
|
|
height: self.minimum_height
|
|
padding: [dp(0), dp(24), dp(0), dp(48)]
|
|
|
|
MDLabel:
|
|
text: "[b]Ringer[/b]"
|
|
font_size: dp(18)
|
|
markup: True
|
|
|
|
MDBoxLayout:
|
|
id: ringer_devices
|
|
orientation: "vertical"
|
|
spacing: "12dp"
|
|
size_hint_y: None
|
|
height: self.minimum_height
|
|
padding: [dp(0), dp(24), dp(0), dp(48)]
|
|
|
|
""" |