Dependency hell

This commit is contained in:
Thomas Farstrike
2025-10-30 13:03:19 +01:00
parent c2c5089e1f
commit 163511f8d6
12 changed files with 67 additions and 51 deletions
@@ -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):
@@ -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):
+1 -1
View File
@@ -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
@@ -1,3 +1,4 @@
# Import all activity modules → triggers self-registration
from .chooser import ChooserActivity
from .view import ViewActivity
from .share import ShareActivity
@@ -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")
@@ -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)
@@ -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)
+5 -2
View File
@@ -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)
+5 -1
View File
@@ -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)
+11 -5
View File
@@ -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)
@@ -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))
+9 -12
View File
@@ -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)