PackageManager: refresh applist after (un)install, reduce parse_manifest() calls

This commit is contained in:
Thomas Farstrike
2025-10-30 00:13:25 +01:00
parent cea4e59341
commit 056db408d7
4 changed files with 59 additions and 59 deletions
@@ -83,7 +83,7 @@ class Launcher(mpos.apps.Activity):
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: mpos.apps.start_app(path), lv.EVENT.CLICKED, None)
app_cont.add_event_cb(lambda e, fullname=app.fullname: mpos.apps.start_app(fullname), lv.EVENT.CLICKED, None)
app_cont.add_event_cb(lambda e, app_cont=app_cont: self.focus_app_cont(app_cont),lv.EVENT.FOCUSED,None)
app_cont.add_event_cb(lambda e, app_cont=app_cont: self.defocus_app_cont(app_cont),lv.EVENT.DEFOCUSED,None)
if focusgroup:
+17 -43
View File
@@ -105,31 +105,25 @@ def execute_script_new_thread(scriptname, is_file):
except Exception as e:
print("main.py: execute_script_new_thread(): error starting new thread thread: ", e)
def start_app_by_name(app_name, is_launcher=False):
mpos.ui.set_foreground_app(app_name)
custom_app_dir=f"apps/{app_name}"
builtin_app_dir=f"builtin/apps/{app_name}"
try:
stat = uos.stat(custom_app_dir)
start_app(custom_app_dir, is_launcher)
except OSError:
start_app(builtin_app_dir, is_launcher)
def start_app(app_dir, is_launcher=False):
print(f"main.py start_app({app_dir},{is_launcher})")
def start_app(fullname):
mpos.ui.set_foreground_app(fullname)
import utime
start_time = utime.ticks_ms()
mpos.ui.set_foreground_app(app_dir) # would be better to store only the app name...
app = mpos.apps.parse_manifest(app_dir)
print(f"start_app parsed manifest and got: {str(app)}")
main_launcher_activity = find_main_launcher_activity(app)
if not main_launcher_activity:
print(f"WARNING: can't start {app_dir} because no main_launcher_activity was found.")
app = PackageManager.get(fullname)
if not app:
print(f"Warning: start_app could not find app {fullname}, aborting...")
return
start_script_fullpath = f"{app_dir}/{main_launcher_activity.get('entrypoint')}"
execute_script(start_script_fullpath, True, app_dir + "/assets/", main_launcher_activity.get("classname"))
if not app.installed_path:
print(f"Warning: start_app could not find installed_path for {fullname}, aborting...")
return
main_launcher_activity = PackageManager.find_main_launcher_activity(app)
if not main_launcher_activity:
print(f"WARNING: can't start {fullname} because no main_launcher_activity was found.")
return
start_script_fullpath = f"{app.installed_path}/{main_launcher_activity.get('entrypoint')}"
execute_script(start_script_fullpath, True, app.installed_path + "/assets/", main_launcher_activity.get("classname"))
# Launchers have the bar, other apps don't have it
if is_launcher:
if PackageManager.is_launcher(fullname):
mpos.ui.topmenu.open_bar()
else:
mpos.ui.topmenu.close_bar()
@@ -143,29 +137,9 @@ def restart_launcher():
# No need to stop the other launcher first, because it exits after building the screen
for app in mpos.package_manager.PackageManager.get_app_list():
#print(f"checking {app}")
if app.category == "launcher" and find_main_launcher_activity(app):
if app.category == "launcher" and PackageManager.find_main_launcher_activity(app):
print(f"Found launcher, starting {app.fullname}")
start_app_by_name(app.fullname, True)
def find_main_launcher_activity(app):
result = None
for activity in app.activities:
if not activity.get("entrypoint") or not activity.get("classname"):
print(f"Warning: activity {activity} has no entrypoint and classname, skipping...")
continue
print("checking activity's intent_filters...")
for intent_filter in activity.get("intent_filters"):
print("checking intent_filter...")
if intent_filter.get("action") == "main" and intent_filter.get("category") == "launcher":
print("found main_launcher!")
result = activity
break
return result
def is_launcher(app_name):
print(f"checking is_launcher for {app_name}")
# Simple check, could be more elaborate by checking the MANIFEST.JSON for the app...
return "launcher" in app_name or len(mpos.ui.screen_stack) < 2 # assumes the first one on the stack is the launcher
start_app(app.fullname)
class App:
+39 -13
View File
@@ -1,5 +1,6 @@
import os
import mpos.apps
import mpos.ui # wont be needed after improving is_launcher()
try:
import zipfile
@@ -52,7 +53,7 @@ class PackageManager:
@classmethod
def get_app_list(cls):
if not cls._app_list:
cls.find_apps()
cls.refresh_apps()
return cls._app_list
# --------------------------------------------------------------------- #
@@ -71,13 +72,23 @@ class PackageManager:
def get(cls, fullname):
return cls._by_fullname.get(fullname)
# --------------------------------------------------------------------- #
# Clear everything useful when you want to force a full rescan
# --------------------------------------------------------------------- #
@classmethod
def clear(cls):
"""Empty the internal caches. Call ``get_app_list()`` afterwards to repopulate."""
cls._app_list = []
cls._by_fullname = {}
# --------------------------------------------------------------------- #
# discovery & population
# --------------------------------------------------------------------- #
@classmethod
def find_apps(cls):
print("\n\n\nPackageManager finding apps...")
def refresh_apps(cls):
print("PackageManager finding apps...")
cls.clear() # <-- this guarantees both containers are empty
seen = set() # avoid processing the same fullname twice
apps_dir = "apps"
apps_dir_builtin = "builtin/apps"
@@ -127,15 +138,14 @@ class PackageManager:
# ---- sort the list by display name (case-insensitive) ------------
cls._app_list.sort(key=lambda a: a.name.lower())
@staticmethod
def uninstall_app(app_fullname):
try:
import shutil
shutil.rmtree(f"apps/{app_fullname}") # never in builtin/apps because those can't be uninstalled
# TODO: also remove it from the app_list
except Exception as e:
print(f"Removing app_folder {app_folder} got error: {e}")
PackageManager.refresh_apps()
@staticmethod
def install_mpk(temp_zip_path, dest_folder):
@@ -151,6 +161,7 @@ class PackageManager:
except Exception as e:
print(f"Unzip and cleanup failed: {e}")
# Would be good to show error message here if it fails...
PackageManager.refresh_apps()
@staticmethod
def compare_versions(ver1: str, ver2: str) -> bool:
@@ -186,14 +197,8 @@ class PackageManager:
def is_update_available(app_fullname, new_version):
appdir = f"apps/{app_fullname}"
builtinappdir = f"builtin/apps/{app_fullname}"
installed_app=None
if PackageManager.is_installed_by_path(appdir):
print(f"{appdir} found, getting version...")
installed_app = mpos.apps.parse_manifest(appdir) # probably no need to re-parse the manifest
elif PackageManager.is_installed_by_path(builtinappdir):
print(f"{builtinappdir} found, getting version...")
installed_app = mpos.apps.parse_manifest(builtinappdir) # probably no need to re-parse the manifest
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
installed_app=PackageManager.get(app_fullname)
if not installed_app:
return False
return PackageManager.compare_versions(new_version, installed_app.version)
@@ -215,3 +220,24 @@ class PackageManager:
print(f"Checking if app {app_fullname} is installed...")
return PackageManager.is_installed_by_path(f"apps/{app_fullname}") or PackageManager.is_installed_by_path(f"builtin/apps/{app_fullname}")
@staticmethod
def find_main_launcher_activity(app):
result = None
for activity in app.activities:
if not activity.get("entrypoint") or not activity.get("classname"):
print(f"Warning: activity {activity} has no entrypoint and classname, skipping...")
continue
print("checking activity's intent_filters...")
for intent_filter in activity.get("intent_filters"):
print("checking intent_filter...")
if intent_filter.get("action") == "main" and intent_filter.get("category") == "launcher":
print("found main_launcher!")
result = activity
break
return result
@staticmethod
def is_launcher(app_name):
print(f"checking is_launcher for {app_name}")
# Simple check, could be more elaborate by checking the MANIFEST.JSON for the app...
return "launcher" in app_name or len(mpos.ui.screen_stack) < 2 # assumes the first one on the stack is the launcher
+2 -2
View File
@@ -253,7 +253,7 @@ def create_drawer(display=None):
wifi_label.center()
def wifi_event(e):
close_drawer()
mpos.apps.start_app_by_name("com.micropythonos.wifi")
mpos.apps.start_app("com.micropythonos.wifi")
wifi_btn.add_event_cb(wifi_event,lv.EVENT.CLICKED,None)
settings_btn=lv.button(drawer)
settings_btn.set_size(lv.pct(drawer_button_pct),lv.pct(20))
@@ -263,7 +263,7 @@ def create_drawer(display=None):
settings_label.center()
def settings_event(e):
close_drawer()
mpos.apps.start_app_by_name("com.micropythonos.settings")
mpos.apps.start_app("com.micropythonos.settings")
settings_btn.add_event_cb(settings_event,lv.EVENT.CLICKED,None)
launcher_btn=lv.button(drawer)
launcher_btn.set_size(lv.pct(drawer_button_pct),lv.pct(20))