You've already forked MicroPythonOS
mirror of
https://github.com/m5stack/MicroPythonOS.git
synced 2026-05-20 11:51:27 -07:00
Refactor app structure
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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()")
|
||||
|
||||
@@ -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
|
||||
@@ -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"
|
||||
]
|
||||
@@ -0,0 +1,5 @@
|
||||
from .chooser import ChooserActivity
|
||||
from .view import ViewActivity
|
||||
from .share import ShareActivity
|
||||
|
||||
__all__ = ["ChooserActivity", "ViewActivity", "ShareActivity"]
|
||||
@@ -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")
|
||||
@@ -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")
|
||||
@@ -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")
|
||||
@@ -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)
|
||||
@@ -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,
|
||||
)
|
||||
@@ -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 <appdir>/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]
|
||||
}
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
from .intent import Intent
|
||||
__all__ = ["Intent"]
|
||||
@@ -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
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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}")
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user