Add sandbox API integration for RmlUi Lua state

- Register sandbox APIs (timer, JSON, crypto, VFS) into RmlUi's Lua state
- Add switchAppSandbox() function for context switching between apps
- Update goHome() to reset sandbox context when returning home
- Fix icon loading for third-party apps (handle full paths vs relative)
- Mirror changes between Android service (kernel.cpp) and desktop designer

This enables third-party apps to use sandbox APIs when running in RmlUi.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-19 15:49:35 +01:00
parent ea44f0bba4
commit cb86d52705
3 changed files with 213 additions and 8 deletions

View File

@@ -810,6 +810,19 @@ bool InitializeRmlUi(const std::string& assets_path, int fb_width, int fb_height
std::cout << "goHome called - returning to home screen" << std::endl;
// Reset sandbox back to home context
if (g_sandbox && !g_current_app_id.empty()) {
g_sandbox->UnregisterAPIs(L);
g_sandbox->Reset();
mosis::DesktopSandboxConfig config;
config.app_id = "com.mosis.home";
config.data_root = g_main_assets_path + "/sandbox_data";
g_sandbox = std::make_unique<mosis::DesktopSandbox>(config);
g_sandbox->RegisterAPIs(L);
std::cout << "Sandbox reset to home context" << std::endl;
}
// Close existing documents (except debugger)
while (g_context->GetNumDocuments() > 1) {
auto* doc = g_context->GetDocument(0);
@@ -839,7 +852,36 @@ bool InitializeRmlUi(const std::string& assets_path, int fb_width, int fb_height
return 1;
});
lua_setglobal(L, "goHome");
std::cout << "Registered Lua loadScreen and goHome functions" << std::endl;
// Register switchAppSandbox function for third-party app launching
lua_pushcfunction(L, [](lua_State* L) -> int {
const char* app_id = luaL_checkstring(L, 1);
const char* install_path = luaL_checkstring(L, 2);
std::cout << "switchAppSandbox called for: " << app_id << " at " << install_path << std::endl;
// Store current app ID
g_current_app_id = app_id;
// Reset sandbox for the new app
if (g_sandbox) {
g_sandbox->UnregisterAPIs(L);
g_sandbox->Reset();
// Re-configure sandbox with app-specific data root
mosis::DesktopSandboxConfig config;
config.app_id = app_id;
config.data_root = std::string(install_path) + "/sandbox_data";
g_sandbox = std::make_unique<mosis::DesktopSandbox>(config);
g_sandbox->RegisterAPIs(L);
std::cout << "Sandbox switched to: " << app_id << std::endl;
}
lua_pushboolean(L, true);
return 1;
});
lua_setglobal(L, "switchAppSandbox");
std::cout << "Registered Lua loadScreen, goHome, and switchAppSandbox functions" << std::endl;
// Register simulator API (if in simulator mode)
if (g_simulator_mode) {

View File

@@ -110,10 +110,22 @@ function renderThirdPartyApps()
-- Check if app has an icon
if app.icon and app.icon ~= "" then
-- Third-party app icon path would be in their install directory
-- For now, use initial as we need file:// protocol support
icon_html = '<span style="font-size: 28px; color: #000000;">' .. initial .. '</span>'
local icon_path
-- Check if icon is already a full path (starts with / or contains :/)
if app.icon:sub(1, 1) == "/" or app.icon:find(":/") then
-- Already a full path
icon_path = app.icon
elseif app.install_path and app.install_path ~= "" then
-- Relative filename - construct full path from install_path
icon_path = app.install_path .. "/" .. app.icon
else
icon_path = app.icon
end
-- Use img tag for actual icon
icon_html = '<img src="' .. icon_path .. '" style="width: 48px; height: 48px;"/>'
print("[Home] Loading icon: " .. icon_path)
else
-- Fallback to initial letter
icon_html = '<span style="font-size: 28px; color: #000000;">' .. initial .. '</span>'
end
@@ -151,9 +163,16 @@ function launchThirdPartyApp(package_id)
if success then
print("[Home] App sandbox started: " .. package_id)
-- Now load the app's UI document
-- Get app info for sandbox switching and UI loading
local app_info = getAppInfo(package_id)
if app_info and app_info.install_path and app_info.entry_point then
-- Switch sandbox context to this app (registers timer, fs, json, crypto APIs)
if switchAppSandbox then
switchAppSandbox(package_id, app_info.install_path)
print("[Home] Sandbox context switched to: " .. package_id)
end
-- Now load the app's UI document
local entry_path = app_info.install_path .. "/" .. app_info.entry_point
print("[Home] Loading app screen: " .. entry_path)
local loaded = loadScreen(entry_path)

View File

@@ -21,6 +21,12 @@
#include <fstream>
#include <filesystem>
// Sandbox API includes for RmlUi integration
#include <mosis/sandbox/timer_manager.h>
#include <mosis/sandbox/json_api.h>
#include <mosis/sandbox/crypto_api.h>
#include <mosis/sandbox/virtual_fs.h>
// Global state for Lua access
static Rml::Context* g_context = nullptr;
static Rml::ElementDocument* g_document = nullptr;
@@ -28,6 +34,15 @@ static mosis::AppManager* g_app_manager = nullptr;
static mosis::UpdateService* g_update_service = nullptr;
static mosis::LuaSandboxManager* g_sandbox_manager = nullptr;
// RmlUi sandbox state - APIs registered into RmlUi's Lua state
static std::unique_ptr<mosis::TimerManager> g_rmlui_timer_manager;
static std::unique_ptr<mosis::VirtualFS> g_rmlui_vfs;
static std::string g_current_app_id = "com.mosis.home";
static std::string g_data_root;
// Forward declarations
static void SwitchAppSandbox(const std::string& app_id, const std::string& app_data_path);
using namespace aidl::com::omixlab::mosis;
using namespace aidl::android::hardware;
@@ -268,7 +283,7 @@ static int LuaGoHome(lua_State* L)
Logger::Log("goHome called - returning to home screen");
// Stop any running third-party app
// Stop any running third-party app in the legacy sandbox manager
if (g_sandbox_manager) {
auto running_apps = g_sandbox_manager->GetRunningApps();
for (const auto& app_id : running_apps) {
@@ -280,6 +295,9 @@ static int LuaGoHome(lua_State* L)
}
}
// Switch sandbox context back to home screen
SwitchAppSandbox("com.mosis.home", g_data_root);
// Load home screen
const char* home_path = "apps/home/home.rml";
@@ -307,6 +325,113 @@ static int LuaGoHome(lua_State* L)
return 1;
}
// Register sandbox APIs (timer, json, crypto, fs) into RmlUi's Lua state
static void RegisterSandboxAPIs(lua_State* L)
{
Logger::Log("Registering sandbox APIs...");
// Register timer API
if (g_rmlui_timer_manager) {
mosis::RegisterTimerAPI(L, g_rmlui_timer_manager.get(), g_current_app_id);
Logger::Log(" Timer API registered");
}
// Register JSON API
mosis::RegisterJsonAPI(L, mosis::JsonLimits{});
Logger::Log(" JSON API registered");
// Register crypto API
mosis::RegisterCryptoAPI(L);
Logger::Log(" Crypto API registered");
// Register virtual filesystem API
if (g_rmlui_vfs) {
mosis::RegisterVirtualFS(L, g_rmlui_vfs.get());
Logger::Log(" VirtualFS API registered");
}
Logger::Log("All sandbox APIs registered");
}
// Unregister sandbox APIs (for clean switch between apps)
static void UnregisterSandboxAPIs(lua_State* L)
{
// Clear timer globals
lua_pushnil(L);
lua_setglobal(L, "setTimeout");
lua_pushnil(L);
lua_setglobal(L, "clearTimeout");
lua_pushnil(L);
lua_setglobal(L, "setInterval");
lua_pushnil(L);
lua_setglobal(L, "clearInterval");
// Clear json global
lua_pushnil(L);
lua_setglobal(L, "json");
// Clear crypto global
lua_pushnil(L);
lua_setglobal(L, "crypto");
// Clear fs global
lua_pushnil(L);
lua_setglobal(L, "fs");
Logger::Log("Sandbox APIs unregistered");
}
// Switch sandbox context to a different app
static void SwitchAppSandbox(const std::string& app_id, const std::string& app_data_path)
{
lua_State* L = Rml::Lua::Interpreter::GetLuaState();
// Clear timers for current app
if (g_rmlui_timer_manager) {
g_rmlui_timer_manager->ClearAppTimers(g_current_app_id);
Logger::Log(std::format("Timers cleared for {}", g_current_app_id));
}
// Unregister current APIs
UnregisterSandboxAPIs(L);
// Update current app ID
g_current_app_id = app_id;
// Create new VirtualFS for the app
std::string vfs_path = app_data_path.empty() ?
(g_data_root + "/sandbox_data") :
(app_data_path + "/sandbox_data");
std::filesystem::create_directories(vfs_path);
g_rmlui_vfs = std::make_unique<mosis::VirtualFS>(
app_id,
vfs_path,
mosis::VirtualFSLimits{}
);
Logger::Log(std::format("VirtualFS created for {} at {}", app_id, vfs_path));
// Register APIs for new app
RegisterSandboxAPIs(L);
}
// Lua function to switch sandbox context for a third-party app
// Called before loading an app's UI: switchAppSandbox(app_id, install_path)
static int LuaSwitchAppSandbox(lua_State* L)
{
const char* app_id = luaL_checkstring(L, 1);
const char* install_path = luaL_checkstring(L, 2);
Logger::Log(std::format("Switching sandbox to app: {} at {}", app_id, install_path));
SwitchAppSandbox(app_id, install_path);
lua_pushboolean(L, true);
return 1;
}
// Register Lua functions for navigation
static void RegisterLuaFunctions(const std::string& current_app_id, bool is_system_app)
{
@@ -315,13 +440,18 @@ static void RegisterLuaFunctions(const std::string& current_app_id, bool is_syst
lua_setglobal(L, "loadScreen");
lua_pushcfunction(L, LuaGoHome);
lua_setglobal(L, "goHome");
Logger::Log("Registered Lua loadScreen and goHome functions");
lua_pushcfunction(L, LuaSwitchAppSandbox);
lua_setglobal(L, "switchAppSandbox");
Logger::Log("Registered Lua loadScreen, goHome, and switchAppSandbox functions");
// Register app management APIs
if (g_app_manager && g_update_service) {
mosis::RegisterAppAPIs(L, g_app_manager, g_update_service, current_app_id, is_system_app);
Logger::Log("Registered Lua app management APIs");
}
// Initialize and register sandbox APIs
RegisterSandboxAPIs(L);
}
void Kernel::main_loop()
@@ -362,6 +492,7 @@ void Kernel::main_loop()
// Initialize app management system
// TODO: Get data root from Android context (for now use a placeholder)
std::string data_root = "/data/data/com.omixlab.mosis/files";
g_data_root = data_root; // Store for sandbox switching
m_app_manager = std::make_unique<mosis::AppManager>(data_root);
m_update_service = std::make_unique<mosis::UpdateService>(
m_app_manager.get(), "https://portal.mosis.dev/api/v1");
@@ -372,6 +503,14 @@ void Kernel::main_loop()
g_sandbox_manager = m_sandbox_manager.get();
Logger::Log("App management system initialized");
// Initialize RmlUi sandbox state (timer manager and VFS for home screen)
g_rmlui_timer_manager = std::make_unique<mosis::TimerManager>();
std::string home_sandbox_path = data_root + "/sandbox_data";
std::filesystem::create_directories(home_sandbox_path);
g_rmlui_vfs = std::make_unique<mosis::VirtualFS>(
"com.mosis.home", home_sandbox_path, mosis::VirtualFSLimits{});
Logger::Log("RmlUi sandbox initialized");
// Start background update checks (every 24 hours)
m_update_service->Start(std::chrono::hours(24));
@@ -415,11 +554,16 @@ void Kernel::main_loop()
m_tasks.clear();
}
// Update sandbox timers for running apps
// Update sandbox timers for running apps (legacy sandbox manager)
if (m_sandbox_manager) {
m_sandbox_manager->UpdateTimers();
}
// Update RmlUi sandbox timers (for UI-based apps)
if (g_rmlui_timer_manager) {
g_rmlui_timer_manager->ProcessTimers();
}
m_render_target->bind();
glClearColor(0.f, 0.f, 0.f, 1.f);
glClear(GL_COLOR_BUFFER_BIT);