mirror of
https://github.com/markqvist/Sideband.git
synced 2024-12-24 23:19:28 -05:00
Added paper message support
This commit is contained in:
parent
933792b5d6
commit
0226cbd5c6
152
sbapp/main.py
152
sbapp/main.py
@ -1,6 +1,6 @@
|
|||||||
__debug_build__ = False
|
__debug_build__ = False
|
||||||
__disable_shaders__ = True
|
__disable_shaders__ = True
|
||||||
__version__ = "0.3.0"
|
__version__ = "0.4.0"
|
||||||
__variant__ = "beta"
|
__variant__ = "beta"
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
@ -382,6 +382,35 @@ class SidebandApp(MDApp):
|
|||||||
|
|
||||||
self.check_bluetooth_permissions()
|
self.check_bluetooth_permissions()
|
||||||
|
|
||||||
|
def on_new_intent(self, intent):
|
||||||
|
RNS.log("Received intent", RNS.LOG_DEBUG)
|
||||||
|
intent_action = intent.getAction()
|
||||||
|
action = None
|
||||||
|
data = None
|
||||||
|
|
||||||
|
if intent_action == "android.intent.action.WEB_SEARCH":
|
||||||
|
SearchManager = autoclass('android.app.SearchManager')
|
||||||
|
data = intent.getStringExtra(SearchManager.QUERY)
|
||||||
|
|
||||||
|
if data.lower().startswith(LXMF.LXMessage.URI_SCHEMA):
|
||||||
|
action = "lxm_uri"
|
||||||
|
|
||||||
|
if intent_action == "android.intent.action.VIEW":
|
||||||
|
data = intent.getData().toString()
|
||||||
|
if data.lower().startswith(LXMF.LXMessage.URI_SCHEMA):
|
||||||
|
action = "lxm_uri"
|
||||||
|
|
||||||
|
if action != None:
|
||||||
|
self.handle_action(action, data)
|
||||||
|
|
||||||
|
def handle_action(self, action, data):
|
||||||
|
if action == "lxm_uri":
|
||||||
|
self.ingest_lxm_uri(data)
|
||||||
|
|
||||||
|
def ingest_lxm_uri(self, lxm_uri):
|
||||||
|
RNS.log("Ingesting LXMF paper message from URI: "+str(lxm_uri), RNS.LOG_DEBUG)
|
||||||
|
self.sideband.lxm_ingest_uri(lxm_uri)
|
||||||
|
|
||||||
def build(self):
|
def build(self):
|
||||||
FONT_PATH = self.sideband.asset_dir+"/fonts"
|
FONT_PATH = self.sideband.asset_dir+"/fonts"
|
||||||
if RNS.vendor.platformutils.is_darwin():
|
if RNS.vendor.platformutils.is_darwin():
|
||||||
@ -396,6 +425,9 @@ class SidebandApp(MDApp):
|
|||||||
activity = autoclass('org.kivy.android.PythonActivity').mActivity
|
activity = autoclass('org.kivy.android.PythonActivity').mActivity
|
||||||
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED)
|
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED)
|
||||||
|
|
||||||
|
from android import activity as a_activity
|
||||||
|
a_activity.bind(on_new_intent=self.on_new_intent)
|
||||||
|
|
||||||
screen = Builder.load_string(root_layout)
|
screen = Builder.load_string(root_layout)
|
||||||
|
|
||||||
return screen
|
return screen
|
||||||
@ -435,6 +467,22 @@ class SidebandApp(MDApp):
|
|||||||
if self.conversations_view != None:
|
if self.conversations_view != None:
|
||||||
self.conversations_view.update()
|
self.conversations_view.update()
|
||||||
|
|
||||||
|
if self.sideband.getstate("lxm_uri_ingest.result"):
|
||||||
|
info_text = self.sideband.getstate("lxm_uri_ingest.result")
|
||||||
|
self.sideband.setstate("lxm_uri_ingest.result", False)
|
||||||
|
ok_button = MDRectangleFlatButton(text="OK",font_size=dp(18))
|
||||||
|
dialog = MDDialog(
|
||||||
|
title="Message Scan",
|
||||||
|
text=info_text,
|
||||||
|
buttons=[ ok_button ],
|
||||||
|
# elevation=0,
|
||||||
|
)
|
||||||
|
def dl_ok(s):
|
||||||
|
dialog.dismiss()
|
||||||
|
|
||||||
|
ok_button.bind(on_release=dl_ok)
|
||||||
|
dialog.open()
|
||||||
|
|
||||||
def on_start(self):
|
def on_start(self):
|
||||||
self.last_exit_event = time.time()
|
self.last_exit_event = time.time()
|
||||||
self.root.ids.screen_manager.transition.duration = 0.25
|
self.root.ids.screen_manager.transition.duration = 0.25
|
||||||
@ -597,6 +645,7 @@ class SidebandApp(MDApp):
|
|||||||
Clock.schedule_once(cb, 0.15)
|
Clock.schedule_once(cb, 0.15)
|
||||||
|
|
||||||
def open_conversation(self, context_dest):
|
def open_conversation(self, context_dest):
|
||||||
|
self.outbound_mode_paper = False
|
||||||
if self.sideband.config["propagation_by_default"]:
|
if self.sideband.config["propagation_by_default"]:
|
||||||
self.outbound_mode_propagation = True
|
self.outbound_mode_propagation = True
|
||||||
else:
|
else:
|
||||||
@ -662,11 +711,17 @@ class SidebandApp(MDApp):
|
|||||||
else:
|
else:
|
||||||
msg_content = self.root.ids.message_text.text
|
msg_content = self.root.ids.message_text.text
|
||||||
context_dest = self.root.ids.messages_scrollview.active_conversation
|
context_dest = self.root.ids.messages_scrollview.active_conversation
|
||||||
if self.sideband.send_message(msg_content, context_dest, self.outbound_mode_propagation):
|
if self.outbound_mode_paper:
|
||||||
|
if self.sideband.paper_message(msg_content, context_dest):
|
||||||
|
self.root.ids.message_text.text = ""
|
||||||
|
self.root.ids.messages_scrollview.scroll_y = 0
|
||||||
|
self.jobs(0)
|
||||||
|
|
||||||
|
elif self.sideband.send_message(msg_content, context_dest, self.outbound_mode_propagation):
|
||||||
self.root.ids.message_text.text = ""
|
self.root.ids.message_text.text = ""
|
||||||
|
|
||||||
self.root.ids.messages_scrollview.scroll_y = 0
|
self.root.ids.messages_scrollview.scroll_y = 0
|
||||||
self.jobs(0)
|
self.jobs(0)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
self.messages_view.send_error_dialog = MDDialog(
|
self.messages_view.send_error_dialog = MDDialog(
|
||||||
title="Error",
|
title="Error",
|
||||||
@ -688,23 +743,34 @@ class SidebandApp(MDApp):
|
|||||||
|
|
||||||
|
|
||||||
def message_propagation_action(self, sender):
|
def message_propagation_action(self, sender):
|
||||||
if self.outbound_mode_propagation:
|
if self.outbound_mode_paper:
|
||||||
|
self.outbound_mode_paper = False
|
||||||
self.outbound_mode_propagation = False
|
self.outbound_mode_propagation = False
|
||||||
else:
|
else:
|
||||||
self.outbound_mode_propagation = True
|
if self.outbound_mode_propagation:
|
||||||
|
self.outbound_mode_paper = True
|
||||||
|
self.outbound_mode_propagation = False
|
||||||
|
else:
|
||||||
|
self.outbound_mode_propagation = True
|
||||||
|
self.outbound_mode_paper = False
|
||||||
|
|
||||||
self.update_message_widgets()
|
self.update_message_widgets()
|
||||||
|
|
||||||
def update_message_widgets(self):
|
def update_message_widgets(self):
|
||||||
toolbar_items = self.root.ids.messages_toolbar.ids.right_actions.children
|
toolbar_items = self.root.ids.messages_toolbar.ids.right_actions.children
|
||||||
mode_item = toolbar_items[1]
|
mode_item = toolbar_items[1]
|
||||||
|
|
||||||
if not self.outbound_mode_propagation:
|
if self.outbound_mode_paper:
|
||||||
mode_item.icon = "lan-connect"
|
mode_item.icon = "qrcode"
|
||||||
self.root.ids.message_text.hint_text = "Write message for direct delivery"
|
self.root.ids.message_text.hint_text = "Paper message"
|
||||||
else:
|
else:
|
||||||
mode_item.icon = "upload-network"
|
if not self.outbound_mode_propagation:
|
||||||
self.root.ids.message_text.hint_text = "Write message for propagation"
|
mode_item.icon = "lan-connect"
|
||||||
# self.root.ids.message_text.hint_text = "Write message for delivery via propagation nodes"
|
self.root.ids.message_text.hint_text = "Message for direct delivery"
|
||||||
|
else:
|
||||||
|
mode_item.icon = "upload-network"
|
||||||
|
self.root.ids.message_text.hint_text = "Message for propagation"
|
||||||
|
# self.root.ids.message_text.hint_text = "Write message for delivery via propagation nodes"
|
||||||
|
|
||||||
def key_query_action(self, sender):
|
def key_query_action(self, sender):
|
||||||
context_dest = self.root.ids.messages_scrollview.active_conversation
|
context_dest = self.root.ids.messages_scrollview.active_conversation
|
||||||
@ -791,7 +857,51 @@ class SidebandApp(MDApp):
|
|||||||
self.connectivity_updater.cancel()
|
self.connectivity_updater.cancel()
|
||||||
self.connectivity_updater = Clock.schedule_interval(cs_updater, 1.0)
|
self.connectivity_updater = Clock.schedule_interval(cs_updater, 1.0)
|
||||||
|
|
||||||
|
def ingest_lxm_action(self, sender):
|
||||||
|
def cb(dt):
|
||||||
|
self.open_ingest_lxm_dialog(sender)
|
||||||
|
Clock.schedule_once(cb, 0.15)
|
||||||
|
|
||||||
|
def open_ingest_lxm_dialog(self, sender=None):
|
||||||
|
try:
|
||||||
|
cancel_button = MDRectangleFlatButton(text="Cancel",font_size=dp(18))
|
||||||
|
ingest_button = MDRectangleFlatButton(text="Read LXM",font_size=dp(18), theme_text_color="Custom", line_color=self.color_accept, text_color=self.color_accept)
|
||||||
|
|
||||||
|
dialog = MDDialog(
|
||||||
|
title="Ingest Paper Message",
|
||||||
|
text="You can read LXMF paper messages into this program by scanning a QR-code containing the message with your device camera or QR-scanner app, and then opening the resulting link in Sideband.\n\nAlternatively, you can copy an [b]lxm://[/b] link from any source to your clipboard, and ingest it using the [i]Read LXM[/i] button below.",
|
||||||
|
buttons=[ ingest_button, cancel_button ],
|
||||||
|
)
|
||||||
|
def dl_yes(s):
|
||||||
|
try:
|
||||||
|
lxm_uri = Clipboard.paste()
|
||||||
|
if not lxm_uri.lower().startswith(LXMF.LXMessage.URI_SCHEMA+"://"):
|
||||||
|
lxm_uri = LXMF.LXMessage.URI_SCHEMA+"://"+lxm_uri
|
||||||
|
|
||||||
|
self.ingest_lxm_uri(lxm_uri)
|
||||||
|
dialog.dismiss()
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
response = "Error ingesting message from URI: "+str(e)
|
||||||
|
RNS.log(response, RNS.LOG_ERROR)
|
||||||
|
self.sideband.setstate("lxm_uri_ingest.result", response)
|
||||||
|
dialog.dismiss()
|
||||||
|
|
||||||
|
def dl_no(s):
|
||||||
|
dialog.dismiss()
|
||||||
|
|
||||||
|
def dl_ds(s):
|
||||||
|
self.dialog_open = False
|
||||||
|
|
||||||
|
ingest_button.bind(on_release=dl_yes)
|
||||||
|
cancel_button.bind(on_release=dl_no)
|
||||||
|
|
||||||
|
dialog.bind(on_dismiss=dl_ds)
|
||||||
|
dialog.open()
|
||||||
|
self.dialog_open = True
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
RNS.log("Error while creating ingest LXM dialog: "+str(e), RNS.LOG_ERROR)
|
||||||
|
|
||||||
def lxmf_sync_action(self, sender):
|
def lxmf_sync_action(self, sender):
|
||||||
def cb(dt):
|
def cb(dt):
|
||||||
@ -921,7 +1031,7 @@ class SidebandApp(MDApp):
|
|||||||
self.root.ids.information_scrollview.effect_cls = ScrollEffect
|
self.root.ids.information_scrollview.effect_cls = ScrollEffect
|
||||||
self.root.ids.information_logo.icon = self.sideband.asset_dir+"/rns_256.png"
|
self.root.ids.information_logo.icon = self.sideband.asset_dir+"/rns_256.png"
|
||||||
|
|
||||||
info = "This is Sideband v"+__version__+" "+__variant__+", on RNS v"+RNS.__version__+"\n\nHumbly build using the following open components:\n\n - [b]Reticulum[/b] (MIT License)\n - [b]LXMF[/b] (MIT License)\n - [b]KivyMD[/b] (MIT License)\n - [b]Kivy[/b] (MIT License)\n - [b]Python[/b] (PSF License)"+"\n\nGo to [u][ref=link]https://unsigned.io/donate[/ref][/u] to support the project.\n\nThe Sideband app is Copyright (c) 2022 Mark Qvist / unsigned.io\n\nPermission is granted to freely share and distribute binary copies of Sideband v"+__version__+" "+__variant__+", so long as no payment or compensation is charged for said distribution or sharing.\n\nIf you were charged or paid anything for this copy of Sideband, please report it to [b]license@unsigned.io[/b].\n\nTHIS IS EXPERIMENTAL SOFTWARE - USE AT YOUR OWN RISK AND RESPONSIBILITY"
|
info = "This is Sideband v"+__version__+" "+__variant__+", on RNS v"+RNS.__version__+" and LXMF v"+LXMF.__version__+".\n\nHumbly build using the following open components:\n\n - [b]Reticulum[/b] (MIT License)\n - [b]LXMF[/b] (MIT License)\n - [b]KivyMD[/b] (MIT License)\n - [b]Kivy[/b] (MIT License)\n - [b]Python[/b] (PSF License)"+"\n\nGo to [u][ref=link]https://unsigned.io/donate[/ref][/u] to support the project.\n\nThe Sideband app is Copyright (c) 2022 Mark Qvist / unsigned.io\n\nPermission is granted to freely share and distribute binary copies of Sideband v"+__version__+" "+__variant__+", so long as no payment or compensation is charged for said distribution or sharing.\n\nIf you were charged or paid anything for this copy of Sideband, please report it to [b]license@unsigned.io[/b].\n\nTHIS IS EXPERIMENTAL SOFTWARE - USE AT YOUR OWN RISK AND RESPONSIBILITY"
|
||||||
self.root.ids.information_info.text = info
|
self.root.ids.information_info.text = info
|
||||||
self.root.ids.information_info.bind(on_ref_press=link_exec)
|
self.root.ids.information_info.bind(on_ref_press=link_exec)
|
||||||
self.root.ids.screen_manager.transition.direction = "left"
|
self.root.ids.screen_manager.transition.direction = "left"
|
||||||
@ -1003,6 +1113,17 @@ class SidebandApp(MDApp):
|
|||||||
self.sideband.config["lxmf_sync_limit"] = self.root.ids.settings_lxmf_sync_limit.active
|
self.sideband.config["lxmf_sync_limit"] = self.root.ids.settings_lxmf_sync_limit.active
|
||||||
self.sideband.save_configuration()
|
self.sideband.save_configuration()
|
||||||
|
|
||||||
|
def save_print_command(sender=None, event=None):
|
||||||
|
in_cmd = self.root.ids.settings_print_command.text
|
||||||
|
if in_cmd == "":
|
||||||
|
new_cmd = "lp"
|
||||||
|
else:
|
||||||
|
new_cmd = in_cmd
|
||||||
|
|
||||||
|
self.sideband.config["print_command"] = new_cmd.strip()
|
||||||
|
self.root.ids.settings_print_command.text = self.sideband.config["print_command"]
|
||||||
|
self.sideband.save_configuration()
|
||||||
|
|
||||||
def save_lxmf_periodic_sync(sender=None, event=None, save=True):
|
def save_lxmf_periodic_sync(sender=None, event=None, save=True):
|
||||||
if self.root.ids.settings_lxmf_periodic_sync.active:
|
if self.root.ids.settings_lxmf_periodic_sync.active:
|
||||||
self.widget_hide(self.root.ids.lxmf_syncslider_container, False)
|
self.widget_hide(self.root.ids.lxmf_syncslider_container, False)
|
||||||
@ -1029,6 +1150,13 @@ class SidebandApp(MDApp):
|
|||||||
self.root.ids.settings_display_name.bind(on_text_validate=save_disp_name)
|
self.root.ids.settings_display_name.bind(on_text_validate=save_disp_name)
|
||||||
self.root.ids.settings_display_name.bind(focus=save_disp_name)
|
self.root.ids.settings_display_name.bind(focus=save_disp_name)
|
||||||
|
|
||||||
|
if RNS.vendor.platformutils.is_android():
|
||||||
|
self.widget_hide(self.root.ids.settings_print_command, True)
|
||||||
|
else:
|
||||||
|
self.root.ids.settings_print_command.text = self.sideband.config["print_command"]
|
||||||
|
self.root.ids.settings_print_command.bind(on_text_validate=save_print_command)
|
||||||
|
self.root.ids.settings_print_command.bind(focus=save_print_command)
|
||||||
|
|
||||||
if self.sideband.config["lxmf_propagation_node"] == None:
|
if self.sideband.config["lxmf_propagation_node"] == None:
|
||||||
prop_node_addr = ""
|
prop_node_addr = ""
|
||||||
else:
|
else:
|
||||||
|
@ -120,6 +120,7 @@ class SidebandCore():
|
|||||||
self.identity_path = self.app_dir+"/app_storage/primary_identity"
|
self.identity_path = self.app_dir+"/app_storage/primary_identity"
|
||||||
self.db_path = self.app_dir+"/app_storage/sideband.db"
|
self.db_path = self.app_dir+"/app_storage/sideband.db"
|
||||||
self.lxmf_storage = self.app_dir+"/app_storage/"
|
self.lxmf_storage = self.app_dir+"/app_storage/"
|
||||||
|
self.tmp_dir = self.app_dir+"/app_storage/tmp"
|
||||||
|
|
||||||
self.first_run = True
|
self.first_run = True
|
||||||
|
|
||||||
@ -129,6 +130,11 @@ class SidebandCore():
|
|||||||
else:
|
else:
|
||||||
self.__load_config()
|
self.__load_config()
|
||||||
self.first_run = False
|
self.first_run = False
|
||||||
|
|
||||||
|
if not os.path.isdir(self.tmp_dir):
|
||||||
|
os.makedirs(self.tmp_dir)
|
||||||
|
else:
|
||||||
|
self.clear_tmp_dir()
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
RNS.log("Error while configuring Sideband: "+str(e), RNS.LOG_ERROR)
|
RNS.log("Error while configuring Sideband: "+str(e), RNS.LOG_ERROR)
|
||||||
@ -165,6 +171,12 @@ class SidebandCore():
|
|||||||
RNS.Transport.register_announce_handler(self)
|
RNS.Transport.register_announce_handler(self)
|
||||||
RNS.Transport.register_announce_handler(self.propagation_detector)
|
RNS.Transport.register_announce_handler(self.propagation_detector)
|
||||||
|
|
||||||
|
def clear_tmp_dir(self):
|
||||||
|
if os.path.isdir(self.tmp_dir):
|
||||||
|
for file in os.listdir(self.tmp_dir):
|
||||||
|
fpath = self.tmp_dir+"/"+file
|
||||||
|
os.unlink(fpath)
|
||||||
|
|
||||||
def __init_config(self):
|
def __init_config(self):
|
||||||
RNS.log("Creating new Sideband configuration...")
|
RNS.log("Creating new Sideband configuration...")
|
||||||
if os.path.isfile(self.identity_path):
|
if os.path.isfile(self.identity_path):
|
||||||
@ -189,6 +201,8 @@ class SidebandCore():
|
|||||||
self.config["lxmf_sync_interval"] = 43200
|
self.config["lxmf_sync_interval"] = 43200
|
||||||
self.config["last_lxmf_propagation_node"] = None
|
self.config["last_lxmf_propagation_node"] = None
|
||||||
self.config["nn_home_node"] = None
|
self.config["nn_home_node"] = None
|
||||||
|
self.config["print_command"] = "lp"
|
||||||
|
|
||||||
# Connectivity
|
# Connectivity
|
||||||
self.config["connect_transport"] = False
|
self.config["connect_transport"] = False
|
||||||
self.config["connect_local"] = True
|
self.config["connect_local"] = True
|
||||||
@ -277,6 +291,8 @@ class SidebandCore():
|
|||||||
self.config["lxmf_sync_interval"] = 43200
|
self.config["lxmf_sync_interval"] = 43200
|
||||||
if not "notifications_on" in self.config:
|
if not "notifications_on" in self.config:
|
||||||
self.config["notifications_on"] = True
|
self.config["notifications_on"] = True
|
||||||
|
if not "print_command" in self.config:
|
||||||
|
self.config["print_command"] = "lp"
|
||||||
|
|
||||||
if not "connect_transport" in self.config:
|
if not "connect_transport" in self.config:
|
||||||
self.config["connect_transport"] = False
|
self.config["connect_transport"] = False
|
||||||
@ -945,7 +961,20 @@ class SidebandCore():
|
|||||||
return None
|
return None
|
||||||
else:
|
else:
|
||||||
entry = result[0]
|
entry = result[0]
|
||||||
lxm = LXMF.LXMessage.unpack_from_bytes(entry[10])
|
|
||||||
|
lxm_method = entry[7]
|
||||||
|
if lxm_method == LXMF.LXMessage.PAPER:
|
||||||
|
lxm_data = msgpack.unpackb(entry[10])
|
||||||
|
packed_lxm = lxm_data[0]
|
||||||
|
paper_packed_lxm = lxm_data[1]
|
||||||
|
else:
|
||||||
|
packed_lxm = entry[10]
|
||||||
|
|
||||||
|
lxm = LXMF.LXMessage.unpack_from_bytes(packed_lxm, original_method = lxm_method)
|
||||||
|
|
||||||
|
if lxm.desired_method == LXMF.LXMessage.PAPER:
|
||||||
|
lxm.paper_packed = paper_packed_lxm
|
||||||
|
|
||||||
message = {
|
message = {
|
||||||
"hash": lxm.hash,
|
"hash": lxm.hash,
|
||||||
"dest": lxm.destination_hash,
|
"dest": lxm.destination_hash,
|
||||||
@ -980,7 +1009,19 @@ class SidebandCore():
|
|||||||
else:
|
else:
|
||||||
messages = []
|
messages = []
|
||||||
for entry in result:
|
for entry in result:
|
||||||
lxm = LXMF.LXMessage.unpack_from_bytes(entry[10])
|
lxm_method = entry[7]
|
||||||
|
if lxm_method == LXMF.LXMessage.PAPER:
|
||||||
|
lxm_data = msgpack.unpackb(entry[10])
|
||||||
|
packed_lxm = lxm_data[0]
|
||||||
|
paper_packed_lxm = lxm_data[1]
|
||||||
|
else:
|
||||||
|
packed_lxm = entry[10]
|
||||||
|
|
||||||
|
lxm = LXMF.LXMessage.unpack_from_bytes(packed_lxm, original_method = lxm_method)
|
||||||
|
|
||||||
|
if lxm.desired_method == LXMF.LXMessage.PAPER:
|
||||||
|
lxm.paper_packed = paper_packed_lxm
|
||||||
|
|
||||||
message = {
|
message = {
|
||||||
"hash": lxm.hash,
|
"hash": lxm.hash,
|
||||||
"dest": lxm.destination_hash,
|
"dest": lxm.destination_hash,
|
||||||
@ -1006,6 +1047,11 @@ class SidebandCore():
|
|||||||
if not lxm.packed:
|
if not lxm.packed:
|
||||||
lxm.pack()
|
lxm.pack()
|
||||||
|
|
||||||
|
if lxm.method == LXMF.LXMessage.PAPER:
|
||||||
|
packed_lxm = msgpack.packb([lxm.packed, lxm.paper_packed])
|
||||||
|
else:
|
||||||
|
packed_lxm = lxm.packed
|
||||||
|
|
||||||
query = "INSERT INTO lxm (lxm_hash, dest, source, title, tx_ts, rx_ts, state, method, t_encrypted, t_encryption, data) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
|
query = "INSERT INTO lxm (lxm_hash, dest, source, title, tx_ts, rx_ts, state, method, t_encrypted, t_encryption, data) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
|
||||||
data = (
|
data = (
|
||||||
lxm.hash,
|
lxm.hash,
|
||||||
@ -1018,7 +1064,7 @@ class SidebandCore():
|
|||||||
lxm.method,
|
lxm.method,
|
||||||
lxm.transport_encrypted,
|
lxm.transport_encrypted,
|
||||||
lxm.transport_encryption,
|
lxm.transport_encryption,
|
||||||
lxm.packed
|
packed_lxm,
|
||||||
)
|
)
|
||||||
|
|
||||||
dbc.execute(query, data)
|
dbc.execute(query, data)
|
||||||
@ -1611,6 +1657,26 @@ class SidebandCore():
|
|||||||
else:
|
else:
|
||||||
self.lxm_ingest(message, originator=True)
|
self.lxm_ingest(message, originator=True)
|
||||||
|
|
||||||
|
def paper_message(self, content, destination_hash):
|
||||||
|
try:
|
||||||
|
if content == "":
|
||||||
|
raise ValueError("Message content cannot be empty")
|
||||||
|
|
||||||
|
dest_identity = RNS.Identity.recall(destination_hash)
|
||||||
|
dest = RNS.Destination(dest_identity, RNS.Destination.OUT, RNS.Destination.SINGLE, "lxmf", "delivery")
|
||||||
|
source = self.lxmf_destination
|
||||||
|
|
||||||
|
desired_method = LXMF.LXMessage.PAPER
|
||||||
|
lxm = LXMF.LXMessage(dest, source, content, title="", desired_method=desired_method)
|
||||||
|
|
||||||
|
self.lxm_ingest(lxm, originator=True)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
RNS.log("Error while creating paper message: "+str(e), RNS.LOG_ERROR)
|
||||||
|
return False
|
||||||
|
|
||||||
def send_message(self, content, destination_hash, propagation):
|
def send_message(self, content, destination_hash, propagation):
|
||||||
try:
|
try:
|
||||||
if content == "":
|
if content == "":
|
||||||
@ -1665,6 +1731,30 @@ class SidebandCore():
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def lxm_ingest_uri(self, uri):
|
||||||
|
local_delivery_signal = "local_delivery_occurred"
|
||||||
|
duplicate_signal = "duplicate_lxm"
|
||||||
|
ingest_result = self.message_router.ingest_lxm_uri(
|
||||||
|
uri,
|
||||||
|
signal_local_delivery=local_delivery_signal,
|
||||||
|
signal_duplicate=duplicate_signal
|
||||||
|
)
|
||||||
|
|
||||||
|
if ingest_result == False:
|
||||||
|
response = "The URI contained no decodable messages"
|
||||||
|
|
||||||
|
elif ingest_result == local_delivery_signal:
|
||||||
|
response = "Message was decoded, decrypted successfully, and added to your conversation list."
|
||||||
|
|
||||||
|
elif ingest_result == duplicate_signal:
|
||||||
|
response = "The decoded message has already been processed by the LXMF Router, and will not be ingested again."
|
||||||
|
|
||||||
|
else:
|
||||||
|
# TODO: Add message to sneakernet queues
|
||||||
|
response = "The decoded message was not addressed to your LXMF address, and has been discarded."
|
||||||
|
|
||||||
|
self.setstate("lxm_uri_ingest.result", response)
|
||||||
|
|
||||||
def lxm_ingest(self, message, originator = False):
|
def lxm_ingest(self, message, originator = False):
|
||||||
should_notify = False
|
should_notify = False
|
||||||
is_trusted = False
|
is_trusted = False
|
||||||
|
@ -6,6 +6,7 @@ from kivymd.uix.list import OneLineIconListItem, MDList, IconLeftWidget, IconRig
|
|||||||
from kivy.properties import StringProperty
|
from kivy.properties import StringProperty
|
||||||
|
|
||||||
ts_format = "%Y-%m-%d %H:%M:%S"
|
ts_format = "%Y-%m-%d %H:%M:%S"
|
||||||
|
file_ts_format = "%Y_%m_%d_%H_%M_%S"
|
||||||
|
|
||||||
def mdc(color, hue=None):
|
def mdc(color, hue=None):
|
||||||
if hue == None:
|
if hue == None:
|
||||||
@ -14,6 +15,7 @@ def mdc(color, hue=None):
|
|||||||
|
|
||||||
color_received = "LightGreen"
|
color_received = "LightGreen"
|
||||||
color_delivered = "Blue"
|
color_delivered = "Blue"
|
||||||
|
color_paper = "Indigo"
|
||||||
color_propagated = "Indigo"
|
color_propagated = "Indigo"
|
||||||
color_failed = "Red"
|
color_failed = "Red"
|
||||||
color_unknown = "Gray"
|
color_unknown = "Gray"
|
||||||
|
@ -88,6 +88,7 @@ MDNavigationLayout:
|
|||||||
]
|
]
|
||||||
right_action_items:
|
right_action_items:
|
||||||
[
|
[
|
||||||
|
['qrcode', lambda x: root.ids.screen_manager.app.ingest_lxm_action(self)],
|
||||||
['webhook', lambda x: root.ids.screen_manager.app.connectivity_status(self)],
|
['webhook', lambda x: root.ids.screen_manager.app.connectivity_status(self)],
|
||||||
['access-point', lambda x: root.ids.screen_manager.app.announce_now_action(self)],
|
['access-point', lambda x: root.ids.screen_manager.app.announce_now_action(self)],
|
||||||
['email-sync', lambda x: root.ids.screen_manager.app.lxmf_sync_action(self)],
|
['email-sync', lambda x: root.ids.screen_manager.app.lxmf_sync_action(self)],
|
||||||
@ -96,7 +97,7 @@ MDNavigationLayout:
|
|||||||
|
|
||||||
ScrollView:
|
ScrollView:
|
||||||
id: conversations_scrollview
|
id: conversations_scrollview
|
||||||
|
|
||||||
|
|
||||||
MDScreen:
|
MDScreen:
|
||||||
name: "messages_screen"
|
name: "messages_screen"
|
||||||
@ -1007,6 +1008,14 @@ MDNavigationLayout:
|
|||||||
max_text_length: 32
|
max_text_length: 32
|
||||||
font_size: dp(24)
|
font_size: dp(24)
|
||||||
|
|
||||||
|
MDTextField:
|
||||||
|
id: settings_print_command
|
||||||
|
hint_text: "Print Command"
|
||||||
|
disabled: False
|
||||||
|
text: ""
|
||||||
|
max_text_length: 32
|
||||||
|
font_size: dp(24)
|
||||||
|
|
||||||
MDLabel:
|
MDLabel:
|
||||||
text: ""
|
text: ""
|
||||||
font_style: "H6"
|
font_style: "H6"
|
||||||
|
@ -16,13 +16,17 @@ from kivy.clock import Clock
|
|||||||
from kivymd.uix.button import MDRectangleFlatButton
|
from kivymd.uix.button import MDRectangleFlatButton
|
||||||
from kivymd.uix.dialog import MDDialog
|
from kivymd.uix.dialog import MDDialog
|
||||||
|
|
||||||
|
import os
|
||||||
|
import plyer
|
||||||
|
import subprocess
|
||||||
|
import shlex
|
||||||
|
|
||||||
if RNS.vendor.platformutils.get_platform() == "android":
|
if RNS.vendor.platformutils.get_platform() == "android":
|
||||||
from ui.helpers import ts_format, mdc
|
from ui.helpers import ts_format, file_ts_format, mdc
|
||||||
from ui.helpers import color_received, color_delivered, color_propagated, color_failed, color_unknown, intensity_msgs_dark, intensity_msgs_light
|
from ui.helpers import color_received, color_delivered, color_propagated, color_paper, color_failed, color_unknown, intensity_msgs_dark, intensity_msgs_light
|
||||||
else:
|
else:
|
||||||
from .helpers import ts_format, mdc
|
from .helpers import ts_format, file_ts_format, mdc
|
||||||
from .helpers import color_received, color_delivered, color_propagated, color_failed, color_unknown, intensity_msgs_dark, intensity_msgs_light
|
from .helpers import color_received, color_delivered, color_propagated, color_paper, color_failed, color_unknown, intensity_msgs_dark, intensity_msgs_light
|
||||||
|
|
||||||
class ListLXMessageCard(MDCard):
|
class ListLXMessageCard(MDCard):
|
||||||
# class ListLXMessageCard(MDCard, FakeRectangularElevationBehavior):
|
# class ListLXMessageCard(MDCard, FakeRectangularElevationBehavior):
|
||||||
@ -85,6 +89,15 @@ class Messages():
|
|||||||
w.heading = titlestr+"[b]Sent[/b] "+txstr+"\n[b]State[/b] Delivered"
|
w.heading = titlestr+"[b]Sent[/b] "+txstr+"\n[b]State[/b] Delivered"
|
||||||
m["state"] = msg["state"]
|
m["state"] = msg["state"]
|
||||||
|
|
||||||
|
if msg["method"] == LXMF.LXMessage.PAPER:
|
||||||
|
w.md_bg_color = msg_color = mdc(color_paper, intensity_msgs)
|
||||||
|
txstr = time.strftime(ts_format, time.localtime(msg["sent"]))
|
||||||
|
titlestr = ""
|
||||||
|
if msg["title"]:
|
||||||
|
titlestr = "[b]Title[/b] "+msg["title"].decode("utf-8")+"\n"
|
||||||
|
w.heading = titlestr+"[b]Sent[/b] "+txstr+"\n[b]State[/b] Paper Message"
|
||||||
|
m["state"] = msg["state"]
|
||||||
|
|
||||||
if msg["method"] == LXMF.LXMessage.PROPAGATED and msg["state"] == LXMF.LXMessage.SENT:
|
if msg["method"] == LXMF.LXMessage.PROPAGATED and msg["state"] == LXMF.LXMessage.SENT:
|
||||||
w.md_bg_color = msg_color = mdc(color_propagated, intensity_msgs)
|
w.md_bg_color = msg_color = mdc(color_propagated, intensity_msgs)
|
||||||
txstr = time.strftime(ts_format, time.localtime(msg["sent"]))
|
txstr = time.strftime(ts_format, time.localtime(msg["sent"]))
|
||||||
@ -130,6 +143,10 @@ class Messages():
|
|||||||
msg_color = mdc(color_propagated, intensity_msgs)
|
msg_color = mdc(color_propagated, intensity_msgs)
|
||||||
heading_str = titlestr+"[b]Sent[/b] "+txstr+"\n[b]State[/b] On Propagation Net"
|
heading_str = titlestr+"[b]Sent[/b] "+txstr+"\n[b]State[/b] On Propagation Net"
|
||||||
|
|
||||||
|
elif m["method"] == LXMF.LXMessage.PAPER:
|
||||||
|
msg_color = mdc(color_paper, intensity_msgs)
|
||||||
|
heading_str = titlestr+"[b]Created[/b] "+txstr+"\n[b]State[/b] Paper Message"
|
||||||
|
|
||||||
elif m["state"] == LXMF.LXMessage.FAILED:
|
elif m["state"] == LXMF.LXMessage.FAILED:
|
||||||
msg_color = mdc(color_failed, intensity_msgs)
|
msg_color = mdc(color_failed, intensity_msgs)
|
||||||
heading_str = titlestr+"[b]Sent[/b] "+txstr+"\n[b]State[/b] Failed"
|
heading_str = titlestr+"[b]Sent[/b] "+txstr+"\n[b]State[/b] Failed"
|
||||||
@ -189,25 +206,153 @@ class Messages():
|
|||||||
def gen_copy(msg, item):
|
def gen_copy(msg, item):
|
||||||
def x():
|
def x():
|
||||||
Clipboard.copy(msg)
|
Clipboard.copy(msg)
|
||||||
RNS.log(str(item))
|
|
||||||
item.dmenu.dismiss()
|
item.dmenu.dismiss()
|
||||||
|
|
||||||
return x
|
return x
|
||||||
|
|
||||||
dm_items = [
|
def gen_copy_lxm_uri(lxm, item):
|
||||||
{
|
def x():
|
||||||
"viewclass": "OneLineListItem",
|
Clipboard.copy(lxm.as_uri())
|
||||||
"text": "Copy",
|
item.dmenu.dismiss()
|
||||||
"height": dp(40),
|
|
||||||
"on_release": gen_copy(m["content"].decode("utf-8"), item)
|
return x
|
||||||
},
|
|
||||||
{
|
def gen_save_qr(lxm, item):
|
||||||
"text": "Delete",
|
if RNS.vendor.platformutils.is_android():
|
||||||
"viewclass": "OneLineListItem",
|
def x():
|
||||||
"height": dp(40),
|
item.dmenu.dismiss()
|
||||||
"on_release": gen_del(m["hash"], item)
|
return x
|
||||||
}
|
|
||||||
]
|
else:
|
||||||
|
def x():
|
||||||
|
try:
|
||||||
|
qr_image = lxm.as_qr()
|
||||||
|
hash_str = RNS.hexrep(lxm.hash[-2:], delimit=False)
|
||||||
|
filename = "Paper_Message_"+time.strftime(file_ts_format, time.localtime(m["sent"]))+"_"+hash_str+".png"
|
||||||
|
save_path = plyer.storagepath.get_downloads_dir()+"/"+filename
|
||||||
|
qr_image.save(save_path)
|
||||||
|
item.dmenu.dismiss()
|
||||||
|
|
||||||
|
ok_button = MDRectangleFlatButton(text="OK",font_size=dp(18))
|
||||||
|
dialog = MDDialog(
|
||||||
|
title="QR Code Saved",
|
||||||
|
text="The paper message has been saved to: "+save_path+"",
|
||||||
|
buttons=[ ok_button ],
|
||||||
|
# elevation=0,
|
||||||
|
)
|
||||||
|
def dl_ok(s):
|
||||||
|
dialog.dismiss()
|
||||||
|
|
||||||
|
ok_button.bind(on_release=dl_ok)
|
||||||
|
dialog.open()
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
item.dmenu.dismiss()
|
||||||
|
ok_button = MDRectangleFlatButton(text="OK",font_size=dp(18))
|
||||||
|
dialog = MDDialog(
|
||||||
|
title="Error",
|
||||||
|
text="Could not save the paper message QR-code to:\n\n"+save_path+"\n\n"+str(e),
|
||||||
|
buttons=[ ok_button ],
|
||||||
|
# elevation=0,
|
||||||
|
)
|
||||||
|
def dl_ok(s):
|
||||||
|
dialog.dismiss()
|
||||||
|
|
||||||
|
ok_button.bind(on_release=dl_ok)
|
||||||
|
dialog.open()
|
||||||
|
|
||||||
|
return x
|
||||||
|
|
||||||
|
def gen_print_qr(lxm, item):
|
||||||
|
if RNS.vendor.platformutils.is_android():
|
||||||
|
def x():
|
||||||
|
item.dmenu.dismiss()
|
||||||
|
return x
|
||||||
|
|
||||||
|
else:
|
||||||
|
def x():
|
||||||
|
try:
|
||||||
|
qr_image = lxm.as_qr()
|
||||||
|
qr_tmp_path = self.app.sideband.tmp_dir+"/"+str(RNS.hexrep(lxm.hash, delimit=False))
|
||||||
|
qr_image.save(qr_tmp_path)
|
||||||
|
|
||||||
|
print_command = self.app.sideband.config["print_command"]+" "+qr_tmp_path
|
||||||
|
return_code = subprocess.call(shlex.split(print_command), stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
||||||
|
|
||||||
|
os.unlink(qr_tmp_path)
|
||||||
|
|
||||||
|
item.dmenu.dismiss()
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
item.dmenu.dismiss()
|
||||||
|
ok_button = MDRectangleFlatButton(text="OK",font_size=dp(18))
|
||||||
|
dialog = MDDialog(
|
||||||
|
title="Error",
|
||||||
|
text="Could not print the paper message QR-code.\n\n"+str(e),
|
||||||
|
buttons=[ ok_button ],
|
||||||
|
# elevation=0,
|
||||||
|
)
|
||||||
|
def dl_ok(s):
|
||||||
|
dialog.dismiss()
|
||||||
|
|
||||||
|
ok_button.bind(on_release=dl_ok)
|
||||||
|
dialog.open()
|
||||||
|
|
||||||
|
return x
|
||||||
|
|
||||||
|
if m["method"] == LXMF.LXMessage.PAPER:
|
||||||
|
if RNS.vendor.platformutils.is_android():
|
||||||
|
qr_save_text = "Share QR Code"
|
||||||
|
else:
|
||||||
|
qr_save_text = "Save QR Code"
|
||||||
|
|
||||||
|
dm_items = [
|
||||||
|
{
|
||||||
|
"viewclass": "OneLineListItem",
|
||||||
|
"text": "Print QR Code",
|
||||||
|
"height": dp(40),
|
||||||
|
"on_release": gen_print_qr(m["lxm"], item)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"viewclass": "OneLineListItem",
|
||||||
|
"text": qr_save_text,
|
||||||
|
"height": dp(40),
|
||||||
|
"on_release": gen_save_qr(m["lxm"], item)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"viewclass": "OneLineListItem",
|
||||||
|
"text": "Copy LXM URI",
|
||||||
|
"height": dp(40),
|
||||||
|
"on_release": gen_copy_lxm_uri(m["lxm"], item)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"viewclass": "OneLineListItem",
|
||||||
|
"text": "Copy message text",
|
||||||
|
"height": dp(40),
|
||||||
|
"on_release": gen_copy(m["content"].decode("utf-8"), item)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"text": "Delete",
|
||||||
|
"viewclass": "OneLineListItem",
|
||||||
|
"height": dp(40),
|
||||||
|
"on_release": gen_del(m["hash"], item)
|
||||||
|
}
|
||||||
|
]
|
||||||
|
else:
|
||||||
|
dm_items = [
|
||||||
|
{
|
||||||
|
"viewclass": "OneLineListItem",
|
||||||
|
"text": "Copy",
|
||||||
|
"height": dp(40),
|
||||||
|
"on_release": gen_copy(m["content"].decode("utf-8"), item)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"text": "Delete",
|
||||||
|
"viewclass": "OneLineListItem",
|
||||||
|
"height": dp(40),
|
||||||
|
"on_release": gen_del(m["hash"], item)
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
item.dmenu = MDDropdownMenu(
|
item.dmenu = MDDropdownMenu(
|
||||||
caller=item.ids.msg_submenu,
|
caller=item.ids.msg_submenu,
|
||||||
|
Loading…
Reference in New Issue
Block a user