diff --git a/internal_filesystem/builtin/apps/com.micropythonos.launcher/assets/launcher.py b/internal_filesystem/builtin/apps/com.micropythonos.launcher/assets/launcher.py index cd3c9156..eb7968f8 100644 --- a/internal_filesystem/builtin/apps/com.micropythonos.launcher/assets/launcher.py +++ b/internal_filesystem/builtin/apps/com.micropythonos.launcher/assets/launcher.py @@ -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: diff --git a/internal_filesystem/lib/mpos/apps.py b/internal_filesystem/lib/mpos/apps.py index 37f6b97c..00a77950 100644 --- a/internal_filesystem/lib/mpos/apps.py +++ b/internal_filesystem/lib/mpos/apps.py @@ -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: diff --git a/internal_filesystem/lib/mpos/package_manager.py b/internal_filesystem/lib/mpos/package_manager.py index fe217fe8..5f885156 100644 --- a/internal_filesystem/lib/mpos/package_manager.py +++ b/internal_filesystem/lib/mpos/package_manager.py @@ -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 diff --git a/internal_filesystem/lib/mpos/ui/topmenu.py b/internal_filesystem/lib/mpos/ui/topmenu.py index cc3536df..b961b3aa 100644 --- a/internal_filesystem/lib/mpos/ui/topmenu.py +++ b/internal_filesystem/lib/mpos/ui/topmenu.py @@ -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))