diff --git a/sbapp/main.py b/sbapp/main.py index 59524b8..38ee203 100644 --- a/sbapp/main.py +++ b/sbapp/main.py @@ -72,8 +72,6 @@ class SidebandApp(MDApp): self.android_service = None self.app_dir = plyer.storagepath.get_application_dir() - # TODO: Remove - RNS.log("Application directory is: "+str(self.app_dir)) if RNS.vendor.platformutils.get_platform() == "android": self.sideband = SidebandCore(self, is_client=True, android_app_dir=self.app_dir) @@ -82,20 +80,16 @@ class SidebandApp(MDApp): self.conversations_view = None - self.flag_new_conversations = False - self.flag_unread_conversations = False - self.flag_new_announces = False - self.lxmf_sync_dialog_open = False self.sync_dialog = None Window.softinput_mode = "below_target" self.icon = self.sideband.asset_dir+"/icon.png" self.notification_icon = self.sideband.asset_dir+"/notification_icon.png" + self.check_permissions() def start_core(self, dt): self.start_service() - self.open_conversations() Clock.schedule_interval(self.jobs, 1) def dismiss_splash(dt): @@ -105,8 +99,15 @@ class SidebandApp(MDApp): if RNS.vendor.platformutils.get_platform() == "android": Clock.schedule_once(dismiss_splash, 0) - if self.sideband.first_run: - self.guide_action() + self.sideband.setstate("app.loaded", True) + self.sideband.setstate("app.running", True) + self.sideband.setstate("app.foreground", True) + + if self.sideband.first_run: + self.guide_action() + self.request_notifications_permission() + else: + self.open_conversations() self.app_state = SidebandApp.ACTIVE @@ -125,8 +126,6 @@ class SidebandApp(MDApp): time.sleep(7) # Start local core instance - # TODO: Remove log - RNS.log("Starting local core") self.sideband.start() else: @@ -150,14 +149,20 @@ class SidebandApp(MDApp): mActivity.startActivity(shareIntent) def on_pause(self): + self.sideband.setstate("app.running", True) + self.sideband.setstate("app.foreground", False) self.app_state = SidebandApp.PAUSED self.sideband.should_persist_data() return True def on_resume(self): + self.sideband.setstate("app.running", True) + self.sideband.setstate("app.foreground", True) self.app_state = SidebandApp.ACTIVE def on_stop(self): + self.sideband.setstate("app.running", False) + self.sideband.setstate("app.foreground", False) self.app_state = SidebandApp.STOPPING def is_in_foreground(self): @@ -166,25 +171,27 @@ class SidebandApp(MDApp): else: return False - def notify(self, title, content): - notifications_enabled = True - - if notifications_enabled: - if RNS.vendor.platformutils.get_platform() == "android": - notifications_permitted = False - if check_permission("android.permission.POST_NOTIFICATIONS"): - notifications_permitted = True - else: - RNS.log("Requesting notification permission") - request_permissions(["android.permission.POST_NOTIFICATIONS"]) + def check_permissions(self): + if RNS.vendor.platformutils.get_platform() == "android": + if check_permission("android.permission.POST_NOTIFICATIONS"): + RNS.log("Have notification permissions") + self.sideband.setpersistent("permissions.notifications", True) else: - notifications_permitted = True + RNS.log("Do not have notification permissions") + self.sideband.setpersistent("permissions.notifications", False) + else: + self.sideband.setpersistent("permissions.notifications", True) - if notifications_permitted: - if RNS.vendor.platformutils.get_platform() == "android": - plyer.notification.notify(title, content, notification_icon=self.notification_icon) - else: - plyer.notification.notify(title, content) + def request_permissions(self): + self.request_notifications_permission() + + def request_notifications_permission(self): + if RNS.vendor.platformutils.get_platform() == "android": + if not check_permission("android.permission.POST_NOTIFICATIONS"): + RNS.log("Requesting notification permission") + request_permissions(["android.permission.POST_NOTIFICATIONS"]) + + self.check_permissions() def build(self): FONT_PATH = self.sideband.asset_dir+"/fonts" @@ -201,22 +208,22 @@ class SidebandApp(MDApp): self.message_area_detect() elif self.root.ids.screen_manager.current == "conversations_screen": - if self.flag_new_conversations: + if self.sideband.getstate("app.flags.new_conversations"): RNS.log("Updating because of new conversations flag") if self.conversations_view != None: self.conversations_view.update() - if self.flag_unread_conversations: + if self.sideband.getstate("app.flags.unread_conversations"): RNS.log("Updating because of unread messages flag") if self.conversations_view != None: self.conversations_view.update() - if self.lxmf_sync_dialog_open and self.sync_dialog != None: + if self.sideband.getstate("app.flags.lxmf_sync_dialog_open") and self.sync_dialog != None: self.sync_dialog.ids.sync_progress.value = self.sideband.get_sync_progress()*100 self.sync_dialog.ids.sync_status.text = self.sideband.get_sync_status() elif self.root.ids.screen_manager.current == "announces_screen": - if self.flag_new_announces: + if self.sideband.getstate("app.flags.new_announces"): RNS.log("Updating because of new announces flag") if self.announces_view != None: self.announces_view.update() @@ -304,6 +311,9 @@ class SidebandApp(MDApp): self.root.ids.nav_drawer.set_state("closed") self.sideband.should_persist_data() + self.sideband.setstate("app.running", False) + self.sideband.setstate("app.foreground", False) + def final_exit(dt): RNS.exit() MDApp.get_running_app().stop() @@ -328,13 +338,6 @@ class SidebandApp(MDApp): yes_button.bind(on_release=dl_yes) dialog.open() - def conversation_update(self, context_dest): - pass - # if self.root.ids.messages_scrollview.active_conversation == context_dest: - # self.messages_view.update_widget() - # else: - # RNS.log("Not updating since context_dest does not match active") - ################################################# # Screens # @@ -372,13 +375,15 @@ class SidebandApp(MDApp): self.root.ids.messages_scrollview.scroll_y = 0 self.root.ids.messages_toolbar.title = self.sideband.peer_display_name(context_dest) self.root.ids.messages_scrollview.active_conversation = context_dest + self.sideband.setstate("app.active_conversation", context_dest) self.root.ids.nokeys_text.text = "" self.message_area_detect() self.update_message_widgets() self.root.ids.screen_manager.current = "messages_screen" - + self.sideband.setstate("app.displaying", self.root.ids.screen_manager.current) + self.sideband.read_conversation(context_dest) def close_messages_action(self, sender=None): @@ -480,6 +485,7 @@ class SidebandApp(MDApp): self.root.ids.screen_manager.current = "conversations_screen" self.root.ids.messages_scrollview.active_conversation = None + self.sideband.setstate("app.displaying", self.root.ids.screen_manager.current) def connectivity_status(self, sender): hs = dp(22) @@ -567,18 +573,12 @@ class SidebandApp(MDApp): ) dialog.d_content = dialog_content def dl_close(s): - self.lxmf_sync_dialog_open = False + self.sideband.setstate("app.flags.lxmf_sync_dialog_open", False) dialog.dismiss() self.sideband.cancel_lxmf_sync() - # def dl_stop(s): - # self.lxmf_sync_dialog_open = False - # dialog.dismiss() - # self.sideband.cancel_lxmf_sync() - close_button.bind(on_release=dl_close) - # stop_button.bind(on_release=dl_stop) - self.lxmf_sync_dialog_open = True + self.sideband.setstate("app.flags.lxmf_sync_dialog_open", True) self.sync_dialog = dialog_content dialog.open() dialog_content.ids.sync_progress.value = self.sideband.get_sync_progress()*100 @@ -652,6 +652,7 @@ class SidebandApp(MDApp): self.root.ids.screen_manager.transition.direction = "left" self.root.ids.screen_manager.current = "information_screen" self.root.ids.nav_drawer.set_state("closed") + self.sideband.setstate("app.displaying", self.root.ids.screen_manager.current) def close_information_action(self, sender=None): self.open_conversations(direction="right") @@ -743,6 +744,7 @@ class SidebandApp(MDApp): self.root.ids.screen_manager.current = "settings_screen" self.root.ids.nav_drawer.set_state("closed") + self.sideband.setstate("app.displaying", self.root.ids.screen_manager.current) def close_settings_action(self, sender=None): self.open_conversations(direction="right") @@ -852,6 +854,7 @@ class SidebandApp(MDApp): self.root.ids.screen_manager.transition.direction = "left" self.root.ids.screen_manager.current = "connectivity_screen" self.root.ids.nav_drawer.set_state("closed") + self.sideband.setstate("app.displaying", self.root.ids.screen_manager.current) def close_connectivity_action(self, sender=None): @@ -875,6 +878,7 @@ class SidebandApp(MDApp): self.root.ids.announces_scrollview.add_widget(self.announces_view.get_widget()) self.root.ids.screen_manager.current = "announces_screen" + self.sideband.setstate("app.displaying", self.root.ids.screen_manager.current) def close_announces_action(self, sender=None): self.open_conversations(direction="right") @@ -900,6 +904,7 @@ class SidebandApp(MDApp): self.root.ids.screen_manager.transition.direction = "left" self.root.ids.screen_manager.current = "keys_screen" self.root.ids.nav_drawer.set_state("closed") + self.sideband.setstate("app.displaying", self.root.ids.screen_manager.current) def close_keys_action(self, sender=None): self.open_conversations(direction="right") @@ -1031,6 +1036,7 @@ Thank you very much for using Free Communications Systems. self.root.ids.screen_manager.transition.direction = "left" self.root.ids.screen_manager.current = "guide_screen" self.root.ids.nav_drawer.set_state("closed") + self.sideband.setstate("app.displaying", self.root.ids.screen_manager.current) ################################################# @@ -1049,6 +1055,7 @@ Thank you very much for using Free Communications Systems. self.root.ids.screen_manager.transition.direction = "left" self.root.ids.screen_manager.current = "map_screen" self.root.ids.nav_drawer.set_state("closed") + self.sideband.setstate("app.displaying", self.root.ids.screen_manager.current) def broadcasts_action(self, sender=None): def link_exec(sender=None, event=None): @@ -1062,6 +1069,7 @@ Thank you very much for using Free Communications Systems. self.root.ids.screen_manager.transition.direction = "left" self.root.ids.screen_manager.current = "broadcasts_screen" self.root.ids.nav_drawer.set_state("closed") + self.sideband.setstate("app.displaying", self.root.ids.screen_manager.current) diff --git a/sbapp/patches/PythonService.java b/sbapp/patches/PythonService.java index 41b59b3..a912f9a 100644 --- a/sbapp/patches/PythonService.java +++ b/sbapp/patches/PythonService.java @@ -130,20 +130,22 @@ public class PythonService extends Service implements Runnable { } else { // for android 8+ we need to create our own channel // https://stackoverflow.com/questions/47531742/startforeground-fail-after-upgrade-to-android-8-1 - String NOTIFICATION_CHANNEL_ID = "org.kivy.p4a"; //TODO: make this configurable - String channelName = "Background Service"; //TODO: make this configurable + String NOTIFICATION_CHANNEL_ID = "io.unsigned.sideband.reticulum"; + String channelName = "Background Service"; NotificationChannel chan = new NotificationChannel(NOTIFICATION_CHANNEL_ID, channelName, NotificationManager.IMPORTANCE_NONE); chan.setLightColor(Color.BLUE); - chan.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE); + chan.setLockscreenVisibility(Notification.VISIBILITY_SECRET); + chan.setShowBadge(false); NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); manager.createNotificationChannel(chan); Notification.Builder builder = new Notification.Builder(context, NOTIFICATION_CHANNEL_ID); - builder.setContentTitle("Sideband"); - builder.setContentText("Reticulum Running"); + builder.setContentTitle("Sideband Active"); + // builder.setContentText("Reticulum Active"); builder.setContentIntent(pIntent); + // builder.setOngoing(true); // TODO: Generalise this Bitmap icon_bitmap = BitmapFactory.decodeFile("/data/user/0/io.unsigned.sideband/files/app/assets/notification_icon.png"); diff --git a/sbapp/plyer/facades/notification.py b/sbapp/plyer/facades/notification.py index 5b09dee..71c7d53 100644 --- a/sbapp/plyer/facades/notification.py +++ b/sbapp/plyer/facades/notification.py @@ -46,7 +46,7 @@ class Notification: ''' def notify(self, title='', message='', app_name='', app_icon='', notification_icon=None, - timeout=10, ticker='', toast=False, hints={}): + timeout=10, ticker='', toast=False, hints={}, context_override=None): ''' Send a notification. @@ -84,7 +84,7 @@ class Notification: self._notify( title=title, message=message, app_icon=app_icon, app_name=app_name, notification_icon=notification_icon, - timeout=timeout, ticker=ticker, toast=toast, hints=hints + timeout=timeout, ticker=ticker, toast=toast, hints=hints, context_override=context_override ) # private diff --git a/sbapp/plyer/platforms/android/notification.py b/sbapp/plyer/platforms/android/notification.py index 24a567d..ac9f76b 100644 --- a/sbapp/plyer/platforms/android/notification.py +++ b/sbapp/plyer/platforms/android/notification.py @@ -180,6 +180,7 @@ class AndroidNotification(Notification): ) icon = kwargs.get('app_icon') notification_icon = kwargs.get('notification_icon') + context_override = kwargs.get('context_override') # decide whether toast only or proper notification if kwargs.get('toast'): diff --git a/sbapp/services/sidebandservice.py b/sbapp/services/sidebandservice.py index 89e87d5..53aa0fe 100644 --- a/sbapp/services/sidebandservice.py +++ b/sbapp/services/sidebandservice.py @@ -9,7 +9,18 @@ Logger.setLevel(LOG_LEVELS["debug"]) if RNS.vendor.platformutils.get_platform() == "android": from jnius import autoclass, cast + from android import python_act Context = autoclass('android.content.Context') + Intent = autoclass('android.content.Intent') + BitmapFactory = autoclass('android.graphics.BitmapFactory') + Icon = autoclass("android.graphics.drawable.Icon") + PendingIntent = autoclass('android.app.PendingIntent') + AndroidString = autoclass('java.lang.String') + NotificationManager = autoclass('android.app.NotificationManager') + Context = autoclass('android.content.Context') + NotificationBuilder = autoclass('android.app.Notification$Builder') + NotificationChannel = autoclass('android.app.NotificationChannel') + from sideband.core import SidebandCore else: @@ -20,6 +31,64 @@ class AppProxy(): pass class SidebandService(): + def android_notification(self, title="", content="", ticker="", group=None, context_id=None): + package_name = "io.unsigned.sideband" + + if not self.notification_service: + self.notification_service = cast(NotificationManager, self.app_context.getSystemService( + Context.NOTIFICATION_SERVICE + )) + + channel_id = package_name + group_id = "" + if group != None: + channel_id += "."+str(group) + group_id += str(group) + if context_id != None: + channel_id += "."+str(context_id) + group_id += "."+str(context_id) + + if not title or title == "": + channel_name = "Sideband" + else: + channel_name = title + + self.notification_channel = NotificationChannel(channel_id, channel_name, NotificationManager.IMPORTANCE_DEFAULT) + self.notification_channel.enableVibration(True) + self.notification_channel.setShowBadge(True) + self.notification_service.createNotificationChannel(self.notification_channel) + + notification = NotificationBuilder(self.app_context, channel_id) + notification.setContentTitle(title) + notification.setContentText(AndroidString(content)) + + # if group != None: + # notification.setGroup(group_id) + + if not self.notification_small_icon: + path = self.sideband.notification_icon + bitmap = BitmapFactory.decodeFile(path) + self.notification_small_icon = Icon.createWithBitmap(bitmap) + + notification.setSmallIcon(self.notification_small_icon) + + # large_icon_path = self.sideband.icon + # bitmap_icon = BitmapFactory.decodeFile(large_icon_path) + # notification.setLargeIcon(bitmap_icon) + + if not self.notification_intent: + notification_intent = Intent(self.app_context, python_act) + notification_intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP) + notification_intent.setAction(Intent.ACTION_MAIN) + notification_intent.addCategory(Intent.CATEGORY_LAUNCHER) + + self.notification_intent = PendingIntent.getActivity(self.app_context, 0, notification_intent, 0) + + notification.setContentIntent(self.notification_intent) + notification.setAutoCancel(True) + + built_notification = notification.build() + self.notification_service.notify(0, built_notification) def __init__(self): self.argument = environ.get('PYTHON_SERVICE_ARGUMENT', '') @@ -34,15 +103,20 @@ class SidebandService(): self.app_context = None self.wifi_manager = None + self.notification_service = None + self.notification_channel = None + self.notification_intent = None + self.notification_small_icon = None + if RNS.vendor.platformutils.get_platform() == "android": self.android_service = autoclass('org.kivy.android.PythonService').mService self.app_context = self.android_service.getApplication().getApplicationContext() self.wifi_manager = self.app_context.getSystemService(Context.WIFI_SERVICE) - # The returned instance is an android.net.wifi.WifiManager + # The returned instance /\ is an android.net.wifi.WifiManager - # TODO: Remove? - RNS.log("Sideband background service created, starting core...", RNS.LOG_DEBUG) self.sideband = SidebandCore(self.app_proxy, is_service=True, android_app_dir=self.app_dir) + self.sideband.service_context = self.android_service + self.sideband.owner_service = self self.sideband.start() def start(self): @@ -76,8 +150,4 @@ class SidebandService(): self.release_locks() sbs = SidebandService() -sbs.start() - -# TODO: Remove -print("SBS: Service thread done") -RNS.log("Service thread done") \ No newline at end of file +sbs.start() \ No newline at end of file diff --git a/sbapp/sideband/core.py b/sbapp/sideband/core.py index eed9460..f36eae1 100644 --- a/sbapp/sideband/core.py +++ b/sbapp/sideband/core.py @@ -8,6 +8,9 @@ import sqlite3 import RNS.vendor.umsgpack as msgpack +if RNS.vendor.platformutils.get_platform() == "android": + from jnius import autoclass, cast + class PropagationNodeDetector(): EMITTED_DELTA_GRACE = 300 EMITTED_DELTA_IGNORE = 10 @@ -60,6 +63,8 @@ class SidebandCore(): MAX_ANNOUNCES = 48 + JOB_INTERVAL = 1 + aspect_filter = "lxmf.delivery" def received_announce(self, destination_hash, announced_identity, app_data): # Add the announce to the directory announce @@ -75,12 +80,6 @@ class SidebandCore(): else: self.is_standalone = False - if self.is_client: - from .serviceproxy import ServiceProxy - self.serviceproxy = ServiceProxy(self) - else: - self.serviceproxy = None - self.owner_app = owner_app self.reticulum = None @@ -90,16 +89,23 @@ class SidebandCore(): if RNS.vendor.platformutils.get_platform() == "android": self.app_dir = android_app_dir+"/io.unsigned.sideband/files/" self.rns_configdir = self.app_dir+"/app_storage/reticulum" + self.asset_dir = self.app_dir+"/app/assets" + else: + self.asset_dir = plyer.storagepath.get_application_dir()+"/sbapp/assets" + + self.icon = self.asset_dir+"/icon.png" + self.icon_48 = self.asset_dir+"/icon_48.png" + self.icon_32 = self.asset_dir+"/icon_32.png" + self.notification_icon = self.asset_dir+"/notification_icon.png" if not os.path.isdir(self.app_dir+"/app_storage"): os.makedirs(self.app_dir+"/app_storage") - self.asset_dir = self.app_dir+"/app/assets" self.config_path = self.app_dir+"/app_storage/sideband_config" self.identity_path = self.app_dir+"/app_storage/primary_identity" self.db_path = self.app_dir+"/app_storage/sideband.db" self.lxmf_storage = self.app_dir+"/app_storage/" - + self.first_run = True try: @@ -195,6 +201,9 @@ class SidebandCore(): if not os.path.isfile(self.db_path): self.__db_init() + else: + self._db_initstate() + self._db_initpersistent() def __save_config(self): @@ -222,11 +231,32 @@ class SidebandCore(): RNS.log("Error while setting LXMF propagation node: "+str(e), RNS.LOG_ERROR) + def notify(self, title, content, group=None, context_id=None): + notifications_enabled = True + + if notifications_enabled: + if RNS.vendor.platformutils.get_platform() == "android": + if self.getpersistent("permissions.notifications"): + notifications_permitted = True + else: + notifications_permitted = False + else: + notifications_permitted = True + + if notifications_permitted: + if RNS.vendor.platformutils.get_platform() == "android": + if self.is_service: + self.owner_service.android_notification(title, content, group=group, context_id=context_id) + else: + plyer.notification.notify(title, content, notification_icon=self.notification_icon, context_override=None) + else: + plyer.notification.notify(title, content, app_icon=self.icon_32) + def log_announce(self, dest, app_data, dest_type): try: RNS.log("Received "+str(dest_type)+" announce for "+RNS.prettyhexrep(dest)+" with data: "+app_data.decode("utf-8")) self._db_save_announce(dest, app_data, dest_type) - self.owner_app.flag_new_announces = True + self.setstate("app.flags.new_announces", True) except Exception as e: RNS.log("Exception while decoding LXMF destination announce data:"+str(e)) @@ -345,6 +375,27 @@ class SidebandCore(): else: return [] + def gui_foreground(self): + return self._db_getstate("app.foreground") + + def gui_display(self): + return self._db_getstate("app.displaying") + + def gui_conversation(self): + return self._db_getstate("app.active_conversation") + + def setstate(self, prop, val): + self._db_setstate(prop, val) + + def getstate(self, prop): + return self._db_getstate(prop) + + def setpersistent(self, prop, val): + self._db_setpersistent(prop, val) + + def getpersistent(self, prop): + return self._db_getpersistent(prop) + def __event_conversations_changed(self): pass @@ -364,6 +415,100 @@ class SidebandCore(): dbc.execute("DROP TABLE IF EXISTS announce") dbc.execute("CREATE TABLE announce (id PRIMARY KEY, received INTEGER, source BLOB, data BLOB, dest_type BLOB)") + dbc.execute("DROP TABLE IF EXISTS state") + dbc.execute("CREATE TABLE state (property BLOB PRIMARY KEY, value BLOB)") + + dbc.execute("DROP TABLE IF EXISTS persistent") + dbc.execute("CREATE TABLE persistent (property BLOB PRIMARY KEY, value BLOB)") + + db.commit() + db.close() + + def _db_initstate(self): + db = sqlite3.connect(self.db_path) + dbc = db.cursor() + + dbc.execute("DROP TABLE IF EXISTS state") + dbc.execute("CREATE TABLE state (property BLOB PRIMARY KEY, value BLOB)") + self._db_setstate("database_ready", True) + + def _db_getstate(self, prop): + db = sqlite3.connect(self.db_path) + dbc = db.cursor() + + query = "select * from state where property=:uprop" + dbc.execute(query, {"uprop": prop.encode("utf-8")}) + result = dbc.fetchall() + db.close() + + if len(result) < 1: + return None + else: + try: + entry = result[0] + val = msgpack.unpackb(entry[1]) + return val + except Exception as e: + RNS.log("Could not unpack state value from database for property \""+str(prop)+"\". The contained exception was: "+str(e), RNS.LOG_ERROR) + return None + + def _db_setstate(self, prop, val): + db = sqlite3.connect(self.db_path) + dbc = db.cursor() + uprop = prop.encode("utf-8") + bval = msgpack.packb(val) + + if self._db_getstate(prop) == None: + query = "INSERT INTO state (property, value) values (?, ?)" + data = (uprop, bval) + dbc.execute(query, data) + else: + query = "UPDATE state set value=:bval where property=:uprop;" + dbc.execute(query, {"bval": bval, "uprop": uprop}) + + db.commit() + db.close() + + def _db_initpersistent(self): + db = sqlite3.connect(self.db_path) + dbc = db.cursor() + + dbc.execute("CREATE TABLE IF NOT EXISTS persistent (property BLOB PRIMARY KEY, value BLOB)") + + def _db_getpersistent(self, prop): + db = sqlite3.connect(self.db_path) + dbc = db.cursor() + + query = "select * from persistent where property=:uprop" + dbc.execute(query, {"uprop": prop.encode("utf-8")}) + result = dbc.fetchall() + db.close() + + if len(result) < 1: + return None + else: + try: + entry = result[0] + val = msgpack.unpackb(entry[1]) + return val + except Exception as e: + RNS.log("Could not unpack persistent value from database for property \""+str(prop)+"\". The contained exception was: "+str(e), RNS.LOG_ERROR) + return None + + def _db_setpersistent(self, prop, val): + db = sqlite3.connect(self.db_path) + dbc = db.cursor() + uprop = prop.encode("utf-8") + bval = msgpack.packb(val) + + if self._db_getpersistent(prop) == None: + query = "INSERT INTO persistent (property, value) values (?, ?)" + data = (uprop, bval) + dbc.execute(query, data) + else: + query = "UPDATE persistent set value=:bval where property=:uprop;" + dbc.execute(query, {"bval": bval, "uprop": uprop}) + db.commit() db.close() @@ -687,7 +832,11 @@ class SidebandCore(): db.close() def lxmf_announce(self): - self.lxmf_destination.announce() + if self.is_standalone or self.is_service: + self.lxmf_destination.announce() + self.setstate("wants.announce", False) + else: + self.setstate("wants.announce", True) def is_known(self, dest_hash): try: @@ -710,11 +859,21 @@ class SidebandCore(): RNS.log("Error while querying for key: "+str(e), RNS.LOG_ERROR) return False + def _service_jobs(self): + if self.is_service: + while True: + time.sleep(SidebandCore.JOB_INTERVAL) + if self.getstate("wants.announce"): + self.lxmf_announce() + def __start_jobs_deferred(self): if self.config["start_announce"]: - # TODO: (Service) Handle this in service - self.lxmf_destination.announce() + self.lxmf_announce() + if self.is_service: + self.service_thread = threading.Thread(target=self._service_jobs, daemon=True) + self.service_thread.start() + def __start_jobs_immediate(self): # TODO: Reset loglevel self.reticulum = RNS.Reticulum(configdir=self.rns_configdir, loglevel=7) @@ -828,19 +987,14 @@ class SidebandCore(): RNS.log("Error while adding I2P Interface. The contained exception was: "+str(e)) self.interface_i2p = None - if self.is_service or self.is_standalone: - RNS.log("Reticulum started, activating LXMF...") - self.message_router = LXMF.LXMRouter(identity = self.identity, storagepath = self.lxmf_storage, autopeer = True) - self.message_router.register_delivery_callback(self.lxmf_delivery) + RNS.log("Reticulum started, activating LXMF...") + self.message_router = LXMF.LXMRouter(identity = self.identity, storagepath = self.lxmf_storage, autopeer = True) + self.message_router.register_delivery_callback(self.lxmf_delivery) - self.lxmf_destination = self.message_router.register_delivery_identity(self.identity, display_name=self.config["display_name"]) - self.lxmf_destination.set_default_app_data(self.get_display_name_bytes) + self.lxmf_destination = self.message_router.register_delivery_identity(self.identity, display_name=self.config["display_name"]) + self.lxmf_destination.set_default_app_data(self.get_display_name_bytes) - self.rns_dir = RNS.Reticulum.configdir - - else: - self.message_router = self.serviceproxy - self.lxmf_destination = self.serviceproxy + self.rns_dir = RNS.Reticulum.configdir if self.config["lxmf_propagation_node"] != None and self.config["lxmf_propagation_node"] != "": self.set_active_propagation_node(self.config["lxmf_propagation_node"]) @@ -941,23 +1095,21 @@ class SidebandCore(): if self._db_conversation(context_dest) == None: self._db_create_conversation(context_dest) - self.owner_app.flag_new_conversations = True + self.setstate("app.flags.new_conversations", True) - if self.owner_app.root.ids.screen_manager.current == "messages_screen": - if self.owner_app.root.ids.messages_scrollview.active_conversation != context_dest: + if self.gui_display() == "messages_screen": + if self.gui_conversation() != context_dest: self.unread_conversation(context_dest) - self.owner_app.flag_unread_conversations = True + self.setstate("app.flags.unread_conversations", True) else: - if self.owner_app.is_in_foreground(): + if self.gui_foreground(): should_notify = False else: self.unread_conversation(context_dest) - self.owner_app.flag_unread_conversations = True + self.setstate("app.flags.unread_conversations", True) - try: - self.owner_app.conversation_update(context_dest) - except Exception as e: - RNS.log("Error in conversation update callback: "+str(e)) + if self.is_client: + should_notify = False if should_notify: nlen = 128 @@ -966,7 +1118,7 @@ class SidebandCore(): if len(text) > nlen: text += "..." - self.owner_app.notify(title="Message from "+self.peer_display_name(context_dest), content=notification_content) + self.notify(title=self.peer_display_name(context_dest), content=notification_content, group="LXM", context_id=RNS.hexrep(context_dest, delimit=False)) def start(self): @@ -976,6 +1128,8 @@ class SidebandCore(): thread = threading.Thread(target=self.__start_jobs_deferred) thread.setDaemon(True) thread.start() + + self._db_setstate("core.started", True) RNS.log("Sideband Core "+str(self)+" started") def request_lxmf_sync(self, limit = None): diff --git a/sbapp/ui/announces.py b/sbapp/ui/announces.py index 4e59cb0..44024a3 100644 --- a/sbapp/ui/announces.py +++ b/sbapp/ui/announces.py @@ -39,10 +39,8 @@ class Announces(): def update(self): self.clear_list() self.announces = self.app.sideband.list_announces() - self.update_widget() - - self.app.flag_new_announces = False + self.app.sideband.setstate("app.flags.new_announces", False) def update_widget(self): if self.list == None: diff --git a/sbapp/ui/conversations.py b/sbapp/ui/conversations.py index 0e035b9..30a4c94 100644 --- a/sbapp/ui/conversations.py +++ b/sbapp/ui/conversations.py @@ -45,14 +45,14 @@ class Conversations(): self.added_item_dests = [] def update(self): - if self.app.flag_unread_conversations: + if self.app.sideband.getstate("app.flags.unread_conversations"): self.clear_list() self.context_dests = self.app.sideband.list_conversations() self.update_widget() - self.app.flag_new_conversations = False - self.app.flag_unread_conversations = False + self.app.sideband.setstate("app.flags.unread_conversations", False) + self.app.sideband.setstate("app.flags.new_conversations", False) def update_widget(self): if self.list == None: