You've already forked MicroPythonOS
mirror of
https://github.com/m5stack/MicroPythonOS.git
synced 2026-05-20 11:51:27 -07:00
adapt apps to new "back button" paradigm
This commit is contained in:
@@ -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()
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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()
|
||||
@@ -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.")
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user