diff --git a/internal_filesystem/builtin/apps/com.micropythonos.appstore/assets/appstore.py b/internal_filesystem/builtin/apps/com.micropythonos.appstore/assets/appstore.py index 534d551a..5401223b 100644 --- a/internal_filesystem/builtin/apps/com.micropythonos.appstore/assets/appstore.py +++ b/internal_filesystem/builtin/apps/com.micropythonos.appstore/assets/appstore.py @@ -9,7 +9,7 @@ import _thread from mpos.apps import Activity, Intent from mpos.app import App import mpos.ui -from mpos.package_manager import PackageManager +from mpos.content.pm import PackageManager class AppStore(Activity): 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 6c78e7c4..aacac865 100644 --- a/internal_filesystem/builtin/apps/com.micropythonos.launcher/assets/launcher.py +++ b/internal_filesystem/builtin/apps/com.micropythonos.launcher/assets/launcher.py @@ -14,7 +14,7 @@ import lvgl as lv import mpos.apps import mpos.ui -from mpos.package_manager import PackageManager +from mpos.content.pm import PackageManager from mpos import Activity class Launcher(Activity): diff --git a/internal_filesystem/lib/mpos/__init__.py b/internal_filesystem/lib/mpos/__init__.py index 61112812..6a6700e3 100644 --- a/internal_filesystem/lib/mpos/__init__.py +++ b/internal_filesystem/lib/mpos/__init__.py @@ -3,7 +3,7 @@ from .app.app import App from .app.activity import Activity from .content.intent import Intent from .navigator import ActivityNavigator -from .package_manager import PackageManager +from .content.pm import PackageManager # Optional: re-export activities from .app.activities.chooser import ChooserActivity diff --git a/internal_filesystem/lib/mpos/app/activities/__init__.py b/internal_filesystem/lib/mpos/app/activities/__init__.py index 65acba2a..7a336c45 100644 --- a/internal_filesystem/lib/mpos/app/activities/__init__.py +++ b/internal_filesystem/lib/mpos/app/activities/__init__.py @@ -1,3 +1,4 @@ +# Import all activity modules → triggers self-registration from .chooser import ChooserActivity from .view import ViewActivity from .share import ShareActivity diff --git a/internal_filesystem/lib/mpos/app/activities/chooser.py b/internal_filesystem/lib/mpos/app/activities/chooser.py index de9dbafa..d28bf922 100644 --- a/internal_filesystem/lib/mpos/app/activities/chooser.py +++ b/internal_filesystem/lib/mpos/app/activities/chooser.py @@ -1,6 +1,8 @@ from ..activity import Activity +# Chooser doesn't handle an action — it shows handlers +# → No registration needed -import mpos.package_manager +from ...content.pm import PackageManager class ChooserActivity(Activity): def __init__(self): @@ -25,7 +27,7 @@ class ChooserActivity(Activity): self.setContentView(screen) def _select_handler(self, handler_name, original_intent): - for handler in mpos.package_manager.PackageManager.APP_REGISTRY.get(original_intent.action, []): + for handler in PackageManager.APP_REGISTRY.get(original_intent.action, []): if handler.__name__ == handler_name: original_intent.activity_class = handler navigator.startActivity(original_intent) @@ -37,3 +39,5 @@ class ChooserActivity(Activity): print("Stopped for Chooser") else: print("Stopped for other screen") + + diff --git a/internal_filesystem/lib/mpos/app/activities/share.py b/internal_filesystem/lib/mpos/app/activities/share.py index fc83fe4f..fa7e69e1 100644 --- a/internal_filesystem/lib/mpos/app/activities/share.py +++ b/internal_filesystem/lib/mpos/app/activities/share.py @@ -1,4 +1,5 @@ from ..activity import Activity +from ...content.pm import PackageManager class ShareActivity(Activity): def __init__(self): @@ -33,3 +34,5 @@ class ShareActivity(Activity): print("Stopped for Share") else: print("Stopped for other screen") + +PackageManager.register_activity("share", ShareActivity) diff --git a/internal_filesystem/lib/mpos/app/activities/view.py b/internal_filesystem/lib/mpos/app/activities/view.py index 5e83cc1c..8721b4de 100644 --- a/internal_filesystem/lib/mpos/app/activities/view.py +++ b/internal_filesystem/lib/mpos/app/activities/view.py @@ -1,4 +1,5 @@ from ..activity import Activity +from ...content.pm import PackageManager class ViewActivity(Activity): def __init__(self): @@ -25,3 +26,6 @@ class ViewActivity(Activity): print("Stopped for View") else: print("Stopped for other screen") + +# Register this activity for "view" intents +PackageManager.register_activity("view", ViewActivity) diff --git a/internal_filesystem/lib/mpos/app/activity.py b/internal_filesystem/lib/mpos/app/activity.py index 8e47c400..4bb1d533 100644 --- a/internal_filesystem/lib/mpos/app/activity.py +++ b/internal_filesystem/lib/mpos/app/activity.py @@ -1,4 +1,4 @@ -from mpos.navigator import ActivityNavigator +#from mpos.navigator import ActivityNavigator import mpos.ui @@ -26,7 +26,10 @@ class Activity: mpos.ui.setContentView(self, screen) def startActivity(self, intent): - ActivityNavigator.startActivity(intent) + if not hasattr(self, 'app') or not self.app: + print("ERROR: Activity has no .app – cannot startActivity") + return + self.app.start_activity(intent) def startActivityForResult(self, intent, result_callback): ActivityNavigator.startActivityForResult(intent, result_callback) diff --git a/internal_filesystem/lib/mpos/app/app.py b/internal_filesystem/lib/mpos/app/app.py index 84e08763..e8f184a7 100644 --- a/internal_filesystem/lib/mpos/app/app.py +++ b/internal_filesystem/lib/mpos/app/app.py @@ -1,6 +1,6 @@ import ujson #from ..content.intent import Intent # optional, if App uses Intent - +from ..navigator import ActivityNavigator class App: def __init__( @@ -71,3 +71,7 @@ class App: activities=data.get("activities", default.activities), installed_path=appdir, ) + + def start_activity(self, intent): + """Android-like: App.startActivity(Intent)""" + return ActivityNavigator.startActivity(intent) diff --git a/internal_filesystem/lib/mpos/apps.py b/internal_filesystem/lib/mpos/apps.py index 282d82e2..fef07bd8 100644 --- a/internal_filesystem/lib/mpos/apps.py +++ b/internal_filesystem/lib/mpos/apps.py @@ -11,7 +11,7 @@ import traceback import mpos.info import mpos.ui from mpos import Activity, Intent -from mpos.package_manager import PackageManager +from mpos.content.pm import PackageManager def good_stack_size(): stacksize = 24*1024 @@ -21,7 +21,7 @@ def good_stack_size(): return stacksize # Run the script in the current thread: -def execute_script(script_source, is_file, cwd=None, classname=None): +def execute_script(script_source, is_file, cwd=None, classname=None, app=None): import utime # for timing read and compile thread_id = _thread.get_ident() compile_name = 'script' if not is_file else script_source @@ -63,7 +63,13 @@ def execute_script(script_source, is_file, cwd=None, classname=None): main_activity = script_globals.get(classname) if main_activity: start_time = utime.ticks_ms() - Activity.startActivity(None, Intent(activity_class=main_activity)) + from mpos.app.activity import Activity as BaseActivity + if app: + dummy = BaseActivity() + dummy.app = app + returned_activity = dummy.startActivity(Intent(activity_class=main_activity)) + else: + print("Warning: app not found in PackageManager") end_time = utime.ticks_diff(utime.ticks_ms(), start_time) print(f"execute_script: Activity.startActivity took {end_time}ms") else: @@ -123,7 +129,7 @@ def start_app(fullname): print(f"WARNING: start_app can't start {fullname} because it doesn't have a main_launcher_activity") return start_script_fullpath = f"{app.installed_path}/{app.main_launcher_activity.get('entrypoint')}" - execute_script(start_script_fullpath, True, app.installed_path + "/assets/", app.main_launcher_activity.get("classname")) + execute_script(start_script_fullpath, True, app.installed_path + "/assets/", app.main_launcher_activity.get("classname"), app) # Launchers have the bar, other apps don't have it if app.is_valid_launcher(): mpos.ui.topmenu.open_bar() @@ -137,7 +143,7 @@ def restart_launcher(): print("restart_launcher") mpos.ui.empty_screen_stack() # 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(): + for app in PackageManager.get_app_list(): if app.is_valid_launcher(): print(f"Found launcher, starting {app.fullname}") start_app(app.fullname) diff --git a/internal_filesystem/lib/mpos/package_manager.py b/internal_filesystem/lib/mpos/content/pm.py similarity index 83% rename from internal_filesystem/lib/mpos/package_manager.py rename to internal_filesystem/lib/mpos/content/pm.py index 4c752a55..114b5c19 100644 --- a/internal_filesystem/lib/mpos/package_manager.py +++ b/internal_filesystem/lib/mpos/content/pm.py @@ -1,9 +1,5 @@ import os -from mpos.app.app import App -from mpos.app.activities.view import ViewActivity -from mpos.app.activities.share import ShareActivity - try: import zipfile except ImportError: @@ -33,10 +29,25 @@ Question: does it make sense to cache the database? class PackageManager: - APP_REGISTRY = { - "view": [ViewActivity], - "share": [ShareActivity] - } + _registry = {} # action → [ActivityClass, ...] + + @classmethod + def register_activity(cls, action, activity_cls): + """Called by each activity module to register itself.""" + if action not in cls._registry: + cls._registry[action] = [] + if activity_cls not in cls._registry[action]: + cls._registry[action].append(activity_cls) + + @classmethod + def resolve_activity(cls, intent): + """Return list of Activity classes that handle the intent.action.""" + return cls._registry.get(intent.action, []) + + @classmethod + def query_intent_activities(cls, intent): + """Same as resolve_activity – more Android-like name.""" + return cls.resolve_activity(intent) """Registry of all discovered apps. @@ -45,49 +56,31 @@ class PackageManager: * PackageManager.get(fullname) -> App or None """ - # --------------------------------------------------------------------- # - # internal storage - # --------------------------------------------------------------------- # _app_list = [] # sorted by app.name _by_fullname = {} # fullname -> App - # --------------------------------------------------------------------- # - # public list access (kept for backward compatibility) - # --------------------------------------------------------------------- # @classmethod def get_app_list(cls): if not cls._app_list: cls.refresh_apps() return cls._app_list - # --------------------------------------------------------------------- # - # dict-style lookup: PackageManager["com.example.myapp"] - # --------------------------------------------------------------------- # def __class_getitem__(cls, fullname): try: return cls._by_fullname[fullname] except KeyError: raise KeyError("No app with fullname='{}'".format(fullname)) - # --------------------------------------------------------------------- # - # safe lookup: PackageManager.get("com.example.myapp") -> App or None - # --------------------------------------------------------------------- # @classmethod 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 refresh_apps(cls): print("PackageManager finding apps...") @@ -126,6 +119,7 @@ class PackageManager: # ---- parse the manifest --------------------------------- try: + from ..app.app import App app = App.from_manifest(full_path) except Exception as e: print("PackageManager: parsing {} failed: {}".format(full_path, e)) diff --git a/internal_filesystem/lib/mpos/navigator.py b/internal_filesystem/lib/mpos/navigator.py index 7f02c8b5..46dbd5de 100644 --- a/internal_filesystem/lib/mpos/navigator.py +++ b/internal_filesystem/lib/mpos/navigator.py @@ -1,11 +1,7 @@ import utime from .content.intent import Intent -# circular import issue: -#import mpos.package_manager -#from .package_manager import PackageManager -#from mpos import PackageManager -#from mpos import * +from .content.pm import PackageManager import mpos.ui @@ -17,14 +13,15 @@ class ActivityNavigator: if not isinstance(intent, Intent): raise ValueError("Must provide an Intent") if intent.action: # Implicit intent: resolve handlers - #handlers = PackageManager.APP_REGISTRY.get(intent.action, []) + handlers = PackageManager.resolve_activity(intent) + if not handlers: + print("No handler for action:", intent.action) + return if len(handlers) == 1: intent.activity_class = handlers[0] ActivityNavigator._launch_activity(intent) elif handlers: ActivityNavigator._show_chooser(intent, handlers) - else: - raise ValueError(f"No handlers for action: {intent.action}") else: ActivityNavigator._launch_activity(intent) @@ -34,15 +31,16 @@ class ActivityNavigator: if not isinstance(intent, Intent): raise ValueError("Must provide an Intent") if intent.action: # Implicit intent: resolve handlers - #handlers = PackageManager.APP_REGISTRY.get(intent.action, []) + handlers = PackageManager.resolve_activity(intent) + if not handlers: + print("No handler for action:", intent.action) + return if len(handlers) == 1: intent.activity_class = handlers[0] return ActivityNavigator._launch_activity(intent, result_callback) elif handlers: ActivityNavigator._show_chooser(intent, handlers) return None # Chooser handles result forwarding - else: - raise ValueError(f"No handlers for action: {intent.action}") else: return ActivityNavigator._launch_activity(intent, result_callback) @@ -64,4 +62,3 @@ class ActivityNavigator: chooser_intent = Intent(ChooserActivity, extras={"original_intent": intent, "handlers": [h.__name__ for h in handlers]}) ActivityNavigator._launch_activity(chooser_intent) -