implement third-party app launching from home screen

This commit is contained in:
2026-01-19 13:50:05 +01:00
parent ad28cf2360
commit d6b7504408
5 changed files with 214 additions and 33 deletions

View File

@@ -132,6 +132,16 @@ function renderThirdPartyApps()
print("[Home] Rendered " .. #installed_apps .. " third-party apps")
end
-- Get app info by package_id
function getAppInfo(package_id)
for _, app in ipairs(installed_apps) do
if app.package_id == package_id then
return app
end
end
return nil
end
-- Launch a third-party app
function launchThirdPartyApp(package_id)
print("[Home] Launching app: " .. package_id)
@@ -139,7 +149,22 @@ function launchThirdPartyApp(package_id)
if mosis and mosis.apps then
local success = mosis.apps.launch(package_id)
if success then
print("[Home] App launched: " .. package_id)
print("[Home] App sandbox started: " .. package_id)
-- Now load the app's UI document
local app_info = getAppInfo(package_id)
if app_info and app_info.install_path and app_info.entry_point then
local entry_path = app_info.install_path .. "/" .. app_info.entry_point
print("[Home] Loading app screen: " .. entry_path)
local loaded = loadScreen(entry_path)
if loaded then
print("[Home] App UI loaded: " .. package_id)
else
print("[Home] Failed to load app UI: " .. entry_path)
end
else
print("[Home] App info missing entry point: " .. package_id)
end
else
print("[Home] Failed to launch app: " .. package_id)
end

View File

@@ -95,6 +95,12 @@ static int apps_getInstalled(lua_State* L) {
lua_pushstring(L, app.developer_name.c_str());
lua_setfield(L, -2, "developer");
lua_pushstring(L, app.install_path.c_str());
lua_setfield(L, -2, "install_path");
lua_pushstring(L, app.entry_point.c_str());
lua_setfield(L, -2, "entry_point");
// installed_at as Unix timestamp
auto ts = std::chrono::duration_cast<std::chrono::seconds>(
app.installed_at.time_since_epoch()).count();

View File

@@ -330,7 +330,23 @@ bool AppManager::LaunchApp(const std::string& package_id) {
return true;
}
std::string app_path = app->install_path + "/package";
// Determine app path - check for package/ subfolder or direct structure
std::string app_path;
std::string package_subdir = app->install_path + "/package";
std::string entry_in_package = package_subdir + "/" + app->entry_point;
std::string entry_direct = app->install_path + "/" + app->entry_point;
if (fs::exists(entry_in_package)) {
// Installed package structure: files in package/ subfolder
app_path = package_subdir;
} else if (fs::exists(entry_direct)) {
// Direct structure: files in install_path directly
app_path = app->install_path;
} else {
LOG_ERROR("Cannot launch app: entry point not found: %s", app->entry_point.c_str());
return false;
}
LOG_INFO("Launching app: %s from %s", package_id.c_str(), app_path.c_str());
return m_sandbox_manager->StartApp(package_id, app_path, app->permissions, app->is_system_app);

View File

@@ -6,6 +6,7 @@
#include "apps/app_manager.h"
#include "apps/update_service.h"
#include "apps/app_api.h"
#include "sandbox/sandbox_manager.h"
#include "aidl/com/omixlab/mosis/IMosisListener.h"
#include <android/hardware_buffer.h>
#include <android/asset_manager.h>
@@ -17,16 +18,30 @@
#include <ranges>
#include <chrono>
#include <format>
#include <fstream>
#include <filesystem>
// Global state for Lua access
static Rml::Context* g_context = nullptr;
static Rml::ElementDocument* g_document = nullptr;
static mosis::AppManager* g_app_manager = nullptr;
static mosis::UpdateService* g_update_service = nullptr;
static mosis::LuaSandboxManager* g_sandbox_manager = nullptr;
using namespace aidl::com::omixlab::mosis;
using namespace aidl::android::hardware;
// File handle wrapper to support both assets and filesystem files
struct FileHandleWrapper {
enum class Type { Asset, File };
Type type;
union {
AAsset* asset;
std::ifstream* file;
};
size_t file_size = 0; // For filesystem files
};
class AssetFilesInterface : public Rml::FileInterface
{
public:
@@ -35,52 +50,138 @@ public:
static AssetFilesInterface instance;
return instance;
}
// Check if path is a filesystem path (starts with /)
static bool IsFilesystemPath(const Rml::String& path) {
return !path.empty() && path[0] == '/';
}
Rml::FileHandle Open(const Rml::String &path) override
{
auto* wrapper = new FileHandleWrapper();
if (IsFilesystemPath(path)) {
// Filesystem path
auto* file = new std::ifstream(path, std::ios::binary);
if (!file->is_open()) {
delete file;
delete wrapper;
return 0;
}
file->seekg(0, std::ios::end);
wrapper->file_size = file->tellg();
file->seekg(0, std::ios::beg);
wrapper->type = FileHandleWrapper::Type::File;
wrapper->file = file;
} else {
// Asset path
AAssetManager* am = AssetsManager::Native();
AAsset* asset = AAssetManager_open(am, path.c_str(), AASSET_MODE_BUFFER);
return reinterpret_cast<Rml::FileHandle>(asset);
if (!asset) {
delete wrapper;
return 0;
}
wrapper->type = FileHandleWrapper::Type::Asset;
wrapper->asset = asset;
}
return reinterpret_cast<Rml::FileHandle>(wrapper);
}
void Close(Rml::FileHandle file) override
{
AAsset* asset = reinterpret_cast<AAsset*>(file);
AAsset_close(asset);
auto* wrapper = reinterpret_cast<FileHandleWrapper*>(file);
if (!wrapper) return;
if (wrapper->type == FileHandleWrapper::Type::Asset) {
AAsset_close(wrapper->asset);
} else {
wrapper->file->close();
delete wrapper->file;
}
delete wrapper;
}
size_t Read(void *buffer, size_t size, Rml::FileHandle file) override
{
AAsset* asset = reinterpret_cast<AAsset*>(file);
return AAsset_read(asset, buffer, size);
auto* wrapper = reinterpret_cast<FileHandleWrapper*>(file);
if (!wrapper) return 0;
if (wrapper->type == FileHandleWrapper::Type::Asset) {
return AAsset_read(wrapper->asset, buffer, size);
} else {
wrapper->file->read(static_cast<char*>(buffer), size);
return wrapper->file->gcount();
}
}
bool Seek(Rml::FileHandle file, long offset, int origin) override
{
AAsset* asset = reinterpret_cast<AAsset*>(file);
off_t new_cursor = AAsset_seek(asset, offset, origin);
auto* wrapper = reinterpret_cast<FileHandleWrapper*>(file);
if (!wrapper) return false;
if (wrapper->type == FileHandleWrapper::Type::Asset) {
off_t new_cursor = AAsset_seek(wrapper->asset, offset, origin);
return new_cursor != -1;
} else {
std::ios_base::seekdir dir = std::ios::beg;
if (origin == SEEK_CUR) dir = std::ios::cur;
else if (origin == SEEK_END) dir = std::ios::end;
wrapper->file->seekg(offset, dir);
return !wrapper->file->fail();
}
}
size_t Tell(Rml::FileHandle file) override
{
AAsset* asset = reinterpret_cast<AAsset*>(file);
return AAsset_seek(asset, 0, SEEK_CUR);
auto* wrapper = reinterpret_cast<FileHandleWrapper*>(file);
if (!wrapper) return 0;
if (wrapper->type == FileHandleWrapper::Type::Asset) {
return AAsset_seek(wrapper->asset, 0, SEEK_CUR);
} else {
return wrapper->file->tellg();
}
}
size_t Length(Rml::FileHandle file) override
{
AAsset* asset = reinterpret_cast<AAsset*>(file);
return AAsset_getLength(asset);
auto* wrapper = reinterpret_cast<FileHandleWrapper*>(file);
if (!wrapper) return 0;
if (wrapper->type == FileHandleWrapper::Type::Asset) {
return AAsset_getLength(wrapper->asset);
} else {
return wrapper->file_size;
}
}
bool LoadFile(const Rml::String &path, Rml::String &out_data) override
{
if (IsFilesystemPath(path)) {
// Load from filesystem
std::ifstream file(path, std::ios::binary | std::ios::ate);
if (!file.is_open()) return false;
size_t size = file.tellg();
file.seekg(0, std::ios::beg);
out_data.resize(size);
file.read(out_data.data(), size);
return true;
} else {
// Load from assets
AAssetManager* am = AssetsManager::Native();
AAsset* asset = AAssetManager_open(am, path.c_str(), AASSET_MODE_BUFFER);
if (!asset)
return false;
if (!asset) return false;
out_data.resize(AAsset_getLength(asset));
auto data_ptr = static_cast<const char*>(AAsset_getBuffer(asset));
std::span data = std::span(data_ptr, out_data.size());
std::ranges::copy(data, out_data.begin());
AAsset_close(asset);
return true;
}
}
};
class SystemInterface : public Rml::SystemInterface
@@ -110,16 +211,27 @@ static int LuaLoadScreen(lua_State* L)
const char* path = luaL_checkstring(L, 1);
Logger::Log(std::format("Loading screen: {}", path));
// Check if asset exists
// Check if file exists - support both asset and filesystem paths
bool exists = false;
if (AssetFilesInterface::IsFilesystemPath(path)) {
// Filesystem path
exists = std::filesystem::exists(path);
} else {
// Asset path
AAssetManager* am = AssetsManager::Native();
AAsset* asset = AAssetManager_open(am, path, AASSET_MODE_BUFFER);
if (!asset)
if (asset) {
exists = true;
AAsset_close(asset);
}
}
if (!exists)
{
Logger::Log(std::format("Screen not found: {}", path));
lua_pushboolean(L, false);
return 1;
}
AAsset_close(asset);
// Unload current document
if (g_document)
@@ -201,8 +313,11 @@ void Kernel::main_loop()
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");
m_sandbox_manager = std::make_unique<mosis::LuaSandboxManager>(data_root);
m_app_manager->SetSandboxManager(m_sandbox_manager.get());
g_app_manager = m_app_manager.get();
g_update_service = m_update_service.get();
g_sandbox_manager = m_sandbox_manager.get();
Logger::Log("App management system initialized");
// Start background update checks (every 24 hours)
@@ -248,6 +363,11 @@ void Kernel::main_loop()
m_tasks.clear();
}
// Update sandbox timers for running apps
if (m_sandbox_manager) {
m_sandbox_manager->UpdateTimers();
}
m_render_target->bind();
glClearColor(0.f, 0.f, 0.f, 1.f);
glClear(GL_COLOR_BUFFER_BIT);
@@ -289,8 +409,21 @@ void Kernel::on_touch_down(float x, float y)
Logger::Log(std::format("on_touch_down {} - {}", x, y));
std::lock_guard _lock(m_mutex);
m_tasks.emplace_back([x, y](Rml::Context* context){
context->ProcessMouseMove(static_cast<int>(x * 540), static_cast<int>(y * 960), 0);
context->ProcessMouseButtonDown(0, 0);
int px = static_cast<int>(x * 540);
int py = static_cast<int>(y * 960);
bool move_handled = context->ProcessMouseMove(px, py, 0);
bool down_handled = context->ProcessMouseButtonDown(0, 0);
Logger::Log(std::format("Touch at ({}, {}): move={}, down={}", px, py, move_handled, down_handled));
// Debug: Check if there's a hover element
auto* hover_elem = context->GetHoverElement();
if (hover_elem) {
Logger::Log(std::format(" Hover element: {} (id={})",
hover_elem->GetTagName().c_str(),
hover_elem->GetId().c_str()));
} else {
Logger::Log(" No hover element at this position");
}
});
}

View File

@@ -11,7 +11,7 @@ namespace egl { class Context; }
namespace aidl::com::omixlab::mosis { class IMosisListener; }
namespace aidl::android::hardware { struct HardwareBuffer; }
namespace Rml { class Context; }
namespace mosis { class AppManager; class UpdateService; }
namespace mosis { class AppManager; class UpdateService; class LuaSandboxManager; }
class Kernel
{
@@ -21,6 +21,7 @@ class Kernel
std::unique_ptr<aidl::android::hardware::HardwareBuffer> m_aidl_buffer;
std::unique_ptr<mosis::AppManager> m_app_manager;
std::unique_ptr<mosis::UpdateService> m_update_service;
std::unique_ptr<mosis::LuaSandboxManager> m_sandbox_manager;
std::vector<std::function<void(Rml::Context*)>> m_tasks;
std::mutex m_mutex;
std::thread m_main_loop_thread;