implement third-party app launching from home screen
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,51 +50,137 @@ 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
|
||||
{
|
||||
AAssetManager* am = AssetsManager::Native();
|
||||
AAsset* asset = AAssetManager_open(am, path.c_str(), AASSET_MODE_BUFFER);
|
||||
return reinterpret_cast<Rml::FileHandle>(asset);
|
||||
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);
|
||||
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);
|
||||
return new_cursor != -1;
|
||||
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
|
||||
{
|
||||
AAssetManager* am = AssetsManager::Native();
|
||||
AAsset* asset = AAssetManager_open(am, path.c_str(), AASSET_MODE_BUFFER);
|
||||
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());
|
||||
return true;
|
||||
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;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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
|
||||
AAssetManager* am = AssetsManager::Native();
|
||||
AAsset* asset = AAssetManager_open(am, path, AASSET_MODE_BUFFER);
|
||||
if (!asset)
|
||||
// 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) {
|
||||
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");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user