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 8bceae6c..534d551a 100644 --- a/internal_filesystem/builtin/apps/com.micropythonos.appstore/assets/appstore.py +++ b/internal_filesystem/builtin/apps/com.micropythonos.appstore/assets/appstore.py @@ -7,6 +7,7 @@ import time import _thread from mpos.apps import Activity, Intent +from mpos.app import App import mpos.ui from mpos.package_manager import PackageManager @@ -63,7 +64,7 @@ class AppStore(Activity): applist = json.loads(response.text) for app in json.loads(response.text): try: - self.apps.append(mpos.apps.App(app["name"], app["publisher"], app["short_description"], app["long_description"], app["icon_url"], app["download_url"], app["fullname"], app["version"], app["category"], app["activities"])) + self.apps.append(App(app["name"], app["publisher"], app["short_description"], app["long_description"], app["icon_url"], app["download_url"], app["fullname"], app["version"], app["category"], app["activities"])) except Exception as e: print(f"Warning: could not add app from {json_url} to apps list: {e}") # Remove duplicates based on app.name 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 eb7968f8..6c78e7c4 100644 --- a/internal_filesystem/builtin/apps/com.micropythonos.launcher/assets/launcher.py +++ b/internal_filesystem/builtin/apps/com.micropythonos.launcher/assets/launcher.py @@ -15,8 +15,9 @@ import lvgl as lv import mpos.apps import mpos.ui from mpos.package_manager import PackageManager +from mpos import Activity -class Launcher(mpos.apps.Activity): +class Launcher(Activity): def onCreate(self): print("launcher.py onCreate()") diff --git a/internal_filesystem/lib/mpos/__init__.py b/internal_filesystem/lib/mpos/__init__.py new file mode 100644 index 00000000..b53e2465 --- /dev/null +++ b/internal_filesystem/lib/mpos/__init__.py @@ -0,0 +1,11 @@ +# Re-export common classes for convenience +from .app.app import App +from .app.activity import Activity +from .content.intent import Intent +from .navigator import ActivityNavigator +from .package_manager import PackageManager + +# Optional: re-export activities +from .app.activities.chooser import ChooserActivity +from .app.activities.view import ViewActivity +from .app.activities.share import ShareActivity diff --git a/internal_filesystem/lib/mpos/app/__init__.py b/internal_filesystem/lib/mpos/app/__init__.py new file mode 100644 index 00000000..037aa24a --- /dev/null +++ b/internal_filesystem/lib/mpos/app/__init__.py @@ -0,0 +1,10 @@ +from .app import App +from .activity import Activity +from .activities.chooser import ChooserActivity +from .activities.view import ViewActivity +from .activities.share import ShareActivity + +__all__ = [ + "App", "Activity", + "ChooserActivity", "ViewActivity", "ShareActivity" +] diff --git a/internal_filesystem/lib/mpos/app/activities/__init__.py b/internal_filesystem/lib/mpos/app/activities/__init__.py new file mode 100644 index 00000000..65acba2a --- /dev/null +++ b/internal_filesystem/lib/mpos/app/activities/__init__.py @@ -0,0 +1,5 @@ +from .chooser import ChooserActivity +from .view import ViewActivity +from .share import ShareActivity + +__all__ = ["ChooserActivity", "ViewActivity", "ShareActivity"] diff --git a/internal_filesystem/lib/mpos/app/activities/chooser.py b/internal_filesystem/lib/mpos/app/activities/chooser.py new file mode 100644 index 00000000..e611a62b --- /dev/null +++ b/internal_filesystem/lib/mpos/app/activities/chooser.py @@ -0,0 +1,47 @@ +from ..activity import Activity + +#from ..activity import Activity + +#import mpos.app.activity + +#import mpos.app.activity + +#from mpos.app import Activity + +import mpos.package_manager + +class ChooserActivity(Activity): + def __init__(self): + super().__init__() + + def onCreate(self): + screen = lv.obj() + # Get handlers from intent extras + original_intent = self.getIntent().extras.get("original_intent") + handlers = self.getIntent().extras.gepackage_managert("handlers", []) + label = lv.label(screen) + label.set_text("Choose an app") + label.set_pos(10, 10) + + for i, handler_name in enumerate(handlers): + btn = lv.btn(screen) + btn.set_user_data(f"handler_{i}") + btn_label = lv.label(btn) + btn_label.set_text(handler_name) + btn.set_pos(10, 50 * (i + 1) + 10) + btn.add_event_cb(lambda e, h=handler_name, oi=original_intent: self._select_handler(h, oi), lv.EVENT.CLICKED) + self.setContentView(screen) + + def _select_handler(self, handler_name, original_intent): + for handler in mpos.package_manager.PackageManager.APP_REGISTRY.get(original_intent.action, []): + if handler.__name__ == handler_name: + original_intent.activity_class = handler + navigator.startActivity(original_intent) + break + navigator.finish() # Close chooser + + def onStop(self, screen): + if self.getIntent() and self.getIntent().getStringExtra("destination") == "ChooserActivity": + 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 new file mode 100644 index 00000000..fc83fe4f --- /dev/null +++ b/internal_filesystem/lib/mpos/app/activities/share.py @@ -0,0 +1,35 @@ +from ..activity import Activity + +class ShareActivity(Activity): + def __init__(self): + super().__init__() + + def onCreate(self): + screen = lv.obj() + # Get text from intent (prefer extras.text, fallback to data) + text = self.getIntent().extras.get("text", self.getIntent().data or "No text") + label = lv.label(screen) + label.set_user_data("share_label") + label.set_text(f"Share: {text}") + label.set_pos(10, 10) + + btn = lv.btn(screen) + btn.set_user_data("share_btn") + btn_label = lv.label(btn) + btn_label.set_text("Share") + btn.set_pos(10, 50) + btn.add_event_cb(lambda e: self._share_content(text), lv.EVENT.CLICKED) + self.setContentView(screen) + + def _share_content(self, text): + # Dispatch to another app (e.g., MessagingActivity) or simulate sharing + print(f"Sharing: {text}") # Placeholder for actual sharing + # Example: Launch another share handler + navigator.startActivity(Intent(action="share", data=text)) + navigator.finish() # Close ShareActivity + + def onStop(self, screen): + if self.getIntent() and self.getIntent().getStringExtra("destination") == "ShareActivity": + print("Stopped for Share") + else: + print("Stopped for other screen") diff --git a/internal_filesystem/lib/mpos/app/activities/view.py b/internal_filesystem/lib/mpos/app/activities/view.py new file mode 100644 index 00000000..5e83cc1c --- /dev/null +++ b/internal_filesystem/lib/mpos/app/activities/view.py @@ -0,0 +1,27 @@ +from ..activity import Activity + +class ViewActivity(Activity): + def __init__(self): + super().__init__() + + def onCreate(self): + screen = lv.obj() + # Get content from intent (prefer extras.url, fallback to data) + content = self.getIntent().extras.get("url", self.getIntent().data or "No content") + label = lv.label(screen) + label.set_user_data("content_label") + label.set_text(f"Viewing: {content}") + label.center() + self.setContentView(screen) + + def onStart(self, screen): + content = self.getIntent().extras.get("url", self.getIntent().data or "No content") + for i in range(screen.get_child_cnt()): + if screen.get_child(i).get_user_data() == "content_label": + screen.get_child(i).set_text(f"Viewing: {content}") + + def onStop(self, screen): + if self.getIntent() and self.getIntent().getStringExtra("destination") == "ViewActivity": + print("Stopped for View") + else: + print("Stopped for other screen") diff --git a/internal_filesystem/lib/mpos/app/activity.py b/internal_filesystem/lib/mpos/app/activity.py new file mode 100644 index 00000000..8e47c400 --- /dev/null +++ b/internal_filesystem/lib/mpos/app/activity.py @@ -0,0 +1,57 @@ +from mpos.navigator import ActivityNavigator + +import mpos.ui + +class Activity: + + def __init__(self): + self.intent = None # Store the intent that launched this activity + self.result = None + self._result_callback = None + + def onCreate(self): + pass + def onStart(self, screen): + pass + def onResume(self, screen): + pass + def onPause(self, screen): + pass + def onStop(self, screen): + pass + def onDestroy(self, screen): + pass + + def setContentView(self, screen): + mpos.ui.setContentView(self, screen) + + def startActivity(self, intent): + ActivityNavigator.startActivity(intent) + + def startActivityForResult(self, intent, result_callback): + ActivityNavigator.startActivityForResult(intent, result_callback) + + def initError(self, e): + print(f"WARNING: You might have inherited from Activity with a custom __init__() without calling super().__init__(). Got AttributeError: {e}") + + def getIntent(self): + try: + return self.intent + except AttributeError as e: + self.initError(e) + + def setResult(self, result_code, data=None): + """Set the result to be returned when the activity finishes.""" + try: + self.result = {"result_code": result_code, "data": data or {}} + except AttributeError as e: + self.initError(e) + + def finish(self): + mpos.ui.back_screen() + try: + if self._result_callback and self.result: + self._result_callback(self.result) + self._result_callback = None # Clean up + except AttributeError as e: + self.initError(e) diff --git a/internal_filesystem/lib/mpos/app/app.py b/internal_filesystem/lib/mpos/app/app.py new file mode 100644 index 00000000..0bc1e2a0 --- /dev/null +++ b/internal_filesystem/lib/mpos/app/app.py @@ -0,0 +1,73 @@ +import ujson +from ..content.intent import Intent # optional, if App uses Intent + + +class App: + def __init__( + self, + name="Unknown", + publisher="Unknown", + short_description="", + long_description="", + icon_url="", + download_url="", + fullname="Unknown", + version="0.0.0", + category="", + activities=None, + installed_path=None, + ): + self.name = name + self.publisher = publisher + self.short_description = short_description + self.long_description = long_description + self.icon_url = icon_url + self.download_url = download_url + self.fullname = fullname + self.version = version + self.category = category + self.activities = activities or [] + self.installed_path = installed_path + + self.image = None + self.image_dsc = None + self.main_launcher_activity = self._find_main_launcher_activity() + + def __str__(self): + return f"App({self.name}, v{self.version}, {self.category})" + + def _find_main_launcher_activity(self): + for act in self.activities: + if not act.get("entrypoint") or not act.get("classname"): + continue + for f in act.get("intent_filters", []): + if f.get("action") == "main" and f.get("category") == "launcher": + return act + return None + + def is_valid_launcher(self): + return self.category == "launcher" and self.main_launcher_activity + + @classmethod + def from_manifest(cls, appdir): + manifest_path = f"{appdir}/META-INF/MANIFEST.JSON" + default = cls(installed_path=appdir) + try: + with open(manifest_path, "r") as f: + data = ujson.load(f) + except OSError: + return default + + return cls( + name=data.get("name", default.name), + publisher=data.get("publisher", default.publisher), + short_description=data.get("short_description", default.short_description), + long_description=data.get("long_description", default.long_description), + icon_url=data.get("icon_url", default.icon_url), + download_url=data.get("download_url", default.download_url), + fullname=data.get("fullname", default.fullname), + version=data.get("version", default.version), + category=data.get("category", default.category), + activities=data.get("activities", default.activities), + installed_path=appdir, + ) diff --git a/internal_filesystem/lib/mpos/apps.py b/internal_filesystem/lib/mpos/apps.py index 8895891b..282d82e2 100644 --- a/internal_filesystem/lib/mpos/apps.py +++ b/internal_filesystem/lib/mpos/apps.py @@ -10,6 +10,7 @@ import traceback import mpos.info import mpos.ui +from mpos import Activity, Intent from mpos.package_manager import PackageManager def good_stack_size(): @@ -142,343 +143,4 @@ def restart_launcher(): start_app(app.fullname) break -class App: - # ------------------------------------------------------------------ # - # Regular constructor – use when you already have the data - # ------------------------------------------------------------------ # - def __init__( - self, - name="Unknown", - publisher="Unknown", - short_description="", - long_description="", - icon_url="", - download_url="", - fullname="Unknown", - version="0.0.0", - category="", - activities=None, - installed_path=None, - ): - self.name = name - self.publisher = publisher - self.short_description = short_description - self.long_description = long_description - self.icon_url = icon_url - self.download_url = download_url - self.fullname = fullname - self.version = version - self.category = category - self.activities = activities if activities is not None else [] - self.installed_path = installed_path - # Cached image fields (kept for compatibility) - self.image = None - self.image_dsc = None - - # Find the main launcher activity once, at construction time - self.main_launcher_activity = self._find_main_launcher_activity() - - # ------------------------------------------------------------------ # - # Human-readable representation - # ------------------------------------------------------------------ # - def __str__(self): - return ( - f"App(name='{self.name}', " - f"publisher='{self.publisher}', " - f"short_description='{self.short_description}', " - f"version='{self.version}', " - f"category='{self.category}', " - f"activities={len(self.activities)} items, " - f"installed_path={self.installed_path})" - ) - - # ------------------------------------------------------------------ # - # Private helper – locate the MAIN/LAUNCHER activity - # ------------------------------------------------------------------ # - def _find_main_launcher_activity(self): - for activity in self.activities: - if not activity.get("entrypoint") or not activity.get("classname"): - print("Warning: activity missing entrypoint or classname – skipping") - continue - - for intent_filter in activity.get("intent_filters", []): - if ( - intent_filter.get("action") == "main" - and intent_filter.get("category") == "launcher" - ): - print("Found main launcher activity!") - return activity - return None - - # ------------------------------------------------------------------ # - # Convenience check for launcher-type apps - # ------------------------------------------------------------------ # - def is_valid_launcher(self): - return self.category == "launcher" and self.main_launcher_activity is not None - - # ------------------------------------------------------------------ # - # Class-method constructor that builds an App from a manifest file - # ------------------------------------------------------------------ # - @classmethod - def from_manifest(cls, appdir): - """ - Parse /META-INF/MANIFEST.JSON and return a fully-populated - App instance. If the file cannot be read, a default App with - placeholder values is returned. - """ - print(f"parse_manifest({appdir})") - manifest_path = f"{appdir}/META-INF/MANIFEST.JSON" - - # Minimal default instance – guarantees every field has a fallback - default = cls(installed_path=appdir) - - try: - with open(manifest_path, "r") as f: - data = ujson.load(f) - except OSError as exc: - print(f"parse_manifest: error loading {manifest_path} – {exc}") - return default - - # Merge manifest data with defaults - return cls( - name=data.get("name", default.name), - publisher=data.get("publisher", default.publisher), - short_description=data.get("short_description", default.short_description), - long_description=data.get("long_description", default.long_description), - icon_url=data.get("icon_url", default.icon_url), - download_url=data.get("download_url", default.download_url), - fullname=data.get("fullname", default.fullname), - version=data.get("version", default.version), - category=data.get("category", default.category), - activities=data.get("activities", default.activities), - installed_path=appdir, - ) - - -class Activity: - - def __init__(self): - self.intent = None # Store the intent that launched this activity - self.result = None - self._result_callback = None - - def onCreate(self): - pass - def onStart(self, screen): - pass - def onResume(self, screen): - pass - def onPause(self, screen): - pass - def onStop(self, screen): - pass - def onDestroy(self, screen): - pass - - def setContentView(self, screen): - mpos.ui.setContentView(self, screen) - - def startActivity(self, intent): - ActivityNavigator.startActivity(intent) - - def startActivityForResult(self, intent, result_callback): - ActivityNavigator.startActivityForResult(intent, result_callback) - - def initError(self, e): - print(f"WARNING: You might have inherited from Activity with a custom __init__() without calling super().__init__(). Got AttributeError: {e}") - - def getIntent(self): - try: - return self.intent - except AttributeError as e: - self.initError(e) - - def setResult(self, result_code, data=None): - """Set the result to be returned when the activity finishes.""" - try: - self.result = {"result_code": result_code, "data": data or {}} - except AttributeError as e: - self.initError(e) - - def finish(self): - mpos.ui.back_screen() - try: - if self._result_callback and self.result: - self._result_callback(self.result) - self._result_callback = None # Clean up - except AttributeError as e: - self.initError(e) - -class Intent: - def __init__(self, activity_class=None, action=None, data=None, extras=None): - self.activity_class = activity_class # Explicit target (e.g., SettingsActivity) - self.action = action # Action string (e.g., "view", "share") - self.data = data # Single data item (e.g., URL) - self.extras = extras or {} # Dictionary for additional data - self.flags = {} # Simplified flags: {"clear_top": bool, "no_history": bool, "no_animation": bool} - - def addFlag(self, flag, value=True): - self.flags[flag] = value - return self - - def putExtra(self, key, value): - self.extras[key] = value - return self - - -class ActivityNavigator: - @staticmethod - def startActivity(intent): - if not isinstance(intent, Intent): - raise ValueError("Must provide an Intent") - if intent.action: # Implicit intent: resolve handlers - handlers = APP_REGISTRY.get(intent.action, []) - 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) - - @staticmethod - def startActivityForResult(intent, result_callback): - """Launch an activity and pass a callback for the result.""" - if not isinstance(intent, Intent): - raise ValueError("Must provide an Intent") - if intent.action: # Implicit intent: resolve handlers - handlers = APP_REGISTRY.get(intent.action, []) - 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) - - @staticmethod - def _launch_activity(intent, result_callback=None): - """Launch an activity and set up result callback.""" - activity = intent.activity_class() - activity.intent = intent - activity._result_callback = result_callback # Pass callback to activity - start_time = utime.ticks_ms() - mpos.ui.save_and_clear_current_focusgroup() - activity.onCreate() - end_time = utime.ticks_diff(utime.ticks_ms(), start_time) - print(f"apps.py _launch_activity: activity.onCreate took {end_time}ms") - return activity - - @staticmethod - def _show_chooser(intent, handlers): - chooser_intent = Intent(ChooserActivity, extras={"original_intent": intent, "handlers": [h.__name__ for h in handlers]}) - ActivityNavigator._launch_activity(chooser_intent) - - -class ChooserActivity(Activity): - def __init__(self): - super().__init__() - - def onCreate(self): - screen = lv.obj() - # Get handlers from intent extras - original_intent = self.getIntent().extras.get("original_intent") - handlers = self.getIntent().extras.get("handlers", []) - label = lv.label(screen) - label.set_text("Choose an app") - label.set_pos(10, 10) - - for i, handler_name in enumerate(handlers): - btn = lv.btn(screen) - btn.set_user_data(f"handler_{i}") - btn_label = lv.label(btn) - btn_label.set_text(handler_name) - btn.set_pos(10, 50 * (i + 1) + 10) - btn.add_event_cb(lambda e, h=handler_name, oi=original_intent: self._select_handler(h, oi), lv.EVENT.CLICKED) - self.setContentView(screen) - - def _select_handler(self, handler_name, original_intent): - for handler in APP_REGISTRY.get(original_intent.action, []): - if handler.__name__ == handler_name: - original_intent.activity_class = handler - navigator.startActivity(original_intent) - break - navigator.finish() # Close chooser - - def onStop(self, screen): - if self.getIntent() and self.getIntent().getStringExtra("destination") == "ChooserActivity": - print("Stopped for Chooser") - else: - print("Stopped for other screen") - - -class ViewActivity(Activity): - def __init__(self): - super().__init__() - - def onCreate(self): - screen = lv.obj() - # Get content from intent (prefer extras.url, fallback to data) - content = self.getIntent().extras.get("url", self.getIntent().data or "No content") - label = lv.label(screen) - label.set_user_data("content_label") - label.set_text(f"Viewing: {content}") - label.center() - self.setContentView(screen) - - def onStart(self, screen): - content = self.getIntent().extras.get("url", self.getIntent().data or "No content") - for i in range(screen.get_child_cnt()): - if screen.get_child(i).get_user_data() == "content_label": - screen.get_child(i).set_text(f"Viewing: {content}") - - def onStop(self, screen): - if self.getIntent() and self.getIntent().getStringExtra("destination") == "ViewActivity": - print("Stopped for View") - else: - print("Stopped for other screen") - -class ShareActivity(Activity): - def __init__(self): - super().__init__() - - def onCreate(self): - screen = lv.obj() - # Get text from intent (prefer extras.text, fallback to data) - text = self.getIntent().extras.get("text", self.getIntent().data or "No text") - label = lv.label(screen) - label.set_user_data("share_label") - label.set_text(f"Share: {text}") - label.set_pos(10, 10) - - btn = lv.btn(screen) - btn.set_user_data("share_btn") - btn_label = lv.label(btn) - btn_label.set_text("Share") - btn.set_pos(10, 50) - btn.add_event_cb(lambda e: self._share_content(text), lv.EVENT.CLICKED) - self.setContentView(screen) - - def _share_content(self, text): - # Dispatch to another app (e.g., MessagingActivity) or simulate sharing - print(f"Sharing: {text}") # Placeholder for actual sharing - # Example: Launch another share handler - navigator.startActivity(Intent(action="share", data=text)) - navigator.finish() # Close ShareActivity - - def onStop(self, screen): - if self.getIntent() and self.getIntent().getStringExtra("destination") == "ShareActivity": - print("Stopped for Share") - else: - print("Stopped for other screen") - -APP_REGISTRY = { # This should be handled by a new class PackageManager: - "view": [ViewActivity], # Hypothetical activities - "share": [ShareActivity] -} diff --git a/internal_filesystem/lib/mpos/content/__init__.py b/internal_filesystem/lib/mpos/content/__init__.py new file mode 100644 index 00000000..cbfddf72 --- /dev/null +++ b/internal_filesystem/lib/mpos/content/__init__.py @@ -0,0 +1,2 @@ +from .intent import Intent +__all__ = ["Intent"] diff --git a/internal_filesystem/lib/mpos/content/intent.py b/internal_filesystem/lib/mpos/content/intent.py new file mode 100644 index 00000000..85f45a01 --- /dev/null +++ b/internal_filesystem/lib/mpos/content/intent.py @@ -0,0 +1,16 @@ + +class Intent: + def __init__(self, activity_class=None, action=None, data=None, extras=None): + self.activity_class = activity_class # Explicit target (e.g., SettingsActivity) + self.action = action # Action string (e.g., "view", "share") + self.data = data # Single data item (e.g., URL) + self.extras = extras or {} # Dictionary for additional data + self.flags = {} # Simplified flags: {"clear_top": bool, "no_history": bool, "no_animation": bool} + + def addFlag(self, flag, value=True): + self.flags[flag] = value + return self + + def putExtra(self, key, value): + self.extras[key] = value + return self diff --git a/internal_filesystem/lib/mpos/navigator.py b/internal_filesystem/lib/mpos/navigator.py new file mode 100644 index 00000000..4ebbbf7a --- /dev/null +++ b/internal_filesystem/lib/mpos/navigator.py @@ -0,0 +1,61 @@ +import utime +from .content.intent import Intent +#from .app.activity import Activity + +#import mpos.package_manager +import mpos.ui + +class ActivityNavigator: + @staticmethod + def startActivity(intent): + if not isinstance(intent, Intent): + raise ValueError("Must provide an Intent") + if intent.action: # Implicit intent: resolve handlers + #handlers = mpos.package_manager.PackageManager.APP_REGISTRY.get(intent.action, []) + 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) + + @staticmethod + def startActivityForResult(intent, result_callback): + """Launch an activity and pass a callback for the result.""" + 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, []) + 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) + + @staticmethod + def _launch_activity(intent, result_callback=None): + """Launch an activity and set up result callback.""" + activity = intent.activity_class() + activity.intent = intent + activity._result_callback = result_callback # Pass callback to activity + start_time = utime.ticks_ms() + mpos.ui.save_and_clear_current_focusgroup() + activity.onCreate() + end_time = utime.ticks_diff(utime.ticks_ms(), start_time) + print(f"apps.py _launch_activity: activity.onCreate took {end_time}ms") + return activity + + @staticmethod + def _show_chooser(intent, handlers): + chooser_intent = Intent(ChooserActivity, extras={"original_intent": intent, "handlers": [h.__name__ for h in handlers]}) + ActivityNavigator._launch_activity(chooser_intent) + + diff --git a/internal_filesystem/lib/mpos/package_manager.py b/internal_filesystem/lib/mpos/package_manager.py index 605ad300..4c752a55 100644 --- a/internal_filesystem/lib/mpos/package_manager.py +++ b/internal_filesystem/lib/mpos/package_manager.py @@ -1,5 +1,8 @@ import os -import mpos.apps + +from mpos.app.app import App +from mpos.app.activities.view import ViewActivity +from mpos.app.activities.share import ShareActivity try: import zipfile @@ -28,11 +31,13 @@ Question: does it make sense to cache the database? ''' -# PackageManager.py (MicroPython) - -import os - class PackageManager: + + APP_REGISTRY = { + "view": [ViewActivity], + "share": [ShareActivity] + } + """Registry of all discovered apps. * PackageManager.get_app_list() -> list of App objects (sorted by name) @@ -121,7 +126,7 @@ class PackageManager: # ---- parse the manifest --------------------------------- try: - app = mpos.apps.App.from_manifest(full_path) + app = App.from_manifest(full_path) except Exception as e: print("PackageManager: parsing {} failed: {}".format(full_path, e)) continue @@ -219,3 +224,4 @@ 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}") + diff --git a/internal_filesystem/lib/mpos/ui/__init__.py b/internal_filesystem/lib/mpos/ui/__init__.py index 63e5e4d3..1fd80833 100644 --- a/internal_filesystem/lib/mpos/ui/__init__.py +++ b/internal_filesystem/lib/mpos/ui/__init__.py @@ -1,5 +1,5 @@ import lvgl as lv -import mpos.apps +#import mpos.apps import mpos.time import mpos.wifi from mpos.ui.anim import WidgetAnimator @@ -44,8 +44,8 @@ def set_foreground_app(appname): foreground_app_name = appname print(f"foreground app is: {foreground_app_name}") -def show_launcher(): - mpos.apps.restart_launcher() +#def show_launcher(): +# mpos.apps.restart_launcher() def init_rootscreen(): global horizontal_resolution, vertical_resolution