mirror of
https://github.com/markqvist/Sideband.git
synced 2026-04-27 22:25:39 +00:00
Added simple call log to voice call screen
This commit is contained in:
parent
09400ec863
commit
8121de96d7
5 changed files with 266 additions and 59 deletions
|
|
@ -60,7 +60,7 @@ public class PythonActivity extends SDLActivity {
|
|||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
try { this.startIntent = getIntent(); }
|
||||
catch { Log.e(TAG, "Failed to get pending intent on activity create"); }
|
||||
catch (Exception e) { Log.e(TAG, "Failed to get pending intent on activity create"); }
|
||||
|
||||
Log.v(TAG, "PythonActivity onCreate running");
|
||||
resourceManager = new ResourceManager(this);
|
||||
|
|
|
|||
|
|
@ -2138,20 +2138,21 @@ class SidebandCore():
|
|||
elif "get_service_log" in call: connection.send(self.get_service_log())
|
||||
elif "start_voice" in call: connection.send(self.start_voice())
|
||||
elif "stop_voice" in call: connection.send(self.stop_voice())
|
||||
elif "telephone_is_available" in call: connection.send(self.telephone.is_available) if self.telephone else False
|
||||
elif "telephone_is_in_call" in call: connection.send(self.telephone.is_in_call) if self.telephone else False
|
||||
elif "telephone_call_is_connecting" in call: connection.send(self.telephone.call_is_connecting) if self.telephone else False
|
||||
elif "telephone_is_ringing" in call: connection.send(self.telephone.is_ringing) if self.telephone else False
|
||||
elif "telephone_caller_info" in call: connection.send(self.telephone.caller.hash) if self.telephone and self.telephone.caller else None
|
||||
elif "telephone_set_busy" in call: connection.send(self.telephone.set_busy(call["telephone_set_busy"])) if self.telephone else False
|
||||
elif "telephone_dial" in call: connection.send(self.telephone.dial(call["telephone_dial"])) if self.telephone else False
|
||||
elif "telephone_hangup" in call: connection.send(self.telephone.hangup()) if self.telephone else False
|
||||
elif "telephone_answer" in call: connection.send(self.telephone.answer()) if self.telephone else False
|
||||
elif "telephone_set_speaker" in call: connection.send(self.telephone.set_speaker(call["telephone_set_speaker"])) if self.telephone else False
|
||||
elif "telephone_set_microphone" in call: connection.send(self.telephone.set_microphone(call["telephone_set_microphone"])) if self.telephone else False
|
||||
elif "telephone_set_ringer" in call: connection.send(self.telephone.set_ringer(call["telephone_set_ringer"])) if self.telephone else False
|
||||
elif "telephone_set_low_latency_output" in call: connection.send(self.telephone.set_low_latency_output(call["telephone_set_low_latency_output"])) if self.telephone else False
|
||||
elif "telephone_announce" in call: connection.send(self.telephone.announce()) if self.telephone else False
|
||||
elif "telephone_is_available" in call: connection.send(self.telephone.is_available if self.telephone else False)
|
||||
elif "telephone_is_in_call" in call: connection.send(self.telephone.is_in_call if self.telephone else False)
|
||||
elif "telephone_call_is_connecting" in call: connection.send(self.telephone.call_is_connecting if self.telephone else False)
|
||||
elif "telephone_is_ringing" in call: connection.send(self.telephone.is_ringing if self.telephone else False)
|
||||
elif "telephone_caller_info" in call: connection.send(self.telephone.caller.hash if self.telephone and self.telephone.caller else None)
|
||||
elif "telephone_set_busy" in call: connection.send(self.telephone.set_busy(call["telephone_set_busy"]) if self.telephone else False)
|
||||
elif "telephone_dial" in call: connection.send(self.telephone.dial(call["telephone_dial"]) if self.telephone else False)
|
||||
elif "telephone_hangup" in call: connection.send(self.telephone.hangup() if self.telephone else False)
|
||||
elif "telephone_answer" in call: connection.send(self.telephone.answer() if self.telephone else False)
|
||||
elif "telephone_set_speaker" in call: connection.send(self.telephone.set_speaker(call["telephone_set_speaker"]) if self.telephone else False)
|
||||
elif "telephone_set_microphone" in call: connection.send(self.telephone.set_microphone(call["telephone_set_microphone"]) if self.telephone else False)
|
||||
elif "telephone_set_ringer" in call: connection.send(self.telephone.set_ringer(call["telephone_set_ringer"]) if self.telephone else False)
|
||||
elif "telephone_set_low_latency_output" in call: connection.send(self.telephone.set_low_latency_output(call["telephone_set_low_latency_output"]) if self.telephone else False)
|
||||
elif "telephone_announce" in call: connection.send(self.telephone.announce() if self.telephone else False)
|
||||
elif "telephone_get_call_log" in call: connection.send(self.telephone.get_call_log() if self.telephone else [])
|
||||
else:
|
||||
connection.send(None)
|
||||
|
||||
|
|
@ -5518,9 +5519,10 @@ class SidebandCore():
|
|||
RNS.log("Starting voice service", RNS.LOG_DEBUG)
|
||||
self.voice_running = True
|
||||
self.setstate("voice.running", self.voice_running)
|
||||
from .voice import ReticulumTelephone
|
||||
self.telephone = ReticulumTelephone(self.identity, owner=self, speaker=self.config["voice_output"], microphone=self.config["voice_input"], ringer=self.config["voice_ringer"])
|
||||
ringtone_path = os.path.join(self.asset_dir, "audio", "notifications", "soft1.opus")
|
||||
call_log_path = os.path.join(self.app_dir, "app_storage", "lxst_call_log")
|
||||
from .voice import ReticulumTelephone
|
||||
self.telephone = ReticulumTelephone(self.identity, owner=self, speaker=self.config["voice_output"], microphone=self.config["voice_input"], ringer=self.config["voice_ringer"], logpath=call_log_path)
|
||||
self.telephone.set_ringtone(ringtone_path)
|
||||
self.telephone.set_low_latency_output(self.config["voice_low_latency"])
|
||||
return True
|
||||
|
|
@ -5584,6 +5586,13 @@ class SidebandCore():
|
|||
if self.gui_foreground(): RNS.log("Squelching call notification since GUI is in foreground", RNS.LOG_DEBUG)
|
||||
else: self.notify(title="Incoming voice call", content=f"From {display_name}", group="LXST.Telephony", context_id="incoming_call")
|
||||
|
||||
def missed_call(self, remote_identity):
|
||||
display_name = self.voice_display_name(remote_identity.hash)
|
||||
self.setstate("voice.incoming_call", display_name)
|
||||
# if self.gui_foreground(): RNS.log("Squelching call notification since GUI is in foreground", RNS.LOG_DEBUG)
|
||||
# else: self.notify(title="Missed voice call", content=f"From {display_name}", group="LXST.Telephony", context_id="incoming_call")
|
||||
self.notify(title="Missed voice call", content=f"From {display_name}", group="LXST.Telephony", context_id="incoming_call")
|
||||
|
||||
rns_config = """# This template is used to generate a
|
||||
# running configuration for Sideband's
|
||||
# internal RNS instance. Incorrect changes
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import time
|
|||
from LXST._version import __version__
|
||||
from LXST.Primitives.Telephony import Telephone
|
||||
from RNS.vendor.configobj import ConfigObj
|
||||
import RNS.vendor.umsgpack as msgpack
|
||||
|
||||
class ReticulumTelephone():
|
||||
STATE_AVAILABLE = 0x00
|
||||
|
|
@ -22,7 +23,9 @@ class ReticulumTelephone():
|
|||
WAIT_TIME = 60
|
||||
PATH_TIME = 10
|
||||
|
||||
def __init__(self, identity, owner = None, service = False, speaker=None, microphone=None, ringer=None):
|
||||
CALL_LOG_KEEP = 30*24*60*60
|
||||
|
||||
def __init__(self, identity, owner = None, service = False, speaker=None, microphone=None, ringer=None, logpath=None):
|
||||
self.identity = identity
|
||||
self.service = service
|
||||
self.owner = owner
|
||||
|
|
@ -40,6 +43,8 @@ class ReticulumTelephone():
|
|||
self.speaker_device = speaker
|
||||
self.microphone_device = microphone
|
||||
self.ringer_device = ringer
|
||||
self.logpath = logpath
|
||||
self.call_log = None
|
||||
self.phonebook = {}
|
||||
self.aliases = {}
|
||||
self.names = {}
|
||||
|
|
@ -113,6 +118,54 @@ class ReticulumTelephone():
|
|||
def set_busy(self, busy): self.telephone.set_busy(busy)
|
||||
def set_low_latency_output(self, enabled): self.telephone.set_low_latency_output(enabled)
|
||||
|
||||
def get_call_log(self):
|
||||
if self.call_log: return self.call_log
|
||||
else:
|
||||
call_log = []
|
||||
try:
|
||||
if os.path.isfile(self.logpath):
|
||||
with open(self.logpath, "rb") as logfile:
|
||||
read_call_log = msgpack.unpackb(logfile.read())
|
||||
|
||||
for entry in read_call_log:
|
||||
age = time.time()-entry["time"]
|
||||
if age < self.CALL_LOG_KEEP: call_log.append(entry)
|
||||
|
||||
except Exception as e: RNS.log(f"Could not read call log file: {e}", RNS.LOG_ERROR)
|
||||
self.call_log = call_log
|
||||
return self.call_log
|
||||
|
||||
def log_call(self, event, identity):
|
||||
RNS.log(f"Logging call event {event} for {RNS.prettyhexrep(identity.hash)}", RNS.LOG_DEBUG)
|
||||
if self.logpath:
|
||||
try:
|
||||
if not os.path.isfile(self.logpath):
|
||||
try:
|
||||
with open(self.logpath, "wb") as logfile: logfile.write(msgpack.packb([]))
|
||||
except Exception as e: raise OSError("Could not create call log file")
|
||||
|
||||
call_log = []
|
||||
read_call_log = []
|
||||
try:
|
||||
with open(self.logpath, "rb") as logfile: read_call_log = msgpack.unpackb(logfile.read())
|
||||
except Exception as e:
|
||||
RNS.log(f"Error while reading call log file: {e}", RNS.LOG_ERROR)
|
||||
RNS.log(f"Call log file will be re-created", RNS.LOG_ERROR)
|
||||
|
||||
for entry in read_call_log:
|
||||
age = time.time()-entry["time"]
|
||||
if age < self.CALL_LOG_KEEP: call_log.append(entry)
|
||||
|
||||
entry = {"time": time.time(), "event": event, "identity": identity.hash}
|
||||
call_log.append(entry)
|
||||
|
||||
with open(self.logpath, "wb") as logfile: logfile.write(msgpack.packb(call_log))
|
||||
self.call_log = call_log
|
||||
|
||||
except Exception as e:
|
||||
RNS.log(f"An error occurred while updating call log: {e}", RNS.LOG_ERROR)
|
||||
RNS.trace_exception(e)
|
||||
|
||||
def dial(self, identity_hash):
|
||||
self.last_dialled_identity_hash = identity_hash
|
||||
destination_hash = RNS.Destination.hash_from_name_and_identity("lxst.telephony", identity_hash)
|
||||
|
|
@ -145,6 +198,10 @@ class ReticulumTelephone():
|
|||
self.owner.incoming_call(remote_identity)
|
||||
|
||||
def call_ended(self, remote_identity):
|
||||
call_was_connecting = self.call_is_connecting
|
||||
was_ringing = self.is_ringing
|
||||
was_in_call = self.is_in_call
|
||||
|
||||
if self.is_in_call or self.is_ringing or self.call_is_connecting:
|
||||
if self.is_in_call: RNS.log(f"Call with {RNS.prettyhexrep(self.caller.hash)} ended\n", RNS.LOG_DEBUG)
|
||||
if self.is_ringing: RNS.log(f"Call {self.direction} {RNS.prettyhexrep(self.caller.hash)} was not answered\n", RNS.LOG_DEBUG)
|
||||
|
|
@ -152,11 +209,23 @@ class ReticulumTelephone():
|
|||
self.direction = None
|
||||
self.state = self.STATE_AVAILABLE
|
||||
|
||||
if call_was_connecting: self.log_call("outgoing-failure", remote_identity)
|
||||
elif was_in_call: self.log_call("ongoing-ended", remote_identity)
|
||||
elif was_ringing:
|
||||
self.log_call("incoming-missed", remote_identity)
|
||||
self.owner.missed_call(remote_identity)
|
||||
|
||||
def call_established(self, remote_identity):
|
||||
call_was_connecting = self.call_is_connecting
|
||||
was_ringing = self.is_ringing
|
||||
|
||||
if self.call_is_connecting or self.is_ringing:
|
||||
self.state = self.STATE_IN_CALL
|
||||
RNS.log(f"Call established with {RNS.prettyhexrep(self.caller.hash)}", RNS.LOG_DEBUG)
|
||||
|
||||
if call_was_connecting: self.log_call("outgoing-success", remote_identity)
|
||||
elif was_ringing: self.log_call("incoming-success", remote_identity)
|
||||
|
||||
def __is_allowed(self, identity_hash):
|
||||
if self.owner.config["voice_trusted_only"]:
|
||||
return self.owner.voice_is_trusted(identity_hash)
|
||||
|
|
@ -202,4 +271,5 @@ class ReticulumTelephoneProxy():
|
|||
def set_microphone(self, microphone): return self.owner.service_rpc_request({"telephone_set_microphone": microphone })
|
||||
def set_ringer(self, ringer): return self.owner.service_rpc_request({"telephone_set_ringer": ringer })
|
||||
def set_low_latency_output(self, enabled): return self.owner.service_rpc_request({"telephone_set_low_latency_output": enabled})
|
||||
def announce(self): return self.owner.service_rpc_request({"telephone_announce": True})
|
||||
def announce(self): return self.owner.service_rpc_request({"telephone_announce": True})
|
||||
def get_call_log(self): return self.owner.service_rpc_request({"telephone_get_call_log": True})
|
||||
|
|
@ -6,6 +6,7 @@ from kivymd.uix.list import OneLineIconListItem, MDList, IconLeftWidget, IconRig
|
|||
from kivy.properties import StringProperty
|
||||
import re
|
||||
|
||||
ts_format_date = "%Y-%m-%d"
|
||||
ts_format = "%Y-%m-%d %H:%M:%S"
|
||||
file_ts_format = "%Y_%m_%d_%H_%M_%S"
|
||||
|
||||
|
|
|
|||
|
|
@ -12,6 +12,8 @@ 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
|
||||
|
|
@ -22,10 +24,16 @@ import threading
|
|||
from datetime import datetime
|
||||
|
||||
if RNS.vendor.platformutils.get_platform() == "android":
|
||||
from ui.helpers import ts_format
|
||||
from ui.helpers import ts_format_date
|
||||
from android.permissions import request_permissions, check_permission
|
||||
else:
|
||||
from .helpers import ts_format
|
||||
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
|
||||
|
||||
class Voice():
|
||||
def __init__(self, app):
|
||||
|
|
@ -37,6 +45,9 @@ class Voice():
|
|||
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 = []
|
||||
|
|
@ -46,9 +57,8 @@ class Voice():
|
|||
self.screen.app = self.app
|
||||
self.screen.delegate = self
|
||||
self.app.root.ids.screen_manager.add_widget(self.screen)
|
||||
self.update_call_log()
|
||||
|
||||
self.screen.ids.voice_scrollview.effect_cls = ScrollEffect
|
||||
|
||||
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)
|
||||
|
|
@ -89,6 +99,8 @@ class Voice():
|
|||
db.disabled = True; db.text = "Voice calls disabled"
|
||||
ih.disabled = True
|
||||
|
||||
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
|
||||
|
|
@ -170,7 +182,7 @@ class Voice():
|
|||
self.update_call_status()
|
||||
|
||||
|
||||
### settings screen
|
||||
### Settings screen
|
||||
######################################
|
||||
|
||||
def settings_action(self, sender=None):
|
||||
|
|
@ -297,6 +309,116 @@ class Voice():
|
|||
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):
|
||||
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-cancel"
|
||||
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}"}
|
||||
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):
|
||||
app = None
|
||||
owner_screen = None
|
||||
conversation_dropdown = None
|
||||
voice_dropdown = None
|
||||
clear_dialog = None
|
||||
clear_telemetry_dialog = None
|
||||
delete_dialog = 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.app.conversation_action)
|
||||
# self.ids.left_icon.bind(on_release=self.left_icon_action)
|
||||
# self.ids.right_icon.bind(on_release=self.right_icon_action)
|
||||
|
||||
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"
|
||||
|
|
@ -316,48 +438,53 @@ MDScreen:
|
|||
['close', lambda x: root.app.close_any_action(self)],
|
||||
]
|
||||
|
||||
ScrollView:
|
||||
id: voice_scrollview
|
||||
MDBoxLayout:
|
||||
orientation: "vertical"
|
||||
size_hint_y: None
|
||||
height: self.minimum_height
|
||||
padding: [dp(28), dp(32), dp(28), dp(16)]
|
||||
|
||||
MDBoxLayout:
|
||||
orientation: "vertical"
|
||||
# spacing: "24dp"
|
||||
size_hint_y: None
|
||||
height: self.minimum_height
|
||||
padding: [dp(28), dp(32), dp(28), dp(16)]
|
||||
padding: [dp(0), dp(12), dp(0), dp(0)]
|
||||
|
||||
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)
|
||||
|
||||
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(35)]
|
||||
|
||||
MDBoxLayout:
|
||||
orientation: "vertical"
|
||||
spacing: "24dp"
|
||||
size_hint_y: None
|
||||
height: self.minimum_height
|
||||
padding: [dp(0), dp(35), dp(0), dp(35)]
|
||||
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: [1.0, None]
|
||||
on_release: root.delegate.dial_action(self)
|
||||
disabled: True
|
||||
|
||||
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: [1.0, None]
|
||||
on_release: root.delegate.dial_action(self)
|
||||
disabled: True
|
||||
MDSeparator:
|
||||
orientation: "horizontal"
|
||||
height: dp(1)
|
||||
|
||||
MDBoxLayout:
|
||||
orientation: "vertical"
|
||||
id: log_list_container
|
||||
"""
|
||||
|
||||
layout_voice_settings_screen = """
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue