mirror of
https://github.com/markqvist/Sideband.git
synced 2025-07-22 06:29:14 -04:00
Added basic LXST voice call UI
This commit is contained in:
parent
a0a03c9eba
commit
143f440df7
5 changed files with 298 additions and 79 deletions
|
@ -1,6 +1,6 @@
|
||||||
__debug_build__ = False
|
__debug_build__ = False
|
||||||
__disable_shaders__ = False
|
__disable_shaders__ = False
|
||||||
__version__ = "1.4.0"
|
__version__ = "1.5.0"
|
||||||
__variant__ = ""
|
__variant__ = ""
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
|
@ -1597,14 +1597,18 @@ class SidebandApp(MDApp):
|
||||||
self.conversation_action(item)
|
self.conversation_action(item)
|
||||||
|
|
||||||
def conversation_action(self, sender):
|
def conversation_action(self, sender):
|
||||||
|
if sender.conv_type == self.sideband.CONV_P2P:
|
||||||
context_dest = sender.sb_uid
|
context_dest = sender.sb_uid
|
||||||
def cb(dt):
|
def cb(dt): self.open_conversation(context_dest)
|
||||||
self.open_conversation(context_dest)
|
def cbu(dt): self.conversations_view.update()
|
||||||
def cbu(dt):
|
|
||||||
self.conversations_view.update()
|
|
||||||
Clock.schedule_once(cb, 0.15)
|
Clock.schedule_once(cb, 0.15)
|
||||||
Clock.schedule_once(cbu, 0.15+0.25)
|
Clock.schedule_once(cbu, 0.15+0.25)
|
||||||
|
|
||||||
|
elif sender.conv_type == self.sideband.CONV_VOICE:
|
||||||
|
identity_hash = sender.sb_uid
|
||||||
|
def cb(dt): self.dial_action(identity_hash)
|
||||||
|
Clock.schedule_once(cb, 0.15)
|
||||||
|
|
||||||
def open_conversation(self, context_dest, direction="left"):
|
def open_conversation(self, context_dest, direction="left"):
|
||||||
self.rec_dialog_is_open = False
|
self.rec_dialog_is_open = False
|
||||||
self.outbound_mode_paper = False
|
self.outbound_mode_paper = False
|
||||||
|
@ -2750,7 +2754,8 @@ class SidebandApp(MDApp):
|
||||||
n_address = dialog.d_content.ids["n_address_field"].text
|
n_address = dialog.d_content.ids["n_address_field"].text
|
||||||
n_name = dialog.d_content.ids["n_name_field"].text
|
n_name = dialog.d_content.ids["n_name_field"].text
|
||||||
n_trusted = dialog.d_content.ids["n_trusted"].active
|
n_trusted = dialog.d_content.ids["n_trusted"].active
|
||||||
new_result = self.sideband.new_conversation(n_address, n_name, n_trusted)
|
n_voice_only = dialog.d_content.ids["n_voice_only"].active
|
||||||
|
new_result = self.sideband.new_conversation(n_address, n_name, n_trusted, n_voice_only)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
RNS.log("Error while creating conversation: "+str(e), RNS.LOG_ERROR)
|
RNS.log("Error while creating conversation: "+str(e), RNS.LOG_ERROR)
|
||||||
|
@ -5257,7 +5262,7 @@ class SidebandApp(MDApp):
|
||||||
self.voice_screen = Voice(self)
|
self.voice_screen = Voice(self)
|
||||||
self.voice_ready = True
|
self.voice_ready = True
|
||||||
|
|
||||||
def voice_open(self, sender=None, direction="left", no_transition=False):
|
def voice_open(self, sender=None, direction="left", no_transition=False, dial_on_complete=None):
|
||||||
if no_transition:
|
if no_transition:
|
||||||
self.root.ids.screen_manager.transition = self.no_transition
|
self.root.ids.screen_manager.transition = self.no_transition
|
||||||
else:
|
else:
|
||||||
|
@ -5271,21 +5276,29 @@ class SidebandApp(MDApp):
|
||||||
if no_transition:
|
if no_transition:
|
||||||
self.root.ids.screen_manager.transition = self.slide_transition
|
self.root.ids.screen_manager.transition = self.slide_transition
|
||||||
|
|
||||||
def voice_action(self, sender=None, direction="left"):
|
self.voice_screen.update_call_status()
|
||||||
|
if dial_on_complete:
|
||||||
|
self.voice_screen.dial_target = dial_on_complete
|
||||||
|
self.voice_screen.screen.ids.identity_hash.text = RNS.hexrep(dial_on_complete, delimit=False)
|
||||||
|
Clock.schedule_once(self.voice_screen.dial_action, 0.25)
|
||||||
|
|
||||||
|
def voice_action(self, sender=None, direction="left", dial_on_complete=None):
|
||||||
if self.voice_ready:
|
if self.voice_ready:
|
||||||
self.voice_open(direction=direction)
|
self.voice_open(direction=direction, dial_on_complete=dial_on_complete)
|
||||||
else:
|
else:
|
||||||
self.loader_action(direction=direction)
|
self.loader_action(direction=direction)
|
||||||
def final(dt):
|
def final(dt):
|
||||||
self.voice_init()
|
self.voice_init()
|
||||||
def o(dt):
|
def o(dt):
|
||||||
self.voice_open(no_transition=True)
|
self.voice_open(no_transition=True, dial_on_complete=dial_on_complete)
|
||||||
Clock.schedule_once(o, ll_ot)
|
Clock.schedule_once(o, ll_ot)
|
||||||
Clock.schedule_once(final, ll_ft)
|
Clock.schedule_once(final, ll_ft)
|
||||||
|
|
||||||
def close_sub_voice_action(self, sender=None):
|
def close_sub_voice_action(self, sender=None):
|
||||||
self.voice_action(direction="right")
|
self.voice_action(direction="right")
|
||||||
|
|
||||||
|
def dial_action(self, identity_hash):
|
||||||
|
self.voice_action(dial_on_complete=identity_hash)
|
||||||
|
|
||||||
### Telemetry Screen
|
### Telemetry Screen
|
||||||
######################################
|
######################################
|
||||||
|
|
|
@ -107,6 +107,7 @@ class SidebandCore():
|
||||||
CONV_P2P = 0x01
|
CONV_P2P = 0x01
|
||||||
CONV_GROUP = 0x02
|
CONV_GROUP = 0x02
|
||||||
CONV_BROADCAST = 0x03
|
CONV_BROADCAST = 0x03
|
||||||
|
CONV_VOICE = 0x04
|
||||||
|
|
||||||
MAX_ANNOUNCES = 24
|
MAX_ANNOUNCES = 24
|
||||||
|
|
||||||
|
@ -2639,6 +2640,7 @@ 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,
|
||||||
|
"type": entry[4],
|
||||||
"trust": entry[5],
|
"trust": entry[5],
|
||||||
"data": data,
|
"data": data,
|
||||||
}
|
}
|
||||||
|
@ -2790,6 +2792,27 @@ class SidebandCore():
|
||||||
|
|
||||||
self.__event_conversations_changed()
|
self.__event_conversations_changed()
|
||||||
|
|
||||||
|
def _db_create_voice_object(self, identity_hash, name = None, trust = False):
|
||||||
|
RNS.log("Creating voice object for "+RNS.prettyhexrep(identity_hash), RNS.LOG_DEBUG)
|
||||||
|
with self.db_lock:
|
||||||
|
db = self.__db_connect()
|
||||||
|
dbc = db.cursor()
|
||||||
|
|
||||||
|
def_name = "".encode("utf-8")
|
||||||
|
query = "INSERT INTO conv (dest_context, last_tx, last_rx, unread, type, trust, name, data) values (?, ?, ?, ?, ?, ?, ?, ?)"
|
||||||
|
data = (identity_hash, 0, time.time(), 0, SidebandCore.CONV_VOICE, 0, def_name, msgpack.packb(None))
|
||||||
|
|
||||||
|
dbc.execute(query, data)
|
||||||
|
db.commit()
|
||||||
|
|
||||||
|
if trust:
|
||||||
|
self._db_conversation_set_trusted(identity_hash, True)
|
||||||
|
|
||||||
|
if name != None and name != "":
|
||||||
|
self._db_conversation_set_name(identity_hash, name)
|
||||||
|
|
||||||
|
self.__event_conversations_changed()
|
||||||
|
|
||||||
def _db_delete_message(self, msg_hash):
|
def _db_delete_message(self, msg_hash):
|
||||||
RNS.log("Deleting message "+RNS.prettyhexrep(msg_hash))
|
RNS.log("Deleting message "+RNS.prettyhexrep(msg_hash))
|
||||||
with self.db_lock:
|
with self.db_lock:
|
||||||
|
@ -4630,7 +4653,7 @@ class SidebandCore():
|
||||||
RNS.log("Error while sending message: "+str(e), RNS.LOG_ERROR)
|
RNS.log("Error while sending message: "+str(e), RNS.LOG_ERROR)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def new_conversation(self, dest_str, name = "", trusted = False):
|
def new_conversation(self, dest_str, name = "", trusted = False, voice_only = False):
|
||||||
if len(dest_str) != RNS.Reticulum.TRUNCATED_HASHLENGTH//8*2:
|
if len(dest_str) != RNS.Reticulum.TRUNCATED_HASHLENGTH//8*2:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
@ -4640,7 +4663,8 @@ class SidebandCore():
|
||||||
RNS.log("Cannot create conversation with own LXMF address", RNS.LOG_ERROR)
|
RNS.log("Cannot create conversation with own LXMF address", RNS.LOG_ERROR)
|
||||||
return False
|
return False
|
||||||
else:
|
else:
|
||||||
self._db_create_conversation(addr_b, name, trusted)
|
if not voice_only: self._db_create_conversation(addr_b, name, trusted)
|
||||||
|
else: self._db_create_voice_object(addr_b, name, trusted)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
RNS.log("Error while creating conversation: "+str(e), RNS.LOG_ERROR)
|
RNS.log("Error while creating conversation: "+str(e), RNS.LOG_ERROR)
|
||||||
|
|
|
@ -92,18 +92,13 @@ class ReticulumTelephone():
|
||||||
self.telephone.teardown()
|
self.telephone.teardown()
|
||||||
self.telephone = None
|
self.telephone = None
|
||||||
|
|
||||||
|
def hangup(self): self.telephone.hangup()
|
||||||
|
def answer(self): self.telephone.answer(self.caller)
|
||||||
|
def set_busy(self, busy): self.telephone.set_busy(busy)
|
||||||
|
|
||||||
def dial(self, identity_hash):
|
def dial(self, identity_hash):
|
||||||
self.last_dialled_identity_hash = identity_hash
|
self.last_dialled_identity_hash = identity_hash
|
||||||
self.telephone.set_busy(True)
|
|
||||||
identity_hash = bytes.fromhex(identity_hash)
|
|
||||||
destination_hash = RNS.Destination.hash_from_name_and_identity("lxst.telephony", identity_hash)
|
destination_hash = RNS.Destination.hash_from_name_and_identity("lxst.telephony", identity_hash)
|
||||||
if not RNS.Transport.has_path(destination_hash):
|
|
||||||
RNS.Transport.request_path(destination_hash)
|
|
||||||
def spincheck(): return RNS.Transport.has_path(destination_hash)
|
|
||||||
self.__spin(spincheck, "Requesting path for call to "+RNS.prettyhexrep(identity_hash), self.path_time)
|
|
||||||
if not spincheck(): RNS.log("Path request timed out", RNS.LOG_DEBUG)
|
|
||||||
|
|
||||||
self.telephone.set_busy(False)
|
|
||||||
if RNS.Transport.has_path(destination_hash):
|
if RNS.Transport.has_path(destination_hash):
|
||||||
call_hops = RNS.Transport.hops_to(destination_hash)
|
call_hops = RNS.Transport.hops_to(destination_hash)
|
||||||
cs = "" if call_hops == 1 else "s"
|
cs = "" if call_hops == 1 else "s"
|
||||||
|
@ -111,7 +106,7 @@ class ReticulumTelephone():
|
||||||
identity = RNS.Identity.recall(destination_hash)
|
identity = RNS.Identity.recall(destination_hash)
|
||||||
self.call(identity)
|
self.call(identity)
|
||||||
else:
|
else:
|
||||||
pass
|
return "no_path"
|
||||||
|
|
||||||
def redial(self, args=None):
|
def redial(self, args=None):
|
||||||
if self.last_dialled_identity_hash: self.dial(self.last_dialled_identity_hash)
|
if self.last_dialled_identity_hash: self.dial(self.last_dialled_identity_hash)
|
||||||
|
|
|
@ -6,6 +6,7 @@ from kivy.uix.boxlayout import BoxLayout
|
||||||
from kivy.properties import StringProperty, BooleanProperty
|
from kivy.properties import StringProperty, BooleanProperty
|
||||||
from kivymd.uix.list import MDList, IconLeftWidget, IconRightWidget, OneLineAvatarIconListItem
|
from kivymd.uix.list import MDList, IconLeftWidget, IconRightWidget, OneLineAvatarIconListItem
|
||||||
from kivymd.uix.menu import MDDropdownMenu
|
from kivymd.uix.menu import MDDropdownMenu
|
||||||
|
from kivymd.toast import toast
|
||||||
from kivy.uix.gridlayout import GridLayout
|
from kivy.uix.gridlayout import GridLayout
|
||||||
from kivy.uix.boxlayout import BoxLayout
|
from kivy.uix.boxlayout import BoxLayout
|
||||||
from kivy.clock import Clock
|
from kivy.clock import Clock
|
||||||
|
@ -53,6 +54,7 @@ class Conversations():
|
||||||
self.app.root.ids.screen_manager.add_widget(self.screen)
|
self.app.root.ids.screen_manager.add_widget(self.screen)
|
||||||
|
|
||||||
self.conversation_dropdown = None
|
self.conversation_dropdown = None
|
||||||
|
self.voice_dropdown = None
|
||||||
self.delete_dialog = None
|
self.delete_dialog = None
|
||||||
self.clear_dialog = None
|
self.clear_dialog = None
|
||||||
self.clear_telemetry_dialog = None
|
self.clear_telemetry_dialog = None
|
||||||
|
@ -91,6 +93,7 @@ class Conversations():
|
||||||
self.app.sideband.setstate("wants.viewupdate.conversations", False)
|
self.app.sideband.setstate("wants.viewupdate.conversations", False)
|
||||||
|
|
||||||
def trust_icon(self, conv):
|
def trust_icon(self, conv):
|
||||||
|
conv_type = conv["type"]
|
||||||
context_dest = conv["dest"]
|
context_dest = conv["dest"]
|
||||||
unread = conv["unread"]
|
unread = conv["unread"]
|
||||||
appearance = self.app.sideband.peer_appearance(context_dest, conv=conv)
|
appearance = self.app.sideband.peer_appearance(context_dest, conv=conv)
|
||||||
|
@ -105,6 +108,9 @@ class Conversations():
|
||||||
else:
|
else:
|
||||||
trust_icon = appearance[0] or da[0];
|
trust_icon = appearance[0] or da[0];
|
||||||
|
|
||||||
|
else:
|
||||||
|
if conv_type == self.app.sideband.CONV_VOICE:
|
||||||
|
trust_icon = "phone"
|
||||||
else:
|
else:
|
||||||
if self.app.sideband.requests_allowed_from(context_dest):
|
if self.app.sideband.requests_allowed_from(context_dest):
|
||||||
if unread:
|
if unread:
|
||||||
|
@ -166,6 +172,7 @@ class Conversations():
|
||||||
|
|
||||||
iconl._default_icon_pad = dp(ic_p)
|
iconl._default_icon_pad = dp(ic_p)
|
||||||
iconl.icon_size = dp(ic_s)
|
iconl.icon_size = dp(ic_s)
|
||||||
|
iconl.conv_type = conv["type"]
|
||||||
|
|
||||||
return iconl
|
return iconl
|
||||||
|
|
||||||
|
@ -187,6 +194,7 @@ class Conversations():
|
||||||
|
|
||||||
for conv in self.context_dests:
|
for conv in self.context_dests:
|
||||||
context_dest = conv["dest"]
|
context_dest = conv["dest"]
|
||||||
|
conv_type = conv["type"]
|
||||||
unread = conv["unread"]
|
unread = conv["unread"]
|
||||||
last_activity = conv["last_activity"]
|
last_activity = conv["last_activity"]
|
||||||
|
|
||||||
|
@ -203,6 +211,7 @@ class Conversations():
|
||||||
item.sb_uid = context_dest
|
item.sb_uid = context_dest
|
||||||
item.sb_unread = unread
|
item.sb_unread = unread
|
||||||
iconl.sb_uid = context_dest
|
iconl.sb_uid = context_dest
|
||||||
|
item.conv_type = conv_type
|
||||||
|
|
||||||
def gen_edit(item):
|
def gen_edit(item):
|
||||||
def x():
|
def x():
|
||||||
|
@ -366,23 +375,58 @@ class Conversations():
|
||||||
self.delete_dialog.open()
|
self.delete_dialog.open()
|
||||||
return x
|
return x
|
||||||
|
|
||||||
# def gen_move_to(item):
|
|
||||||
# def x():
|
|
||||||
# item.dmenu.dismiss()
|
|
||||||
# self.app.sideband.conversation_set_object(self.conversation_dropdown.context_dest, not self.app.sideband.is_object(self.conversation_dropdown.context_dest))
|
|
||||||
# self.app.conversations_view.update()
|
|
||||||
# return x
|
|
||||||
|
|
||||||
def gen_copy_addr(item):
|
def gen_copy_addr(item):
|
||||||
def x():
|
def x():
|
||||||
Clipboard.copy(RNS.hexrep(self.conversation_dropdown.context_dest, delimit=False))
|
Clipboard.copy(RNS.hexrep(self.conversation_dropdown.context_dest, delimit=False))
|
||||||
|
self.voice_dropdown.dismiss()
|
||||||
|
self.conversation_dropdown.dismiss()
|
||||||
|
return x
|
||||||
|
|
||||||
|
def gen_call(item):
|
||||||
|
def x():
|
||||||
|
identity = RNS.Identity.recall(self.conversation_dropdown.context_dest)
|
||||||
|
if identity: self.app.dial_action(identity.hash)
|
||||||
|
else: toast("Can't call, identity unknown")
|
||||||
item.dmenu.dismiss()
|
item.dmenu.dismiss()
|
||||||
return x
|
return x
|
||||||
|
|
||||||
item.iconr = IconRightWidget(icon="dots-vertical");
|
item.iconr = IconRightWidget(icon="dots-vertical");
|
||||||
|
|
||||||
|
if self.voice_dropdown == None:
|
||||||
|
dmi_h = 40
|
||||||
|
dmv_items = [
|
||||||
|
{
|
||||||
|
"viewclass": "OneLineListItem",
|
||||||
|
"text": "Edit",
|
||||||
|
"height": dp(dmi_h),
|
||||||
|
"on_release": gen_edit(item)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"text": "Copy Identity Hash",
|
||||||
|
"viewclass": "OneLineListItem",
|
||||||
|
"height": dp(dmi_h),
|
||||||
|
"on_release": gen_copy_addr(item)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"text": "Delete",
|
||||||
|
"viewclass": "OneLineListItem",
|
||||||
|
"height": dp(dmi_h),
|
||||||
|
"on_release": gen_del(item)
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
self.voice_dropdown = MDDropdownMenu(
|
||||||
|
caller=item.iconr,
|
||||||
|
items=dmv_items,
|
||||||
|
position="auto",
|
||||||
|
width=dp(256),
|
||||||
|
elevation=0,
|
||||||
|
radius=dp(3),
|
||||||
|
)
|
||||||
|
self.voice_dropdown.effect_cls = ScrollEffect
|
||||||
|
self.voice_dropdown.md_bg_color = self.app.color_hover
|
||||||
|
|
||||||
if self.conversation_dropdown == None:
|
if self.conversation_dropdown == None:
|
||||||
obj_str = "conversations" if is_object else "objects"
|
|
||||||
dmi_h = 40
|
dmi_h = 40
|
||||||
dm_items = [
|
dm_items = [
|
||||||
{
|
{
|
||||||
|
@ -391,18 +435,18 @@ class Conversations():
|
||||||
"height": dp(dmi_h),
|
"height": dp(dmi_h),
|
||||||
"on_release": gen_edit(item)
|
"on_release": gen_edit(item)
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"viewclass": "OneLineListItem",
|
||||||
|
"text": "Call",
|
||||||
|
"height": dp(dmi_h),
|
||||||
|
"on_release": gen_call(item)
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"text": "Copy Address",
|
"text": "Copy Address",
|
||||||
"viewclass": "OneLineListItem",
|
"viewclass": "OneLineListItem",
|
||||||
"height": dp(dmi_h),
|
"height": dp(dmi_h),
|
||||||
"on_release": gen_copy_addr(item)
|
"on_release": gen_copy_addr(item)
|
||||||
},
|
},
|
||||||
# {
|
|
||||||
# "text": "Move to objects",
|
|
||||||
# "viewclass": "OneLineListItem",
|
|
||||||
# "height": dp(dmi_h),
|
|
||||||
# "on_release": gen_move_to(item)
|
|
||||||
# },
|
|
||||||
{
|
{
|
||||||
"text": "Clear Messages",
|
"text": "Clear Messages",
|
||||||
"viewclass": "OneLineListItem",
|
"viewclass": "OneLineListItem",
|
||||||
|
@ -434,11 +478,15 @@ class Conversations():
|
||||||
self.conversation_dropdown.effect_cls = ScrollEffect
|
self.conversation_dropdown.effect_cls = ScrollEffect
|
||||||
self.conversation_dropdown.md_bg_color = self.app.color_hover
|
self.conversation_dropdown.md_bg_color = self.app.color_hover
|
||||||
|
|
||||||
|
if conv_type == self.app.sideband.CONV_VOICE:
|
||||||
|
item.dmenu = self.voice_dropdown
|
||||||
|
else:
|
||||||
item.dmenu = self.conversation_dropdown
|
item.dmenu = self.conversation_dropdown
|
||||||
|
|
||||||
def callback_factory(ref, dest):
|
def callback_factory(ref, dest):
|
||||||
def x(sender):
|
def x(sender):
|
||||||
self.conversation_dropdown.context_dest = dest
|
self.conversation_dropdown.context_dest = dest
|
||||||
|
self.voice_dropdown.context_dest = dest
|
||||||
ref.dmenu.caller = ref.iconr
|
ref.dmenu.caller = ref.iconr
|
||||||
ref.dmenu.open()
|
ref.dmenu.open()
|
||||||
return x
|
return x
|
||||||
|
@ -448,6 +496,7 @@ class Conversations():
|
||||||
item.add_widget(item.iconr)
|
item.add_widget(item.iconr)
|
||||||
|
|
||||||
item.trusted = self.app.sideband.is_trusted(context_dest, conv_data=existing_conv)
|
item.trusted = self.app.sideband.is_trusted(context_dest, conv_data=existing_conv)
|
||||||
|
item.conv_type = conv_type
|
||||||
|
|
||||||
self.added_item_dests.append(context_dest)
|
self.added_item_dests.append(context_dest)
|
||||||
self.list.add_widget(item)
|
self.list.add_widget(item)
|
||||||
|
@ -519,7 +568,7 @@ Builder.load_string("""
|
||||||
orientation: "vertical"
|
orientation: "vertical"
|
||||||
spacing: "24dp"
|
spacing: "24dp"
|
||||||
size_hint_y: None
|
size_hint_y: None
|
||||||
height: dp(250)
|
height: dp(260)
|
||||||
|
|
||||||
MDTextField:
|
MDTextField:
|
||||||
id: n_address_field
|
id: n_address_field
|
||||||
|
@ -540,7 +589,7 @@ Builder.load_string("""
|
||||||
orientation: "horizontal"
|
orientation: "horizontal"
|
||||||
size_hint_y: None
|
size_hint_y: None
|
||||||
padding: [0,0,dp(8),dp(24)]
|
padding: [0,0,dp(8),dp(24)]
|
||||||
height: dp(48)
|
height: dp(24)
|
||||||
MDLabel:
|
MDLabel:
|
||||||
id: "trusted_switch_label"
|
id: "trusted_switch_label"
|
||||||
text: "Trusted"
|
text: "Trusted"
|
||||||
|
@ -551,6 +600,21 @@ Builder.load_string("""
|
||||||
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(8),dp(24)]
|
||||||
|
height: dp(24)
|
||||||
|
MDLabel:
|
||||||
|
id: "trusted_switch_label"
|
||||||
|
text: "Voice Only"
|
||||||
|
font_style: "H6"
|
||||||
|
|
||||||
|
MDSwitch:
|
||||||
|
id: n_voice_only
|
||||||
|
pos_hint: {"center_y": 0.3}
|
||||||
|
active: False
|
||||||
|
|
||||||
<ConvSettings>
|
<ConvSettings>
|
||||||
orientation: "vertical"
|
orientation: "vertical"
|
||||||
spacing: "16dp"
|
spacing: "16dp"
|
||||||
|
|
|
@ -30,9 +30,10 @@ class Voice():
|
||||||
def __init__(self, app):
|
def __init__(self, app):
|
||||||
self.app = app
|
self.app = app
|
||||||
self.screen = None
|
self.screen = None
|
||||||
self.rnstatus_screen = None
|
self.settings_screen = None
|
||||||
self.rnstatus_instance = None
|
self.dial_target = None
|
||||||
self.logviewer_screen = None
|
self.ui_updater = None
|
||||||
|
self.path_requesting = None
|
||||||
|
|
||||||
if not self.app.root.ids.screen_manager.has_screen("voice_screen"):
|
if not self.app.root.ids.screen_manager.has_screen("voice_screen"):
|
||||||
self.screen = Builder.load_string(layout_voice_screen)
|
self.screen = Builder.load_string(layout_voice_screen)
|
||||||
|
@ -41,13 +42,131 @@ class Voice():
|
||||||
self.app.root.ids.screen_manager.add_widget(self.screen)
|
self.app.root.ids.screen_manager.add_widget(self.screen)
|
||||||
|
|
||||||
self.screen.ids.voice_scrollview.effect_cls = ScrollEffect
|
self.screen.ids.voice_scrollview.effect_cls = ScrollEffect
|
||||||
info = "Voice services UI"
|
# info = "Voice services UI"
|
||||||
info += ""
|
# info += ""
|
||||||
|
|
||||||
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]"
|
||||||
|
|
||||||
self.screen.ids.voice_info.text = info
|
# self.screen.ids.voice_info.text = info
|
||||||
|
|
||||||
|
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()
|
||||||
|
|
||||||
|
db = self.screen.ids.dial_button
|
||||||
|
ih = self.screen.ids.identity_hash
|
||||||
|
if self.app.sideband.voice_running:
|
||||||
|
telephone = self.app.sideband.telephone
|
||||||
|
if self.path_requesting:
|
||||||
|
db.disabled = True
|
||||||
|
ih.disabled = True
|
||||||
|
|
||||||
|
else:
|
||||||
|
if telephone.is_available:
|
||||||
|
ih.disabled = False
|
||||||
|
self.target_input_action(ih)
|
||||||
|
else:
|
||||||
|
ih.disabled = True
|
||||||
|
|
||||||
|
if telephone.is_in_call or telephone.call_is_connecting:
|
||||||
|
ih.disabled = True
|
||||||
|
db.disabled = False
|
||||||
|
db.text = "Hang up"
|
||||||
|
db.icon = "phone-hangup"
|
||||||
|
|
||||||
|
elif telephone.is_ringing:
|
||||||
|
ih.disabled = True
|
||||||
|
db.disabled = False
|
||||||
|
db.text = "Answer"
|
||||||
|
db.icon = "phone-ring"
|
||||||
|
if telephone.caller: ih.text = RNS.hexrep(telephone.caller.hash, delimit=False)
|
||||||
|
|
||||||
|
else:
|
||||||
|
db.disabled = True; db.text = "Voice calls disabled"
|
||||||
|
ih.disabled = True
|
||||||
|
|
||||||
|
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)
|
||||||
|
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 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)
|
||||||
|
self.app.sideband.telephone.dial(self.dial_target)
|
||||||
|
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()
|
||||||
|
|
||||||
layout_voice_screen = """
|
layout_voice_screen = """
|
||||||
MDScreen:
|
MDScreen:
|
||||||
|
@ -76,17 +195,21 @@ MDScreen:
|
||||||
height: self.minimum_height
|
height: self.minimum_height
|
||||||
padding: [dp(28), dp(32), dp(28), dp(16)]
|
padding: [dp(28), dp(32), dp(28), dp(16)]
|
||||||
|
|
||||||
# MDLabel:
|
MDBoxLayout:
|
||||||
# text: "Utilities & Tools"
|
orientation: "vertical"
|
||||||
# font_style: "H6"
|
# spacing: "24dp"
|
||||||
|
|
||||||
MDLabel:
|
|
||||||
id: voice_info
|
|
||||||
markup: True
|
|
||||||
text: ""
|
|
||||||
size_hint_y: None
|
size_hint_y: None
|
||||||
text_size: self.width, None
|
height: self.minimum_height
|
||||||
height: self.texture_size[1]
|
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:
|
MDBoxLayout:
|
||||||
orientation: "vertical"
|
orientation: "vertical"
|
||||||
|
@ -96,13 +219,13 @@ MDScreen:
|
||||||
padding: [dp(0), dp(35), dp(0), dp(35)]
|
padding: [dp(0), dp(35), dp(0), dp(35)]
|
||||||
|
|
||||||
MDRectangleFlatIconButton:
|
MDRectangleFlatIconButton:
|
||||||
id: rnstatus_button
|
id: dial_button
|
||||||
icon: "wifi-check"
|
icon: "phone-outgoing"
|
||||||
text: "Reticulum Status"
|
text: "Call"
|
||||||
padding: [dp(0), dp(14), dp(0), dp(14)]
|
padding: [dp(0), dp(14), dp(0), dp(14)]
|
||||||
icon_size: dp(24)
|
icon_size: dp(24)
|
||||||
font_size: dp(16)
|
font_size: dp(16)
|
||||||
size_hint: [1.0, None]
|
size_hint: [1.0, None]
|
||||||
on_release: root.delegate.rnstatus_action(self)
|
on_release: root.delegate.dial_action(self)
|
||||||
disabled: False
|
disabled: True
|
||||||
"""
|
"""
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue