Rename PackageManager to AppManager

This commit is contained in:
Thomas Farstrike
2026-01-25 00:19:38 +01:00
parent 31dcfba683
commit c2ae169638
29 changed files with 123 additions and 124 deletions
@@ -2,7 +2,7 @@ import os
import json
import lvgl as lv
from mpos import Activity, DownloadManager, PackageManager, TaskManager
from mpos import Activity, DownloadManager, AppManager, TaskManager
class AppDetail(Activity):
@@ -142,7 +142,7 @@ class AppDetail(Activity):
self.install_label = lv.label(self.install_button)
self.install_label.center()
self.set_install_label(self.app.fullname)
if app.version and PackageManager.is_update_available(self.app.fullname, app.version):
if app.version and AppManager.is_update_available(self.app.fullname, app.version):
self.install_button.set_size(lv.pct(47), 40) # make space for update button
print("Update available, adding update button.")
self.update_button = lv.button(buttoncont)
@@ -171,10 +171,10 @@ class AppDetail(Activity):
# - update is separate button, only shown if already installed and new version
is_installed = True
update_available = False
builtin_app = PackageManager.is_builtin_app(app_fullname)
overridden_builtin_app = PackageManager.is_overridden_builtin_app(app_fullname)
builtin_app = AppManager.is_builtin_app(app_fullname)
overridden_builtin_app = AppManager.is_overridden_builtin_app(app_fullname)
if not overridden_builtin_app:
is_installed = PackageManager.is_installed_by_name(app_fullname)
is_installed = AppManager.is_installed_by_name(app_fullname)
if is_installed:
if builtin_app:
if overridden_builtin_app:
@@ -214,12 +214,12 @@ class AppDetail(Activity):
self._show_progress_bar()
await self._update_progress(21)
await self._update_progress(42)
PackageManager.uninstall_app(app_fullname)
AppManager.uninstall_app(app_fullname)
await self._update_progress(100, wait=False)
self._hide_progress_bar()
self.set_install_label(app_fullname)
self.install_button.remove_state(lv.STATE.DISABLED)
if PackageManager.is_builtin_app(app_fullname):
if AppManager.is_builtin_app(app_fullname):
self.update_button.remove_flag(lv.obj.FLAG.HIDDEN)
self.install_button.set_size(lv.pct(47), 40) # if a builtin app was removed, then it was overridden, and a new version is available, so make space for update button
@@ -256,7 +256,7 @@ class AppDetail(Activity):
else:
print("Downloaded .mpk file, size:", os.stat(temp_zip_path)[6], "bytes")
# Install it:
PackageManager.install_mpk(temp_zip_path, dest_folder) # 60 until 80 percent is the unzip but no progress there...
AppManager.install_mpk(temp_zip_path, dest_folder) # 60 until 80 percent is the unzip but no progress there...
await self._update_progress(80, wait=False)
except Exception as e:
print(f"Download failed with exception: {e}")
@@ -9,7 +9,7 @@
# All icons took: 1250ms
# Most of this time is actually spent reading and parsing manifests.
import lvgl as lv
from mpos import AppearanceManager, PackageManager, Activity, DisplayMetrics
from mpos import AppearanceManager, AppManager, Activity, DisplayMetrics
import time
import uhashlib
import ubinascii
@@ -54,7 +54,7 @@ class Launcher(Activity):
# ------------------------------------------------------------------
# 1. Build a *compact* representation of the current app list
current_apps = []
for app in PackageManager.get_app_list():
for app in AppManager.get_app_list():
if app.category == "launcher":
continue
icon_hash = Launcher._hash_file(app.icon_path) # cheap SHA-1 of the icon file
@@ -90,7 +90,7 @@ class Launcher(Activity):
iconcont_width = icon_size + label_height
iconcont_height = icon_size + label_height
for app in PackageManager.get_app_list():
for app in AppManager.get_app_list():
if app.category == "launcher":
continue
@@ -128,7 +128,7 @@ class Launcher(Activity):
# ----- events --------------------------------------------------
app_cont.add_event_cb(
lambda e, fullname=app.fullname: PackageManager.start_app(fullname),
lambda e, fullname=app.fullname: AppManager.start_app(fullname),
lv.EVENT.CLICKED, None)
app_cont.add_event_cb(
lambda e, cont=app_cont: self.focus_app_cont(cont),
@@ -2,7 +2,7 @@ import lvgl as lv
import ujson
import time
from mpos import Activity, PackageManager, ConnectivityManager, TaskManager, DownloadManager, DisplayMetrics, DeviceInfo, BuildInfo
from mpos import Activity, AppManager, ConnectivityManager, TaskManager, DownloadManager, DisplayMetrics, DeviceInfo, BuildInfo
class OSUpdate(Activity):
@@ -770,7 +770,7 @@ class UpdateChecker:
Returns:
bool: True if remote version is newer
"""
return PackageManager.compare_versions(remote_version, current_version)
return AppManager.compare_versions(remote_version, current_version)
# Non-class functions:
@@ -1,6 +1,6 @@
import lvgl as lv
from mpos import Intent, PackageManager, SettingActivity, SettingsActivity, TimeZone
from mpos import Intent, AppManager, SettingActivity, SettingsActivity, TimeZone
from calibrate_imu import CalibrateIMUActivity
from check_imu_calibration import CheckIMUCalibrationActivity
@@ -44,7 +44,7 @@ class Settings(SettingsActivity):
{"title": "Theme Color", "key": "theme_primary_color", "placeholder": "HTML hex color, like: EC048C", "ui": "dropdown", "ui_options": theme_colors, "changed_callback": self.theme_changed},
{"title": "Timezone", "key": "timezone", "ui": "dropdown", "ui_options": [(tz, tz) for tz in TimeZone.get_timezones()], "changed_callback": lambda *args: mpos.time.refresh_timezone_preference()},
# Advanced settings, alphabetically:
{"title": "Auto Start App", "key": "auto_start_app", "ui": "radiobuttons", "ui_options": [(app.name, app.fullname) for app in PackageManager.get_app_list()]},
{"title": "Auto Start App", "key": "auto_start_app", "ui": "radiobuttons", "ui_options": [(app.name, app.fullname) for app in AppManager.get_app_list()]},
{"title": "Check IMU Calibration", "key": "check_imu_calibration", "ui": "activity", "activity_class": CheckIMUCalibrationActivity},
{"title": "Calibrate IMU", "key": "calibrate_imu", "ui": "activity", "activity_class": CalibrateIMUActivity},
# Expert settings, alphabetically
@@ -92,7 +92,7 @@ class Settings(SettingsActivity):
# This will throw an exception if there is already a "/builtin" folder present
print("settings.py: WARNING: could not import/run freezefs_mount_builtin: ", e)
print("Done mounting, refreshing apps")
PackageManager.refresh_apps()
AppManager.refresh_apps()
def theme_changed(self, new_value):
from mpos import AppearanceManager
+2 -2
View File
@@ -4,7 +4,7 @@ from .app.activity import Activity
from .content.intent import Intent
from .activity_navigator import ActivityNavigator
from .content.package_manager import PackageManager
from .content.app_manager import AppManager
from .config import SharedPreferences
from .net.connectivity_manager import ConnectivityManager
from .net.wifi_service import WifiService
@@ -65,7 +65,7 @@ __all__ = [
"Activity",
"SharedPreferences",
"ConnectivityManager", "DownloadManager", "WifiService", "AudioFlinger", "Intent",
"ActivityNavigator", "PackageManager", "TaskManager", "CameraManager",
"ActivityNavigator", "AppManager", "TaskManager", "CameraManager",
# Device and build info
"DeviceInfo", "BuildInfo",
# Common activities
@@ -2,7 +2,7 @@ import sys
import utime
from .content.intent import Intent
from .content.package_manager import PackageManager
from .content.app_manager import AppManager
import mpos.ui
@@ -13,7 +13,7 @@ class ActivityNavigator:
if not isinstance(intent, Intent):
raise ValueError("Must provide an Intent")
if intent.action: # Implicit intent: resolve handlers
handlers = PackageManager.resolve_activity(intent)
handlers = AppManager.resolve_activity(intent)
if not handlers:
print("No handler for action:", intent.action)
return
@@ -31,7 +31,7 @@ class ActivityNavigator:
if not isinstance(intent, Intent):
raise ValueError("Must provide an Intent")
if intent.action: # Implicit intent: resolve handlers
handlers = PackageManager.resolve_activity(intent)
handlers = AppManager.resolve_activity(intent)
if not handlers:
print("No handler for action:", intent.action)
return
@@ -2,7 +2,7 @@ from ..activity import Activity
# Chooser doesn't handle an action — it shows handlers
# → No registration needed
from ...content.package_manager import PackageManager
from ...content.app_manager import AppManager
class ChooserActivity(Activity):
def __init__(self):
@@ -27,7 +27,7 @@ class ChooserActivity(Activity):
self.setContentView(screen)
def _select_handler(self, handler_name, original_intent):
for handler in PackageManager.APP_REGISTRY.get(original_intent.action, []):
for handler in AppManager.APP_REGISTRY.get(original_intent.action, []):
if handler.__name__ == handler_name:
original_intent.activity_class = handler
navigator.startActivity(original_intent)
@@ -1,5 +1,5 @@
from ..activity import Activity
from ...content.package_manager import PackageManager
from ...content.app_manager import AppManager
class ShareActivity(Activity):
def __init__(self):
@@ -35,4 +35,4 @@ class ShareActivity(Activity):
else:
print("Stopped for other screen")
PackageManager.register_activity("share", ShareActivity)
AppManager.register_activity("share", ShareActivity)
@@ -1,5 +1,5 @@
from ..activity import Activity
from ...content.package_manager import PackageManager
from ...content.app_manager import AppManager
class ViewActivity(Activity):
def __init__(self):
@@ -28,4 +28,4 @@ class ViewActivity(Activity):
print("Stopped for other screen")
# Register this activity for "view" intents
PackageManager.register_activity("view", ViewActivity)
AppManager.register_activity("view", ViewActivity)
@@ -28,7 +28,7 @@ Question: does it make sense to cache the database?
'''
class PackageManager:
class AppManager:
_registry = {} # action → [ActivityClass, ...]
@@ -52,9 +52,9 @@ class PackageManager:
"""Registry of all discovered apps.
* PackageManager.get_app_list() -> list of App objects (sorted by name)
* PackageManager[fullname] -> App (raises KeyError if missing)
* PackageManager.get(fullname) -> App or None
* AppManager.get_app_list() -> list of App objects (sorted by name)
* AppManager[fullname] -> App (raises KeyError if missing)
* AppManager.get(fullname) -> App or None
"""
_app_list = [] # sorted by app.name
@@ -93,7 +93,7 @@ class PackageManager:
@classmethod
def refresh_apps(cls):
print("PackageManager finding apps...")
print("AppManager finding apps...")
cls.clear() # <-- this guarantees both containers are empty
seen = set() # avoid processing the same fullname twice
@@ -117,7 +117,7 @@ class PackageManager:
if not (st[0] & 0x4000):
continue
except Exception as e:
print("PackageManager: stat of {} got exception: {}".format(full_path, e))
print("AppManager: stat of {} got exception: {}".format(full_path, e))
continue
fullname = name
@@ -132,7 +132,7 @@ class PackageManager:
from ..app.app import App
app = App.from_manifest(full_path)
except Exception as e:
print("PackageManager: parsing {} failed: {}".format(full_path, e))
print("AppManager: parsing {} failed: {}".format(full_path, e))
continue
# ---- store in both containers ---------------------------
@@ -141,7 +141,7 @@ class PackageManager:
print("added app {}".format(app))
except Exception as e:
print("PackageManager: handling {} got exception: {}".format(base, e))
print("AppManager: handling {} got exception: {}".format(base, e))
# ---- sort the list by display name (case-insensitive) ------------
cls._app_list.sort(key=lambda a: a.name.lower())
@@ -153,7 +153,7 @@ class PackageManager:
shutil.rmtree(f"apps/{app_fullname}") # never in builtin/apps because those can't be uninstalled
except Exception as e:
print(f"Removing app_folder {app_folder} got error: {e}")
PackageManager.refresh_apps()
AppManager.refresh_apps()
@staticmethod
def install_mpk(temp_zip_path, dest_folder):
@@ -169,7 +169,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()
AppManager.refresh_apps()
@staticmethod
def compare_versions(ver1: str, ver2: str) -> bool:
@@ -200,20 +200,20 @@ class PackageManager:
@staticmethod
def is_builtin_app(app_fullname):
return PackageManager.is_installed_by_path(f"builtin/apps/{app_fullname}")
return AppManager.is_installed_by_path(f"builtin/apps/{app_fullname}")
@staticmethod
def is_overridden_builtin_app(app_fullname):
return PackageManager.is_installed_by_path(f"apps/{app_fullname}") and PackageManager.is_installed_by_path(f"builtin/apps/{app_fullname}")
return AppManager.is_installed_by_path(f"apps/{app_fullname}") and AppManager.is_installed_by_path(f"builtin/apps/{app_fullname}")
@staticmethod
def is_update_available(app_fullname, new_version):
appdir = f"apps/{app_fullname}"
builtinappdir = f"builtin/apps/{app_fullname}"
installed_app=PackageManager.get(app_fullname)
installed_app=AppManager.get(app_fullname)
if not installed_app:
return False
return PackageManager.compare_versions(new_version, installed_app.version)
return AppManager.compare_versions(new_version, installed_app.version)
@staticmethod
def is_installed_by_path(dir_path):
@@ -231,7 +231,7 @@ class PackageManager:
@staticmethod
def is_installed_by_name(app_fullname):
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}")
return AppManager.is_installed_by_path(f"apps/{app_fullname}") or AppManager.is_installed_by_path(f"builtin/apps/{app_fullname}")
@staticmethod
def execute_script(script_source, is_file, classname, cwd=None):
@@ -311,7 +311,7 @@ class PackageManager:
mpos.ui.set_foreground_app(fullname)
import utime
start_time = utime.ticks_ms()
app = PackageManager.get(fullname)
app = AppManager.get(fullname)
if not app:
print(f"Warning: start_app can't find app {fullname}")
return
@@ -325,7 +325,7 @@ class PackageManager:
else:
entrypoint = app.main_launcher_activity.get('entrypoint')
classname = app.main_launcher_activity.get("classname")
result = PackageManager.execute_script(app.installed_path + "/" + entrypoint, True, classname, app.installed_path + "/assets/")
result = AppManager.execute_script(app.installed_path + "/" + entrypoint, True, classname, app.installed_path + "/assets/")
# Launchers have the bar, other apps don't have it
if app.is_valid_launcher():
mpos.ui.topmenu.open_bar()
@@ -343,5 +343,4 @@ class PackageManager:
# Stop all apps
mpos.ui.remove_and_stop_all_activities()
# No need to stop the other launcher first, because it exits after building the screen
return PackageManager.start_app(PackageManager.get_launcher().fullname)
return AppManager.start_app(AppManager.get_launcher().fullname)
+4 -4
View File
@@ -5,7 +5,7 @@ import lvgl as lv
import mpos.ui
import mpos.ui.topmenu
from mpos import AppearanceManager, DisplayMetrics, PackageManager, SharedPreferences, TaskManager, DeviceInfo
from mpos import AppearanceManager, DisplayMetrics, AppManager, SharedPreferences, TaskManager, DeviceInfo
# White text on black logo works (for dark mode) and can be inverted (for light mode)
logo_white = "M:builtin/res/mipmap-mdpi/MicroPythonOS-logo-white-long-w296.png" # from the MPOS-logo repo
@@ -124,12 +124,12 @@ except Exception as e:
print(f"Couldn't start WifiService.auto_connect thread because: {e}")
# Start launcher so it's always at bottom of stack
launcher_app = PackageManager.get_launcher()
started_launcher = PackageManager.start_app(launcher_app.fullname)
launcher_app = AppManager.get_launcher()
started_launcher = AppManager.start_app(launcher_app.fullname)
# Then start auto_start_app if configured
auto_start_app = prefs.get_string("auto_start_app", None)
if auto_start_app and launcher_app.fullname != auto_start_app:
result = PackageManager.start_app(auto_start_app)
result = AppManager.start_app(auto_start_app)
if result is not True:
print(f"WARNING: could not run {auto_start_app} app")
@@ -815,7 +815,7 @@ class MockThread:
class MockApps:
"""
Mock mpos.apps module for testing (deprecated, use MockPackageManager instead).
Mock mpos.apps module for testing (deprecated, use MockAppManager instead).
This is kept for backward compatibility with existing tests.
@@ -839,12 +839,12 @@ class MockApps:
return True
class MockPackageManager:
class MockAppManager:
"""
Mock mpos.content.package_manager module for testing.
Mock mpos.content.app_manager module for testing.
Usage:
sys.modules['mpos.content.package_manager'] = MockPackageManager
sys.modules['mpos.content.app_manager'] = MockAppManager
"""
@staticmethod
+4 -4
View File
@@ -13,10 +13,10 @@ infrastructure are already initialized (boot.py and main.py executed).
Usage in tests:
from mpos.ui.testing import wait_for_render, capture_screenshot
from mpos import PackageManager
from mpos import AppManager
# Start your app
PackageManager.start_app("com.example.myapp")
AppManager.start_app("com.example.myapp")
# Wait for UI to render
wait_for_render()
@@ -63,8 +63,8 @@ def wait_for_render(iterations=10):
iterations: Number of task handler iterations to run (default: 10)
Example:
from mpos import PackageManager
PackageManager.start_app("com.example.myapp")
from mpos import AppManager
AppManager.start_app("com.example.myapp")
wait_for_render() # Ensure UI is ready
assert verify_text_present(lv.screen_active(), "Welcome")
"""
+5 -5
View File
@@ -7,7 +7,7 @@ from .appearance_manager import AppearanceManager
from .util import (get_foreground_app)
from . import focus_direction
from .widget_animator import WidgetAnimator
from mpos.content.package_manager import PackageManager
from mpos.content.app_manager import AppManager
CLOCK_UPDATE_INTERVAL = 1000 # 10 or even 1 ms doesn't seem to change the framerate but 100ms is enough
WIFI_ICON_UPDATE_INTERVAL = 1500
@@ -268,7 +268,7 @@ def create_drawer():
wifi_label.center()
def wifi_event(e):
close_drawer()
PackageManager.start_app("com.micropythonos.wifi")
AppManager.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))
@@ -278,7 +278,7 @@ def create_drawer():
settings_label.center()
def settings_event(e):
close_drawer()
PackageManager.start_app("com.micropythonos.settings")
AppManager.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))
@@ -289,7 +289,7 @@ def create_drawer():
def launcher_event(e):
print("Launch button pressed!")
close_drawer(True)
PackageManager.restart_launcher()
AppManager.restart_launcher()
launcher_btn.add_event_cb(launcher_event,lv.EVENT.CLICKED,None)
'''
sleep_btn=lv.button(drawer)
@@ -308,7 +308,7 @@ def create_drawer():
else: # assume unix:
# maybe do a system suspend here? or at least show a popup toast "not supported"
close_drawer(True)
PackageManager.restart_launcher()
AppManager.restart_launcher()
sleep_btn.add_event_cb(sleep_event,lv.EVENT.CLICKED,None)
'''
restart_btn=lv.button(drawer)
+1 -1
View File
@@ -1,6 +1,6 @@
import unittest
from mpos import App, PackageManager
from mpos import App, AppManager
from camera import Camera, GrabMode, PixelFormat, FrameSize, GainCeiling
+1 -1
View File
@@ -5,7 +5,7 @@ import _thread
import time
import unittest
from mpos import App, PackageManager
from mpos import App, AppManager
from nostr.relay_manager import RelayManager
from nostr.message_type import ClientMessageType
+3 -3
View File
@@ -27,7 +27,7 @@ from mpos import (
print_screen_labels,
DeviceInfo,
BuildInfo,
PackageManager
AppManager
)
@@ -78,7 +78,7 @@ class TestGraphicalAboutApp(unittest.TestCase):
print("\n=== Starting About app test ===")
# Start the About app
result = PackageManager.start_app("com.micropythonos.about")
result = AppManager.start_app("com.micropythonos.about")
self.assertTrue(result, "Failed to start About app")
# Wait for UI to fully render
@@ -146,7 +146,7 @@ class TestGraphicalAboutApp(unittest.TestCase):
print("\n=== Starting About app OS version test ===")
# Start the About app
result = PackageManager.start_app("com.micropythonos.about")
result = AppManager.start_app("com.micropythonos.about")
self.assertTrue(result, "Failed to start About app")
# Wait for UI to render
+3 -3
View File
@@ -31,7 +31,7 @@ from mpos import (
print_screen_labels,
simulate_click,
get_widget_coords,
PackageManager
AppManager
)
@unittest.skipIf(sys.platform == 'darwin', "Camera tests not supported on macOS (no camera available)")
@@ -117,7 +117,7 @@ class TestGraphicalCameraSettings(unittest.TestCase):
print("\n=== Testing settings button click (no crash) ===")
# Start the Camera app
result = PackageManager.start_app("com.micropythonos.camera")
result = AppManager.start_app("com.micropythonos.camera")
self.assertTrue(result, "Failed to start Camera app")
# Wait for camera to initialize and first frame to render
@@ -251,7 +251,7 @@ class TestGraphicalCameraSettings(unittest.TestCase):
print("\n=== Testing resolution change (no crash) ===")
# Start the Camera app
result = PackageManager.start_app("com.micropythonos.camera")
result = AppManager.start_app("com.micropythonos.camera")
self.assertTrue(result, "Failed to start Camera app")
# Wait for camera to initialize
+4 -4
View File
@@ -27,7 +27,7 @@ from mpos import (
click_label,
click_button,
find_text_on_screen,
PackageManager
AppManager
)
@@ -63,7 +63,7 @@ class TestIMUCalibration(unittest.TestCase):
print("\n=== Testing CheckIMUCalibrationActivity ===")
# Navigate: Launcher -> Settings -> Check IMU Calibration
result = PackageManager.start_app("com.micropythonos.settings")
result = AppManager.start_app("com.micropythonos.settings")
self.assertTrue(result, "Failed to start Settings app")
wait_for_render(15)
@@ -98,7 +98,7 @@ class TestIMUCalibration(unittest.TestCase):
print("\n=== Testing CalibrateIMUActivity Flow ===")
# Navigate: Launcher -> Settings -> Calibrate IMU
result = PackageManager.start_app("com.micropythonos.settings")
result = AppManager.start_app("com.micropythonos.settings")
self.assertTrue(result, "Failed to start Settings app")
wait_for_render(15)
@@ -155,7 +155,7 @@ class TestIMUCalibration(unittest.TestCase):
print("\n=== Testing Check -> Calibrate Navigation ===")
# Navigate to Check activity
result = PackageManager.start_app("com.micropythonos.settings")
result = AppManager.start_app("com.micropythonos.settings")
self.assertTrue(result)
wait_for_render(15)
@@ -27,7 +27,7 @@ from mpos import (
click_label,
click_button,
find_text_on_screen,
PackageManager
AppManager
)
@@ -46,7 +46,7 @@ class TestIMUCalibrationUI(unittest.TestCase):
print("Step 2: Opening Settings app...")
# Start Settings app by name
PackageManager.start_app("com.micropythonos.settings")
AppManager.start_app("com.micropythonos.settings")
wait_for_render(iterations=30)
print("Settings app opened\n")

Some files were not shown because too many files have changed in this diff Show More