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")
|
print("[Home] Rendered " .. #installed_apps .. " third-party apps")
|
||||||
end
|
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
|
-- Launch a third-party app
|
||||||
function launchThirdPartyApp(package_id)
|
function launchThirdPartyApp(package_id)
|
||||||
print("[Home] Launching app: " .. package_id)
|
print("[Home] Launching app: " .. package_id)
|
||||||
@@ -139,7 +149,22 @@ function launchThirdPartyApp(package_id)
|
|||||||
if mosis and mosis.apps then
|
if mosis and mosis.apps then
|
||||||
local success = mosis.apps.launch(package_id)
|
local success = mosis.apps.launch(package_id)
|
||||||
if success then
|
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
|
else
|
||||||
print("[Home] Failed to launch app: " .. package_id)
|
print("[Home] Failed to launch app: " .. package_id)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -95,6 +95,12 @@ static int apps_getInstalled(lua_State* L) {
|
|||||||
lua_pushstring(L, app.developer_name.c_str());
|
lua_pushstring(L, app.developer_name.c_str());
|
||||||
lua_setfield(L, -2, "developer");
|
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
|
// installed_at as Unix timestamp
|
||||||
auto ts = std::chrono::duration_cast<std::chrono::seconds>(
|
auto ts = std::chrono::duration_cast<std::chrono::seconds>(
|
||||||
app.installed_at.time_since_epoch()).count();
|
app.installed_at.time_since_epoch()).count();
|
||||||
|
|||||||
@@ -330,7 +330,23 @@ bool AppManager::LaunchApp(const std::string& package_id) {
|
|||||||
return true;
|
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());
|
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);
|
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/app_manager.h"
|
||||||
#include "apps/update_service.h"
|
#include "apps/update_service.h"
|
||||||
#include "apps/app_api.h"
|
#include "apps/app_api.h"
|
||||||
|
#include "sandbox/sandbox_manager.h"
|
||||||
#include "aidl/com/omixlab/mosis/IMosisListener.h"
|
#include "aidl/com/omixlab/mosis/IMosisListener.h"
|
||||||
#include <android/hardware_buffer.h>
|
#include <android/hardware_buffer.h>
|
||||||
#include <android/asset_manager.h>
|
#include <android/asset_manager.h>
|
||||||
@@ -17,16 +18,30 @@
|
|||||||
#include <ranges>
|
#include <ranges>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <format>
|
#include <format>
|
||||||
|
#include <fstream>
|
||||||
|
#include <filesystem>
|
||||||
|
|
||||||
// Global state for Lua access
|
// Global state for Lua access
|
||||||
static Rml::Context* g_context = nullptr;
|
static Rml::Context* g_context = nullptr;
|
||||||
static Rml::ElementDocument* g_document = nullptr;
|
static Rml::ElementDocument* g_document = nullptr;
|
||||||
static mosis::AppManager* g_app_manager = nullptr;
|
static mosis::AppManager* g_app_manager = nullptr;
|
||||||
static mosis::UpdateService* g_update_service = nullptr;
|
static mosis::UpdateService* g_update_service = nullptr;
|
||||||
|
static mosis::LuaSandboxManager* g_sandbox_manager = nullptr;
|
||||||
|
|
||||||
using namespace aidl::com::omixlab::mosis;
|
using namespace aidl::com::omixlab::mosis;
|
||||||
using namespace aidl::android::hardware;
|
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
|
class AssetFilesInterface : public Rml::FileInterface
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -35,51 +50,137 @@ public:
|
|||||||
static AssetFilesInterface instance;
|
static AssetFilesInterface instance;
|
||||||
return 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
|
Rml::FileHandle Open(const Rml::String &path) override
|
||||||
{
|
{
|
||||||
AAssetManager* am = AssetsManager::Native();
|
auto* wrapper = new FileHandleWrapper();
|
||||||
AAsset* asset = AAssetManager_open(am, path.c_str(), AASSET_MODE_BUFFER);
|
|
||||||
return reinterpret_cast<Rml::FileHandle>(asset);
|
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
|
void Close(Rml::FileHandle file) override
|
||||||
{
|
{
|
||||||
AAsset* asset = reinterpret_cast<AAsset*>(file);
|
auto* wrapper = reinterpret_cast<FileHandleWrapper*>(file);
|
||||||
AAsset_close(asset);
|
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
|
size_t Read(void *buffer, size_t size, Rml::FileHandle file) override
|
||||||
{
|
{
|
||||||
AAsset* asset = reinterpret_cast<AAsset*>(file);
|
auto* wrapper = reinterpret_cast<FileHandleWrapper*>(file);
|
||||||
return AAsset_read(asset, buffer, size);
|
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
|
bool Seek(Rml::FileHandle file, long offset, int origin) override
|
||||||
{
|
{
|
||||||
AAsset* asset = reinterpret_cast<AAsset*>(file);
|
auto* wrapper = reinterpret_cast<FileHandleWrapper*>(file);
|
||||||
off_t new_cursor = AAsset_seek(asset, offset, origin);
|
if (!wrapper) return false;
|
||||||
return new_cursor != -1;
|
|
||||||
|
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
|
size_t Tell(Rml::FileHandle file) override
|
||||||
{
|
{
|
||||||
AAsset* asset = reinterpret_cast<AAsset*>(file);
|
auto* wrapper = reinterpret_cast<FileHandleWrapper*>(file);
|
||||||
return AAsset_seek(asset, 0, SEEK_CUR);
|
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
|
size_t Length(Rml::FileHandle file) override
|
||||||
{
|
{
|
||||||
AAsset* asset = reinterpret_cast<AAsset*>(file);
|
auto* wrapper = reinterpret_cast<FileHandleWrapper*>(file);
|
||||||
return AAsset_getLength(asset);
|
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
|
bool LoadFile(const Rml::String &path, Rml::String &out_data) override
|
||||||
{
|
{
|
||||||
AAssetManager* am = AssetsManager::Native();
|
if (IsFilesystemPath(path)) {
|
||||||
AAsset* asset = AAssetManager_open(am, path.c_str(), AASSET_MODE_BUFFER);
|
// Load from filesystem
|
||||||
if (!asset)
|
std::ifstream file(path, std::ios::binary | std::ios::ate);
|
||||||
return false;
|
if (!file.is_open()) return false;
|
||||||
out_data.resize(AAsset_getLength(asset));
|
|
||||||
auto data_ptr = static_cast<const char*>(AAsset_getBuffer(asset));
|
size_t size = file.tellg();
|
||||||
std::span data = std::span(data_ptr, out_data.size());
|
file.seekg(0, std::ios::beg);
|
||||||
std::ranges::copy(data, out_data.begin());
|
out_data.resize(size);
|
||||||
return true;
|
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);
|
const char* path = luaL_checkstring(L, 1);
|
||||||
Logger::Log(std::format("Loading screen: {}", path));
|
Logger::Log(std::format("Loading screen: {}", path));
|
||||||
|
|
||||||
// Check if asset exists
|
// Check if file exists - support both asset and filesystem paths
|
||||||
AAssetManager* am = AssetsManager::Native();
|
bool exists = false;
|
||||||
AAsset* asset = AAssetManager_open(am, path, AASSET_MODE_BUFFER);
|
if (AssetFilesInterface::IsFilesystemPath(path)) {
|
||||||
if (!asset)
|
// 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));
|
Logger::Log(std::format("Screen not found: {}", path));
|
||||||
lua_pushboolean(L, false);
|
lua_pushboolean(L, false);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
AAsset_close(asset);
|
|
||||||
|
|
||||||
// Unload current document
|
// Unload current document
|
||||||
if (g_document)
|
if (g_document)
|
||||||
@@ -201,8 +313,11 @@ void Kernel::main_loop()
|
|||||||
m_app_manager = std::make_unique<mosis::AppManager>(data_root);
|
m_app_manager = std::make_unique<mosis::AppManager>(data_root);
|
||||||
m_update_service = std::make_unique<mosis::UpdateService>(
|
m_update_service = std::make_unique<mosis::UpdateService>(
|
||||||
m_app_manager.get(), "https://portal.mosis.dev/api/v1");
|
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_app_manager = m_app_manager.get();
|
||||||
g_update_service = m_update_service.get();
|
g_update_service = m_update_service.get();
|
||||||
|
g_sandbox_manager = m_sandbox_manager.get();
|
||||||
Logger::Log("App management system initialized");
|
Logger::Log("App management system initialized");
|
||||||
|
|
||||||
// Start background update checks (every 24 hours)
|
// Start background update checks (every 24 hours)
|
||||||
@@ -248,6 +363,11 @@ void Kernel::main_loop()
|
|||||||
m_tasks.clear();
|
m_tasks.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update sandbox timers for running apps
|
||||||
|
if (m_sandbox_manager) {
|
||||||
|
m_sandbox_manager->UpdateTimers();
|
||||||
|
}
|
||||||
|
|
||||||
m_render_target->bind();
|
m_render_target->bind();
|
||||||
glClearColor(0.f, 0.f, 0.f, 1.f);
|
glClearColor(0.f, 0.f, 0.f, 1.f);
|
||||||
glClear(GL_COLOR_BUFFER_BIT);
|
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));
|
Logger::Log(std::format("on_touch_down {} - {}", x, y));
|
||||||
std::lock_guard _lock(m_mutex);
|
std::lock_guard _lock(m_mutex);
|
||||||
m_tasks.emplace_back([x, y](Rml::Context* context){
|
m_tasks.emplace_back([x, y](Rml::Context* context){
|
||||||
context->ProcessMouseMove(static_cast<int>(x * 540), static_cast<int>(y * 960), 0);
|
int px = static_cast<int>(x * 540);
|
||||||
context->ProcessMouseButtonDown(0, 0);
|
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::com::omixlab::mosis { class IMosisListener; }
|
||||||
namespace aidl::android::hardware { struct HardwareBuffer; }
|
namespace aidl::android::hardware { struct HardwareBuffer; }
|
||||||
namespace Rml { class Context; }
|
namespace Rml { class Context; }
|
||||||
namespace mosis { class AppManager; class UpdateService; }
|
namespace mosis { class AppManager; class UpdateService; class LuaSandboxManager; }
|
||||||
|
|
||||||
class Kernel
|
class Kernel
|
||||||
{
|
{
|
||||||
@@ -21,6 +21,7 @@ class Kernel
|
|||||||
std::unique_ptr<aidl::android::hardware::HardwareBuffer> m_aidl_buffer;
|
std::unique_ptr<aidl::android::hardware::HardwareBuffer> m_aidl_buffer;
|
||||||
std::unique_ptr<mosis::AppManager> m_app_manager;
|
std::unique_ptr<mosis::AppManager> m_app_manager;
|
||||||
std::unique_ptr<mosis::UpdateService> m_update_service;
|
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::vector<std::function<void(Rml::Context*)>> m_tasks;
|
||||||
std::mutex m_mutex;
|
std::mutex m_mutex;
|
||||||
std::thread m_main_loop_thread;
|
std::thread m_main_loop_thread;
|
||||||
|
|||||||
Reference in New Issue
Block a user