From 68dd24dc03aedc2316e9de7e72d899e0754ce79a Mon Sep 17 00:00:00 2001 From: Thomas Farstrike Date: Sat, 24 May 2025 18:02:18 +0200 Subject: [PATCH] adapt apps to new "back button" paradigm --- .../com.example.camtest/assets/camtest.py | 34 +++-- internal_filesystem/boot_unix.py | 2 +- .../com.example.appstore/assets/appstore.py | 67 ++++----- .../com.example.launcher/assets/launcher.py | 14 +- .../com.example.osupdate/assets/osupdate.py | 30 ++-- internal_filesystem/lib/mpos/apps.py | 44 ++---- internal_filesystem/lib/mpos/config.py | 138 ++++++++++++++++++ internal_filesystem/lib/mpos/ui.py | 2 + internal_filesystem/main.py | 2 +- 9 files changed, 230 insertions(+), 103 deletions(-) create mode 100644 internal_filesystem/lib/mpos/config.py diff --git a/internal_filesystem/apps/com.example.camtest/assets/camtest.py b/internal_filesystem/apps/com.example.camtest/assets/camtest.py index 48d958d3..c0f116b9 100644 --- a/internal_filesystem/apps/com.example.camtest/assets/camtest.py +++ b/internal_filesystem/apps/com.example.camtest/assets/camtest.py @@ -7,6 +7,12 @@ import time +import mpos.apps +import mpos.ui + +# screens: +main_screen = None + keepliveqrdecoding = False width = 240 height = 240 @@ -72,7 +78,7 @@ def qrdecode_one(): def close_button_click(e): print("Close button clicked") - show_launcher() + mpos.ui.back_screen() def snap_button_click(e): @@ -142,20 +148,20 @@ def try_capture(event): def build_ui(): - global image, image_dsc,qr_label, status_label, cam, use_webcam, qr_button, snap_button, status_label_cont - cont = lv.obj(appscreen) - cont.set_style_pad_all(0, 0) - cont.set_style_border_width(0, 0) - cont.set_size(lv.pct(100), lv.pct(100)) - cont.set_scrollbar_mode(lv.SCROLLBAR_MODE.OFF) - close_button = lv.button(cont) + global image, image_dsc,qr_label, status_label, cam, use_webcam, qr_button, snap_button, status_label_cont, main_screen + main_screen = lv.obj() + main_screen.set_style_pad_all(0, 0) + main_screen.set_style_border_width(0, 0) + main_screen.set_size(lv.pct(100), lv.pct(100)) + main_screen.set_scrollbar_mode(lv.SCROLLBAR_MODE.OFF) + close_button = lv.button(main_screen) close_button.set_size(60,60) close_button.align(lv.ALIGN.TOP_RIGHT, 0, 0) close_label = lv.label(close_button) close_label.set_text(lv.SYMBOL.CLOSE) close_label.center() close_button.add_event_cb(close_button_click,lv.EVENT.CLICKED,None) - snap_button = lv.button(cont) + snap_button = lv.button(main_screen) snap_button.set_size(60, 60) snap_button.align(lv.ALIGN.RIGHT_MID, 0, 0) snap_button.add_flag(lv.obj.FLAG.HIDDEN) @@ -163,7 +169,7 @@ def build_ui(): snap_label.set_text(lv.SYMBOL.OK) snap_label.center() snap_button.add_event_cb(snap_button_click,lv.EVENT.CLICKED,None) - qr_button = lv.button(cont) + qr_button = lv.button(main_screen) qr_button.set_size(60, 60) qr_button.add_flag(lv.obj.FLAG.HIDDEN) qr_button.align(lv.ALIGN.BOTTOM_RIGHT, 0, 0) @@ -172,7 +178,7 @@ def build_ui(): qr_label.center() qr_button.add_event_cb(qr_button_click,lv.EVENT.CLICKED,None) # Initialize LVGL image widget - image = lv.image(cont) + image = lv.image(main_screen) image.align(lv.ALIGN.LEFT_MID, 0, 0) # Create image descriptor once image_dsc = lv.image_dsc_t({ @@ -188,7 +194,7 @@ def build_ui(): 'data': None # Will be updated per frame }) image.set_src(image_dsc) - status_label_cont = lv.obj(appscreen) + status_label_cont = lv.obj(main_screen) status_label_cont.set_size(lv.pct(66),lv.pct(60)) status_label_cont.align(lv.ALIGN.LEFT_MID, lv.pct(5), 0) status_label_cont.set_style_bg_color(lv.color_white(), 0) @@ -200,6 +206,7 @@ def build_ui(): status_label.set_style_text_color(lv.color_white(), 0) status_label.set_width(lv.pct(100)) status_label.center() + mpos.ui.load_screen(main_screen) def init_cam(): @@ -235,7 +242,7 @@ def init_cam(): def check_running(timer): - if lv.screen_active() != appscreen: + if lv.screen_active() != main_screen: print("camtest.py backgrounded, cleaning up...") check_running_timer.delete() if capture_timer: @@ -248,7 +255,6 @@ def check_running(timer): -appscreen = lv.screen_active() build_ui() cam = init_cam() diff --git a/internal_filesystem/boot_unix.py b/internal_filesystem/boot_unix.py index 2cc60dea..57d80638 100644 --- a/internal_filesystem/boot_unix.py +++ b/internal_filesystem/boot_unix.py @@ -61,7 +61,7 @@ def swipe_read_cb(indev_drv, data): # Mouse/touch pressed (start of potential swipe) start_y = y # Store Y for vertical swipe detection start_x = x # Store X for horizontal swipe detection - print(f"Mouse press at X={start_x}, Y={start_y}") + #print(f"Mouse press at X={start_x}, Y={start_y}") # Check if press is in notification bar (for swipe down) if y <= mpos.ui.NOTIFICATION_BAR_HEIGHT: diff --git a/internal_filesystem/builtin/apps/com.example.appstore/assets/appstore.py b/internal_filesystem/builtin/apps/com.example.appstore/assets/appstore.py index 944cacce..1ef402fe 100644 --- a/internal_filesystem/builtin/apps/com.example.appstore/assets/appstore.py +++ b/internal_filesystem/builtin/apps/com.example.appstore/assets/appstore.py @@ -7,10 +7,11 @@ import time import _thread import mpos.apps +import mpos.ui # Screens: app_detail_screen = None -appscreen = lv.screen_active() +main_screen = None apps = [] update_button = None @@ -58,10 +59,10 @@ def is_update_available(app_fullname, new_version): installed_app=None if is_installed_by_path(appdir): print(f"{appdir} found, getting version...") - installed_app = parse_manifest(f"{appdir}/META-INF/MANIFEST.JSON") + installed_app = mpos.apps.parse_manifest(f"{appdir}/META-INF/MANIFEST.JSON") elif is_installed_by_path(builtinappdir): print(f"{builtinappdir} found, getting version...") - installed_app = parse_manifest(f"{builtinappdir}/META-INF/MANIFEST.JSON") + installed_app = mpos.apps.parse_manifest(f"{builtinappdir}/META-INF/MANIFEST.JSON") if not installed_app or installed_app.version == "0.0.0": # special case, if the installed app doesn't have a version number then there's no update return False return compare_versions(new_version, installed_app.version) @@ -264,10 +265,14 @@ def load_icon(icon_path): return image_dsc def create_apps_list(): - global apps + global apps, main_screen + main_screen = lv.obj() + please_wait_label = lv.label(main_screen) + please_wait_label.set_text("Downloading app index...") + please_wait_label.center() default_icon_dsc = load_icon("builtin/res/mipmap-mdpi/default_icon_64x64.png") print("create_apps_list") - apps_list = lv.list(appscreen) + apps_list = lv.list(main_screen) apps_list.set_style_pad_all(0, 0) apps_list.set_size(lv.pct(100), lv.pct(100)) print("create_apps_list iterating") @@ -300,6 +305,7 @@ def create_apps_list(): desc_label.set_style_text_font(lv.font_montserrat_12, 0) desc_label.add_event_cb(lambda e, a=app: show_app_detail(a), lv.EVENT.CLICKED, None) print("create_apps_list app done") + mpos.ui.load_screen(main_screen) try: _thread.stack_size(16*1024) _thread.start_new_thread(download_icons,()) @@ -309,21 +315,21 @@ def create_apps_list(): def show_app_detail(app): global app_detail_screen, install_button, progress_bar, install_label + #app_detail_screen = lv.obj() + #app_detail_screen.set_size(lv.pct(100), lv.pct(100)) + #back_button = lv.button(app_detail_screen) + #back_button.set_width(lv.pct(15)) + #back_button.add_flag(lv.obj.FLAG.CLICKABLE) + #back_button.add_event_cb(back_to_main, lv.EVENT.CLICKED, None) + #back_label = lv.label(back_button) + #back_label.set_text(lv.SYMBOL.LEFT) + #back_label.center() app_detail_screen = lv.obj() app_detail_screen.set_size(lv.pct(100), lv.pct(100)) - back_button = lv.button(app_detail_screen) - back_button.set_width(lv.pct(15)) - back_button.add_flag(lv.obj.FLAG.CLICKABLE) - back_button.add_event_cb(back_to_main, lv.EVENT.CLICKED, None) - back_label = lv.label(back_button) - back_label.set_text(lv.SYMBOL.LEFT) - back_label.center() - cont = lv.obj(app_detail_screen) - cont.set_size(lv.pct(100), lv.pct(100)) - cont.set_pos(0, 40) - cont.set_flex_flow(lv.FLEX_FLOW.COLUMN) + app_detail_screen.set_pos(0, 40) + app_detail_screen.set_flex_flow(lv.FLEX_FLOW.COLUMN) # - headercont = lv.obj(cont) + headercont = lv.obj(app_detail_screen) headercont.set_style_pad_all(0, 0) headercont.set_flex_flow(lv.FLEX_FLOW.ROW) headercont.set_size(lv.pct(100), lv.SIZE_CONTENT) @@ -344,12 +350,12 @@ def show_app_detail(app): publisher_label.set_text(app.publisher) publisher_label.set_style_text_font(lv.font_montserrat_16, 0) # - progress_bar = lv.bar(cont) + progress_bar = lv.bar(app_detail_screen) progress_bar.set_width(lv.pct(100)) progress_bar.set_range(0, 100) progress_bar.add_flag(lv.obj.FLAG.HIDDEN) # Always have this button: - buttoncont = lv.obj(cont) + buttoncont = lv.obj(app_detail_screen) buttoncont.set_style_pad_all(0, 0) buttoncont.set_flex_flow(lv.FLEX_FLOW.ROW) buttoncont.set_size(lv.pct(100), lv.SIZE_CONTENT) @@ -373,17 +379,17 @@ def show_app_detail(app): update_label.set_text("Update") update_label.center() # version label: - version_label = lv.label(cont) + version_label = lv.label(app_detail_screen) version_label.set_width(lv.pct(100)) version_label.set_text(f"Latest version: {app.version}") # make this bold if this is newer than the currently installed one version_label.set_style_text_font(lv.font_montserrat_12, 0) version_label.align_to(install_button, lv.ALIGN.OUT_BOTTOM_MID, 0, lv.pct(5)) - long_desc_label = lv.label(cont) + long_desc_label = lv.label(app_detail_screen) long_desc_label.align_to(version_label, lv.ALIGN.OUT_BOTTOM_MID, 0, lv.pct(5)) long_desc_label.set_text(app.long_description) long_desc_label.set_style_text_font(lv.font_montserrat_12, 0) long_desc_label.set_width(lv.pct(100)) - lv.screen_load(app_detail_screen) + mpos.ui.load_screen(app_detail_screen) def toggle_install(download_url, fullname): @@ -416,28 +422,17 @@ def update_button_click(download_url, fullname): print("Could not start download_and_unzip thread: ", e) -def back_to_main(event): - global app_detail_screen - if app_detail_screen: - app_detail_screen.delete() - app_detail_screen = None - lv.screen_load(appscreen) - def janitor_cb(timer): - global appscreen, app_detail_screen - if lv.screen_active() != appscreen and lv.screen_active() != app_detail_screen: + global main_screen, app_detail_screen + if lv.screen_active() != main_screen and lv.screen_active() != app_detail_screen: print("appstore.py backgrounded, cleaning up...") janitor.delete() - restart_launcher() # refresh the launcher + mpos.apps.restart_launcher() # refresh the launcher print("appstore.py ending") janitor = lv.timer_create(janitor_cb, 400, None) -please_wait_label = lv.label(appscreen) -please_wait_label.set_text("Downloading app index...") -please_wait_label.center() - can_check_network = True try: import network diff --git a/internal_filesystem/builtin/apps/com.example.launcher/assets/launcher.py b/internal_filesystem/builtin/apps/com.example.launcher/assets/launcher.py index 6e13dd59..f215b96f 100644 --- a/internal_filesystem/builtin/apps/com.example.launcher/assets/launcher.py +++ b/internal_filesystem/builtin/apps/com.example.launcher/assets/launcher.py @@ -9,17 +9,19 @@ # All icons took: 1250ms # Most of this time is actually spent reading and parsing manifests. -appscreen = lv.screen_active() - import uos import lvgl as lv +import mpos.apps +import mpos.ui # Create a container for the grid -cont = lv.obj(appscreen) -cont.set_pos(0, NOTIFICATION_BAR_HEIGHT) # leave some margin for the notification bar +main_screen = lv.obj() +cont = lv.obj(main_screen) +cont.set_pos(0, mpos.ui.NOTIFICATION_BAR_HEIGHT) # leave some margin for the notification bar cont.set_size(lv.pct(100), lv.pct(100)) cont.set_style_pad_all(10, 0) cont.set_flex_flow(lv.FLEX_FLOW.ROW_WRAP) +mpos.ui.load_screen(main_screen) # Grid parameters icon_size = 64 # Adjust based on your display @@ -56,7 +58,7 @@ for dir_path in [apps_dir, apps_dir_builtin]: base_name = d if base_name not in seen_base_names: # Avoid duplicates seen_base_names.add(base_name) - app = parse_manifest(f"{full_path}/META-INF/MANIFEST.JSON") + app = mpos.apps.parse_manifest(f"{full_path}/META-INF/MANIFEST.JSON") if app.category != "launcher": # Skip launchers app_list.append((app.name, full_path)) except OSError: @@ -94,7 +96,7 @@ for app_name, app_dir_fullpath in app_list: label.set_width(iconcont_width) label.align(lv.ALIGN.BOTTOM_MID, 0, 0) label.set_style_text_align(lv.TEXT_ALIGN.CENTER, 0) - app_cont.add_event_cb(lambda e, path=app_dir_fullpath: start_app(path), lv.EVENT.CLICKED, None) + app_cont.add_event_cb(lambda e, path=app_dir_fullpath: mpos.apps.start_app(path), lv.EVENT.CLICKED, None) end = time.ticks_ms() print(f"Displaying all icons took: {end-start}ms") diff --git a/internal_filesystem/builtin/apps/com.example.osupdate/assets/osupdate.py b/internal_filesystem/builtin/apps/com.example.osupdate/assets/osupdate.py index bb7fc46b..cb9b846a 100644 --- a/internal_filesystem/builtin/apps/com.example.osupdate/assets/osupdate.py +++ b/internal_filesystem/builtin/apps/com.example.osupdate/assets/osupdate.py @@ -1,12 +1,14 @@ -appscreen = lv.screen_active() -appscreen.clean() - import lvgl as lv import requests import ujson import time import _thread +import mpos.info +import mpos.ui + +main_screen = None + status_label=None install_button=None @@ -34,7 +36,7 @@ def compare_versions(ver1: str, ver2: str) -> bool: # Custom OTA update with LVGL progress def update_with_lvgl(url): - global install_button, status_label + global install_button, status_label, main_screen install_button.add_flag(lv.obj.FLAG.HIDDEN) # or change to cancel button? status_label.set_text("Update in progress.\nNavigate away to cancel.") import ota.update @@ -45,10 +47,10 @@ def update_with_lvgl(url): print(f"Current partition: {current_partition}") next_partition = current_partition.get_next_update() print(f"Next partition: {next_partition}") - label = lv.label(appscreen) + label = lv.label(main_screen) label.set_text("OS Update: 0.00%") label.align(lv.ALIGN.CENTER, 0, -30) - progress_bar = lv.bar(appscreen) + progress_bar = lv.bar(main_screen) progress_bar.set_size(200, 20) progress_bar.align(lv.ALIGN.BOTTOM_MID, 0, -50) progress_bar.set_range(0, 100) @@ -65,7 +67,7 @@ def update_with_lvgl(url): chunk_size = 4096 i = 0 print(f"Starting OTA update of size: {total_size}") - while appscreen == lv.screen_active(): # stop if the user navigates away + while main_screen == lv.screen_active(): # stop if the user navigates away time.sleep_ms(100) # don't hog the CPU chunk = response.raw.read(chunk_size) if not chunk: @@ -96,8 +98,8 @@ def install_button_click(download_url): def handle_update_info(version, download_url, changelog): global install_button, status_label - label = f"Installed OS version: {CURRENT_OS_VERSION}\n" - if compare_versions(version, CURRENT_OS_VERSION): + label = f"Installed OS version: {mpos.info.CURRENT_OS_VERSION}\n" + if compare_versions(version, mpos.info.CURRENT_OS_VERSION): label += "Available new" install_button.remove_flag(lv.obj.FLAG.HIDDEN) install_button.add_event_cb(lambda e, u=download_url: install_button_click(u), lv.EVENT.CLICKED, None) @@ -137,15 +139,17 @@ def show_update_info(): print("Error:", str(e)) -install_button = lv.button(appscreen) -install_button.align(lv.ALIGN.TOP_RIGHT, 0, NOTIFICATION_BAR_HEIGHT) +main_screen = lv.obj() +install_button = lv.button(main_screen) +install_button.align(lv.ALIGN.TOP_RIGHT, 0, mpos.ui.NOTIFICATION_BAR_HEIGHT) install_button.add_flag(lv.obj.FLAG.HIDDEN) # button will be shown if there is an update available install_button.set_size(lv.SIZE_CONTENT, lv.pct(20)) install_label = lv.label(install_button) install_label.set_text("Update OS") install_label.center() -status_label = lv.label(appscreen) -status_label.align(lv.ALIGN.TOP_LEFT,0,NOTIFICATION_BAR_HEIGHT) +status_label = lv.label(main_screen) +status_label.align(lv.ALIGN.TOP_LEFT,0,mpos.ui.NOTIFICATION_BAR_HEIGHT) +mpos.ui.load_screen(main_screen) network_connected = True try: diff --git a/internal_filesystem/lib/mpos/apps.py b/internal_filesystem/lib/mpos/apps.py index fa721e88..c23e9819 100644 --- a/internal_filesystem/lib/mpos/apps.py +++ b/internal_filesystem/lib/mpos/apps.py @@ -7,11 +7,12 @@ import uos import _thread import traceback +import mpos.apps import mpos.info import mpos.ui # Run the script in the current thread: -def execute_script(script_source, is_file, is_launcher, is_graphical): +def execute_script(script_source, is_file): thread_id = _thread.get_ident() compile_name = 'script' if not is_file else script_source print(f"Thread {thread_id}: executing script") @@ -20,31 +21,10 @@ def execute_script(script_source, is_file, is_launcher, is_graphical): print(f"Thread {thread_id}: reading script from file {script_source}") with open(script_source, 'r') as f: # TODO: check if file exists first? script_source = f.read() - if not is_graphical: - script_globals = { - '__name__': "__main__" - } - else: # is_graphical - if is_launcher: - prevscreen = None - newscreen = mpos.ui.rootscreen - else: - prevscreen = lv.screen_active() - newscreen=lv.obj() - newscreen.set_size(lv.pct(100),lv.pct(100)) - mpos.ui.load_screen(newscreen) - script_globals = { - 'lv': lv, - 'th': mpos.ui.th, - 'NOTIFICATION_BAR_HEIGHT': mpos.ui.NOTIFICATION_BAR_HEIGHT, # for apps that want to leave space for notification bar - 'appscreen': newscreen, - 'start_app': start_app, # for launcher apps - 'parse_manifest': parse_manifest, # for launcher apps - 'restart_launcher': restart_launcher, # for appstore apps - 'show_launcher': mpos.ui.show_launcher, # for apps that want to show the launcher - 'CURRENT_OS_VERSION': mpos.info.CURRENT_OS_VERSION, # for osupdate - '__name__': "__main__" - } + script_globals = { + 'lv': lv, + '__name__': "__main__" + } print(f"Thread {thread_id}: starting script") try: compiled_script = compile(script_source, compile_name, 'exec') @@ -63,8 +43,8 @@ def execute_script(script_source, is_file, is_launcher, is_graphical): # Run the script in a new thread: # TODO: check if the script exists here instead of launching a new thread? -def execute_script_new_thread(scriptname, is_file, is_launcher, is_graphical): - print(f"main.py: execute_script_new_thread({scriptname},{is_file},{is_launcher})") +def execute_script_new_thread(scriptname, is_file): + print(f"main.py: execute_script_new_thread({scriptname},{is_file})") try: # 168KB maximum at startup but 136KB after loading display, drivers, LVGL gui etc so let's go for 128KB for now, still a lot... # But then no additional threads can be created. A stacksize of 32KB allows for 4 threads, so 3 in the app itself, which might be tight. @@ -77,12 +57,12 @@ def execute_script_new_thread(scriptname, is_file, is_launcher, is_graphical): stack=32*1024 elif "appstore"in scriptname: print("Starting appstore with extra stack size!") - stack=24*1024 + stack=24*1024 # this doesn't do anything because it's all started in the same thread else: stack=16*1024 # 16KB doesn't seem to be enough for the AppStore app on desktop print(f"app.py: setting stack size for script to {stack}") _thread.stack_size(stack) - _thread.start_new_thread(execute_script, (scriptname, is_file, is_launcher, is_graphical)) + _thread.start_new_thread(execute_script, (scriptname, is_file)) except Exception as e: print("main.py: execute_script_new_thread(): error starting new thread thread: ", e) @@ -100,10 +80,10 @@ def start_app(app_dir, is_launcher=False): print(f"main.py start_app({app_dir},{is_launcher})") mpos.ui.set_foreground_app(app_dir) # would be better to store only the app name... manifest_path = f"{app_dir}/META-INF/MANIFEST.JSON" - app = parse_manifest(manifest_path) + app = mpos.apps.parse_manifest(manifest_path) start_script_fullpath = f"{app_dir}/{app.entrypoint}" #execute_script_new_thread(start_script_fullpath, True, is_launcher, True) # Starting (GUI?) apps in a new thread can cause hangs (GIL lock?) - execute_script(start_script_fullpath, True, is_launcher, True) + execute_script(start_script_fullpath, True) # Launchers have the bar, other apps don't have it if is_launcher: mpos.ui.open_bar() diff --git a/internal_filesystem/lib/mpos/config.py b/internal_filesystem/lib/mpos/config.py new file mode 100644 index 00000000..a2108f95 --- /dev/null +++ b/internal_filesystem/lib/mpos/config.py @@ -0,0 +1,138 @@ +import ujson +import os + +class SharedPreferences: + def __init__(self, appname, filename="config.json"): + """Initialize with appname and filename for preferences.""" + self.appname = appname + self.filename = filename + self.filepath = f"data/{self.appname}/{self.filename}" + self.data = {} + self.load() # Load existing preferences + + def make_folder_structure(self): + """Create directory structure if it doesn't exist.""" + #print("Checking if data/ exists") + try: + os.stat('data') + #print("data/ exists") + except OSError: + print("Creating data/ directory") + os.mkdir('data') + #print(f"Checking if data/{self.appname}/ exists") + try: + os.stat(f"data/{self.appname}") + #print(f"data/{self.appname}/ exists") + except OSError: + #print(f"Creating data/{self.appname} directory") + os.mkdir(f"data/{self.appname}") + + def load(self): + """Load preferences from the JSON file.""" + try: + with open(self.filepath, 'r') as f: + self.data = ujson.load(f) + print(f"load: Loaded preferences: {self.data}") + except Exception as e: + print(f"load: Got exception {e}, assuming empty or non-existent preferences.") + self.data = {} # Default to empty dict on error + + def get_string(self, key, default=None): + """Retrieve a string value for the given key, with a default if not found.""" + return str(self.data.get(key, default)) + + def get_int(self, key, default=0): + """Retrieve an integer value for the given key, with a default if not found.""" + try: + return int(self.data.get(key, default)) + except (TypeError, ValueError): + return default + + def get_bool(self, key, default=False): + """Retrieve a boolean value for the given key, with a default if not found.""" + try: + return bool(self.data.get(key, default)) + except (TypeError, ValueError): + return default + + def edit(self): + """Return an Editor object to modify preferences.""" + return Editor(self) + + def save_config(self): + """Save preferences to the JSON file.""" + self.make_folder_structure() # Ensure directories exist + print(f"save_config: Saving preferences to {self.filepath}") + try: + with open(self.filepath, 'w') as f: + ujson.dump(self.data, f) + print("save_config: Saved") + except Exception as e: + print(f"save_config: Got exception {e}") + +class Editor: + def __init__(self, preferences): + """Initialize Editor with a reference to SharedPreferences.""" + self.preferences = preferences + self.temp_data = preferences.data.copy() # Work on a copy of the data + + def put_string(self, key, value): + """Store a string value.""" + self.temp_data[key] = str(value) + return self + + def put_int(self, key, value): + """Store an integer value.""" + self.temp_data[key] = int(value) + return self + + def put_bool(self, key, value): + """Store a boolean value.""" + self.temp_data[key] = bool(value) + return self + + def apply(self): + """Save changes to the file asynchronously (emulated).""" + self.preferences.data = self.temp_data.copy() + self.preferences.save_config() + + def commit(self): + """Save changes to the file synchronously.""" + self.preferences.data = self.temp_data.copy() + self.preferences.save_config() + return True + +# Example usage +def main(): + # Initialize SharedPreferences with an appname + prefs = SharedPreferences("com.example.test_shared_prefs") + + print("Getting preferences:") + print(f"theme: {prefs.get_string('theme')}") + print(f"volume: {prefs.get_int('volume')}") + print(f"notifications: {prefs.get_bool('notifications')}") + print("Done getting preferences.") + + # Save some settings using Editor + editor = prefs.edit() + editor.put_string("theme", "dark") + editor.put_int("volume", 75) + editor.put_bool("notifications", True) + editor.apply() # Asynchronous save (emulated) + + # Read back the settings + print("Theme:", prefs.get_string("theme", "light")) + print("Volume:", prefs.get_int("volume", 50)) + print("Notifications:", prefs.get_bool("notifications", False)) + + # Modify a setting + editor = prefs.edit() + editor.put_string("theme", "light") + success = editor.commit() # Synchronous save + print("Save successful:", success) + + # Read updated setting + print("Updated Theme:", prefs.get_string("theme", "light")) + +if __name__ == '__main__': + main() diff --git a/internal_filesystem/lib/mpos/ui.py b/internal_filesystem/lib/mpos/ui.py index 744ba807..0db478c2 100644 --- a/internal_filesystem/lib/mpos/ui.py +++ b/internal_filesystem/lib/mpos/ui.py @@ -457,5 +457,7 @@ def back_screen(): screen_stack.pop() # Remove current screen prevscreen = screen_stack[-1] # load previous screen lv.screen_load(prevscreen) + if len(screen_stack) == 1: + open_bar() else: print("Warning: can't go back because screen_stack is empty.") diff --git a/internal_filesystem/main.py b/internal_filesystem/main.py index b5be6148..7bb47c2b 100644 --- a/internal_filesystem/main.py +++ b/internal_filesystem/main.py @@ -13,7 +13,7 @@ except Exception as e: print("main.py: WARNING: could not import/run freezefs_mount_builtin: ", e) from mpos import apps -apps.execute_script("builtin/system/button.py", True, False, False) # Install button handler through IRQ +apps.execute_script("builtin/system/button.py", True) # Install button handler through IRQ def dummy(): pass