From 010e11cf6b79dd7d45f788a576731cf7558d952f Mon Sep 17 00:00:00 2001 From: omigamedev Date: Mon, 19 Jan 2026 09:02:11 +0100 Subject: [PATCH] move docs to docs/ folder, merge architecture files, update references --- .claude/settings.local.json | 75 + ARCHITECTURE.md | 96 -- CLAUDE.md | 1254 ----------------- AGENTS.md => docs/AGENTS.md | 0 docs/ANDROID-TESTING.md | 129 ++ docs/APP-MANAGEMENT.md | 99 ++ APP_SPECS.md => docs/APP_SPECS.md | 0 docs/ARCHITECTURE.md | 105 ++ docs/BUILD-COMMANDS.md | 86 ++ BUILD.md => docs/BUILD.md | 0 docs/CLAUDE.md | 106 ++ docs/DESKTOP-DESIGNER.md | 40 + docs/DEVELOPER-PORTAL.md | 56 + .../DEV_PORTAL_M01_APP_PACKAGE.md | 0 .../DEV_PORTAL_M02_WEB_STACK.md | 0 .../DEV_PORTAL_M03_DATABASE.md | 0 .../DEV_PORTAL_M04_AUTH.md | 0 .../DEV_PORTAL_M05_FRONTEND.md | 0 .../DEV_PORTAL_M06_API.md | 0 .../DEV_PORTAL_M07_STORAGE.md | 0 .../DEV_PORTAL_M08_TELEMETRY.md | 0 .../DEV_PORTAL_M09_REVIEW.md | 0 .../DEV_PORTAL_M10_DEVICE.md | 0 .../DEV_PORTAL_M11_CLI.md | 0 .../DEV_PORTAL_M12_DOCS.md | 0 .../DEV_PORTAL_MILESTONES.md | 0 docs/GAME-ENGINES.md | 348 +++++ docs/LUA-SANDBOX.md | 126 ++ docs/MATERIAL-DESIGN.md | 79 ++ MILESTONE-2.md => docs/MILESTONE-2.md | 0 MILESTONE-3.md => docs/MILESTONE-3.md | 0 MILESTONE-4.md => docs/MILESTONE-4.md | 0 MILESTONE-5.md => docs/MILESTONE-5.md | 0 MILESTONE-6.md => docs/MILESTONE-6.md | 0 MILESTONE-7.md => docs/MILESTONE-7.md | 0 PLAN.md => docs/PLAN.md | 0 ROADMAP.md => docs/ROADMAP.md | 0 SANDBOX.md => docs/SANDBOX.md | 0 .../SANDBOX_MILESTONES.md | 0 .../SANDBOX_MILESTONE_1.md | 0 .../SANDBOX_MILESTONE_10.md | 0 .../SANDBOX_MILESTONE_11.md | 0 .../SANDBOX_MILESTONE_12.md | 0 .../SANDBOX_MILESTONE_13.md | 0 .../SANDBOX_MILESTONE_14.md | 0 .../SANDBOX_MILESTONE_15.md | 0 .../SANDBOX_MILESTONE_16.md | 0 .../SANDBOX_MILESTONE_17.md | 0 .../SANDBOX_MILESTONE_18.md | 0 .../SANDBOX_MILESTONE_19.md | 0 .../SANDBOX_MILESTONE_2.md | 0 .../SANDBOX_MILESTONE_20.md | 0 .../SANDBOX_MILESTONE_3.md | 0 .../SANDBOX_MILESTONE_4.md | 0 .../SANDBOX_MILESTONE_5.md | 0 .../SANDBOX_MILESTONE_6.md | 0 .../SANDBOX_MILESTONE_7.md | 0 .../SANDBOX_MILESTONE_8.md | 0 .../SANDBOX_MILESTONE_9.md | 0 docs/TESTING-FRAMEWORK.md | 60 + TESTING.md => docs/TESTING.md | 0 docs/UI-ASSETS.md | 67 + test-apps/com.mosis.sandbox-test.mosis | Bin 0 -> 20480 bytes test-apps/com.mosis.sandbox-test/app.lua | 195 +++ test-apps/com.mosis.sandbox-test/main.rml | 46 + .../com.mosis.sandbox-test/manifest.json | 18 + test-apps/com.mosis.sandbox-test/styles.rcss | 85 ++ test-apps/package.bat | 21 + 68 files changed, 1741 insertions(+), 1350 deletions(-) create mode 100644 .claude/settings.local.json delete mode 100644 ARCHITECTURE.md delete mode 100644 CLAUDE.md rename AGENTS.md => docs/AGENTS.md (100%) create mode 100644 docs/ANDROID-TESTING.md create mode 100644 docs/APP-MANAGEMENT.md rename APP_SPECS.md => docs/APP_SPECS.md (100%) create mode 100644 docs/ARCHITECTURE.md create mode 100644 docs/BUILD-COMMANDS.md rename BUILD.md => docs/BUILD.md (100%) create mode 100644 docs/CLAUDE.md create mode 100644 docs/DESKTOP-DESIGNER.md create mode 100644 docs/DEVELOPER-PORTAL.md rename DEV_PORTAL_M01_APP_PACKAGE.md => docs/DEV_PORTAL_M01_APP_PACKAGE.md (100%) rename DEV_PORTAL_M02_WEB_STACK.md => docs/DEV_PORTAL_M02_WEB_STACK.md (100%) rename DEV_PORTAL_M03_DATABASE.md => docs/DEV_PORTAL_M03_DATABASE.md (100%) rename DEV_PORTAL_M04_AUTH.md => docs/DEV_PORTAL_M04_AUTH.md (100%) rename DEV_PORTAL_M05_FRONTEND.md => docs/DEV_PORTAL_M05_FRONTEND.md (100%) rename DEV_PORTAL_M06_API.md => docs/DEV_PORTAL_M06_API.md (100%) rename DEV_PORTAL_M07_STORAGE.md => docs/DEV_PORTAL_M07_STORAGE.md (100%) rename DEV_PORTAL_M08_TELEMETRY.md => docs/DEV_PORTAL_M08_TELEMETRY.md (100%) rename DEV_PORTAL_M09_REVIEW.md => docs/DEV_PORTAL_M09_REVIEW.md (100%) rename DEV_PORTAL_M10_DEVICE.md => docs/DEV_PORTAL_M10_DEVICE.md (100%) rename DEV_PORTAL_M11_CLI.md => docs/DEV_PORTAL_M11_CLI.md (100%) rename DEV_PORTAL_M12_DOCS.md => docs/DEV_PORTAL_M12_DOCS.md (100%) rename DEV_PORTAL_MILESTONES.md => docs/DEV_PORTAL_MILESTONES.md (100%) create mode 100644 docs/GAME-ENGINES.md create mode 100644 docs/LUA-SANDBOX.md create mode 100644 docs/MATERIAL-DESIGN.md rename MILESTONE-2.md => docs/MILESTONE-2.md (100%) rename MILESTONE-3.md => docs/MILESTONE-3.md (100%) rename MILESTONE-4.md => docs/MILESTONE-4.md (100%) rename MILESTONE-5.md => docs/MILESTONE-5.md (100%) rename MILESTONE-6.md => docs/MILESTONE-6.md (100%) rename MILESTONE-7.md => docs/MILESTONE-7.md (100%) rename PLAN.md => docs/PLAN.md (100%) rename ROADMAP.md => docs/ROADMAP.md (100%) rename SANDBOX.md => docs/SANDBOX.md (100%) rename SANDBOX_MILESTONES.md => docs/SANDBOX_MILESTONES.md (100%) rename SANDBOX_MILESTONE_1.md => docs/SANDBOX_MILESTONE_1.md (100%) rename SANDBOX_MILESTONE_10.md => docs/SANDBOX_MILESTONE_10.md (100%) rename SANDBOX_MILESTONE_11.md => docs/SANDBOX_MILESTONE_11.md (100%) rename SANDBOX_MILESTONE_12.md => docs/SANDBOX_MILESTONE_12.md (100%) rename SANDBOX_MILESTONE_13.md => docs/SANDBOX_MILESTONE_13.md (100%) rename SANDBOX_MILESTONE_14.md => docs/SANDBOX_MILESTONE_14.md (100%) rename SANDBOX_MILESTONE_15.md => docs/SANDBOX_MILESTONE_15.md (100%) rename SANDBOX_MILESTONE_16.md => docs/SANDBOX_MILESTONE_16.md (100%) rename SANDBOX_MILESTONE_17.md => docs/SANDBOX_MILESTONE_17.md (100%) rename SANDBOX_MILESTONE_18.md => docs/SANDBOX_MILESTONE_18.md (100%) rename SANDBOX_MILESTONE_19.md => docs/SANDBOX_MILESTONE_19.md (100%) rename SANDBOX_MILESTONE_2.md => docs/SANDBOX_MILESTONE_2.md (100%) rename SANDBOX_MILESTONE_20.md => docs/SANDBOX_MILESTONE_20.md (100%) rename SANDBOX_MILESTONE_3.md => docs/SANDBOX_MILESTONE_3.md (100%) rename SANDBOX_MILESTONE_4.md => docs/SANDBOX_MILESTONE_4.md (100%) rename SANDBOX_MILESTONE_5.md => docs/SANDBOX_MILESTONE_5.md (100%) rename SANDBOX_MILESTONE_6.md => docs/SANDBOX_MILESTONE_6.md (100%) rename SANDBOX_MILESTONE_7.md => docs/SANDBOX_MILESTONE_7.md (100%) rename SANDBOX_MILESTONE_8.md => docs/SANDBOX_MILESTONE_8.md (100%) rename SANDBOX_MILESTONE_9.md => docs/SANDBOX_MILESTONE_9.md (100%) create mode 100644 docs/TESTING-FRAMEWORK.md rename TESTING.md => docs/TESTING.md (100%) create mode 100644 docs/UI-ASSETS.md create mode 100644 test-apps/com.mosis.sandbox-test.mosis create mode 100644 test-apps/com.mosis.sandbox-test/app.lua create mode 100644 test-apps/com.mosis.sandbox-test/main.rml create mode 100644 test-apps/com.mosis.sandbox-test/manifest.json create mode 100644 test-apps/com.mosis.sandbox-test/styles.rcss create mode 100644 test-apps/package.bat diff --git a/.claude/settings.local.json b/.claude/settings.local.json new file mode 100644 index 0000000..bd81ca4 --- /dev/null +++ b/.claude/settings.local.json @@ -0,0 +1,75 @@ +{ + "permissions": { + "allow": [ + "Bash(./gradlew assembleDebug:*)", + "Bash(./gradlew installDebug:*)", + "Bash(adb logcat:*)", + "Bash(adb shell am start:*)", + "Bash(adb devices:*)", + "Bash(findstr:*)", + "Bash(tree:*)", + "Bash(cmake:*)", + "Bash(\".\\\\build\\\\Debug\\\\mosis-designer.exe\" \"..\\\\src\\\\main\\\\assets\\\\apps\\\\home\\\\home.rml\")", + "Bash(dir:*)", + "Bash(start \"\" \".\\\\build\\\\Debug\\\\mosis-designer.exe\" \"..\\\\src\\\\main\\\\assets\\\\apps\\\\home\\\\home.rml\")", + "Bash(\"C:\\\\Program Files\\\\AutoHotkey\\\\v2\\\\AutoHotkey64.exe\" \"D:\\\\Dev\\\\Mosis\\\\MosisService\\\\designer\\\\test\\\\diagnose.ahk\")", + "Bash(tasklist:*)", + "Bash(\"C:\\\\Program Files\\\\AutoHotkey\\\\v2\\\\AutoHotkey64.exe\" /ErrorStdOut diagnose.ahk)", + "Bash(taskkill:*)", + "Bash(\"C:\\\\Program Files\\\\AutoHotkey\\\\v2\\\\AutoHotkey64.exe\" full_click_test.ahk)", + "Bash(\"C:\\\\Program Files\\\\AutoHotkey\\\\v2\\\\AutoHotkey64.exe\" visual_click_test.ahk)", + "Bash(\".\\\\build\\\\Debug\\\\designer-test.exe\" --designer \"..\\\\designer\\\\build\\\\Debug\\\\mosis-designer.exe\" --document \"..\\\\src\\\\main\\\\assets\\\\apps\\\\home\\\\home.rml\")", + "Bash(./designer-test.exe)", + "Bash(\"D:\\\\Dev\\\\Mosis\\\\MosisService\\\\designer-test\\\\build\\\\Debug\\\\designer-test.exe\")", + "Bash(git add:*)", + "Bash(adb install:*)", + "Bash(adb shell am broadcast:*)", + "Bash(timeout 5 ./mosis-designer.exe:*)", + "Bash(.buildDebugmosis-designer.exe --dump D:DevMosisMosisServicesrcmainassetsappshomehome.rml)", + "Bash(cmd /c \"cd /d D:\\\\Dev\\\\Mosis\\\\MosisService\\\\designer && .\\\\build\\\\Debug\\\\mosis-designer.exe --dump ..\\\\src\\\\main\\\\assets\\\\apps\\\\home\\\\home.rml 2>&1\")", + "Bash(start /min cmd /c \".\\\\build\\\\Debug\\\\mosis-designer.exe ..\\\\src\\\\main\\\\assets\\\\apps\\\\home\\\\home.rml --hierarchy hierarchy.json\")", + "Bash(powershell -Command:*)", + "Bash(adb shell \"ps -A | grep mosis\")", + "Bash(adb shell:*)", + "Bash(ls:*)", + "Bash(magick:*)", + "Bash(fc:*)", + "Bash(cmp:*)", + "Bash(sort:*)", + "Bash(vcpkg search:*)", + "Bash(D:/vcpkg/vcpkg search glad)", + "Bash(timeout 8 ./mosis-designer.exe:*)", + "Bash(start mosis-designer.exe apps/home/home.rml)", + "Bash(./mosis-designer.exe:*)", + "Bash(cmd /c \"cd /d D:\\\\Dev\\\\Mosis\\\\MosisService\\\\designer && cmake --build build --config Debug\")", + "Bash(cmd /c:*)", + "Bash(\"D:/Dev/Mosis/MosisService/designer-test/build/Debug/designer-test.exe\")", + "Bash(python:*)", + "Bash(./gradlew connectedAndroidTest:*)", + "Bash(\"D:\\\\Epic\\\\UE_5.5\\\\Engine\\\\Build\\\\BatchFiles\\\\Build.bat\" MosisUnreal Android Development -Project=\"D:\\\\Dev\\\\Mosis\\\\MosisUnreal\\\\MosisUnreal.uproject\")", + "Bash(\"D:\\\\Epic\\\\UE_5.5\\\\Engine\\\\Build\\\\BatchFiles\\\\Build.bat\" MosisUnreal Win64 Development -Project=\"D:\\\\Dev\\\\Mosis\\\\MosisUnreal\\\\MosisUnreal.uproject\")", + "Bash(D:)", + "Bash(\"D:\\\\Epic\\\\UE_5.5\\\\Engine\\\\Build\\\\BatchFiles\\\\Build.bat\" MosisUnrealEditor Win64 Development -Project=\"D:\\\\Dev\\\\Mosis\\\\MosisUnreal\\\\MosisUnreal.uproject\")", + "Bash(\"D:\\\\Epic\\\\UE_5.5\\\\Engine\\\\Build\\\\BatchFiles\\\\RunUAT.bat\" BuildCookRun -project=\"D:\\\\Dev\\\\Mosis\\\\MosisUnreal\\\\MosisUnreal.uproject\" -platform=Android -clientconfig=Development -build -noP4 -utf8output)", + "Bash(\"D:\\\\Epic\\\\UE_5.5\\\\Engine\\\\Build\\\\BatchFiles\\\\RunUAT.bat\" BuildCookRun -project=\"D:\\\\Dev\\\\Mosis\\\\MosisUnreal\\\\MosisUnreal.uproject\" -platform=Android -clientconfig=Development -build -cook -stage -pak -package -noP4 -utf8output)", + "Bash(./gradlew.bat:*)", + "Bash(gradle assembleRelease:*)", + "Bash(\"C:\\\\Program Files\\\\Unity\\\\Hub\\\\Editor\\\\6000.3.2f1\\\\Editor\\\\Unity.exe\" -batchmode -quit -nographics -projectPath \"D:\\\\Dev\\\\Mosis\\\\MosisVR\" -executeMethod BuildScript.BuildAndroidCI -outputPath \"D:\\\\Dev\\\\Mosis\\\\Builds\\\\Unity\\\\Android\\\\MosisVR.apk\" -logFile \"D:\\\\Dev\\\\Mosis\\\\Builds\\\\Unity\\\\build3.log\")", + "Bash(gradlew.bat assembleRelease)", + "Bash(\"C:\\\\Program Files\\\\Unity\\\\Hub\\\\Editor\\\\6000.3.2f1\\\\Editor\\\\Unity.exe\" -batchmode -quit -nographics -projectPath \"D:\\\\Dev\\\\Mosis\\\\MosisVR\" -executeMethod BuildScript.BuildAndroidDirectCI -outputPath \"D:\\\\Dev\\\\Mosis\\\\Builds\\\\Unity\\\\Android\\\\MosisVR-direct.apk\" -logFile \"D:\\\\Dev\\\\Mosis\\\\Builds\\\\Unity\\\\build-direct.log\")", + "Bash(gradlew assembleDebug:*)", + "Bash(\"D:\\\\Epic\\\\UE_5.5\\\\Engine\\\\Build\\\\BatchFiles\\\\RunUAT.bat\" BuildCookRun -project=\"D:\\\\Dev\\\\Mosis\\\\MosisUnreal\\\\MosisUnreal.uproject\" -platform=Android -clientconfig=Development -build -cook -stage -pak -package -archive -archivedirectory=\"D:\\\\Dev\\\\Mosis\\\\Builds\\\\Unreal\" -noP4)", + "Bash(adb:*)", + "Bash(\"D:/Epic/UE_5.5/Engine/Build/BatchFiles/RunUAT.bat\" BuildCookRun -project=\"D:/Dev/Mosis/MosisUnreal/MosisUnreal.uproject\" -platform=Android -clientconfig=Development -build -cook -stage -pak -package -noP4)", + "Bash(MSYS_NO_PATHCONV=1 adb:*)", + "Bash(\"D:/Epic/UE_5.5/Engine/Build/BatchFiles/Build.bat\" MosisUnrealEditor Win64 Development -Project=\"D:/Dev/Mosis/MosisUnreal/MosisUnreal.uproject\")", + "Bash(\"D:/Epic/UE_5.5/Engine/Build/BatchFiles/Build.bat\" MosisUnreal Android Development -Project=\"D:/Dev/Mosis/MosisUnreal/MosisUnreal.uproject\")", + "Bash(\"D:/Epic/UE_5.5/Engine/Build/BatchFiles/RunUAT.bat\" BuildCookRun -project=\"D:/Dev/Mosis/MosisUnreal/MosisUnreal.uproject\" -platform=Android -clientconfig=Development -build -cook -stage -pak -package -noP4 -utf8output)", + "Bash(git -C \"D:/Dev/Mosis/MosisUnreal\" add Plugins/MosisSDK/MosisSDK.uplugin Plugins/MosisSDK/Source/MosisSDK/MosisSDK.Build.cs Plugins/MosisSDK/Source/MosisSDK/Private/MosisPhoneActor.cpp Plugins/MosisSDK/Source/MosisSDK/Private/MosisPointerComponent.cpp Plugins/MosisSDK/Source/MosisSDK/Public/MosisPointerComponent.h)" + ], + "additionalDirectories": [ + "D:\\Dev\\Mosis\\MosisUnreal", + "D:\\Dev\\Mosis\\MosisVR" + ] + } +} diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md deleted file mode 100644 index 7032d35..0000000 --- a/ARCHITECTURE.md +++ /dev/null @@ -1,96 +0,0 @@ -# MosisService Architecture - -## Overview -MosisService is an Android application that combines Kotlin UI components with native C++ libraries for UI rendering and system interaction. The architecture is built around a service-oriented design using Android's Binder system and integrates with RmlUi for rich UI rendering. - -## Core Components - -### 1. Service Layer (mosis-service) -- **Purpose**: Main Android Binder service implementation -- **Interface**: Implements `IMosisService.aidl` -- **Functionality**: - - Touch event processing (onTouchDown, onTouchMove, onTouchUp) - - Service initialization and listener management - - Integration with kernel-based rendering system - - Asset loading through Android AssetManager - -### 2. Rendering Layer (mosis-test) -- **Purpose**: UI rendering and testing infrastructure -- **Interface**: Implements `IMosisListener.aidl` -- **Functionality**: - - OpenGL ES 2.0 rendering pipeline using GLAD - - Multi-threaded rendering with EGL context management - - Surface and buffer handling for Android native windows - - Task queue management for asynchronous rendering operations - - Hardware buffer management and processing - -### 3. Core Engine (Kernel) -- **Purpose**: Central rendering and event processing engine -- **Components**: - - RmlUi-based UI rendering engine - - EGL context creation and management - - Render target handling - - Touch event processing and UI updates - - Multi-threaded execution using std::thread - -### 4. Supporting Libraries -- **AssetsManager**: Asset loading from Android AssetManager -- **Logger**: Cross-platform logging system -- **EGL Context**: OpenGL ES context management -- **Render Target**: Framebuffer and buffer management -- **Shader**: OpenGL shader program handling -- **External Texture**: Hardware buffer texture creation - -## System Architecture - -### Android Binder Integration -- Uses AIDL (Android Interface Definition Language) for cross-process communication -- Service exposes `IMosisService` interface for touch events and initialization -- Listener pattern with `IMosisListener` for rendering callbacks -- Binds to Java/Kotlin components through JNI - -### Multi-threading Model -- Service layer runs in main thread for event handling -- Rendering loop runs in dedicated thread managed by Kernel -- Async task processing for UI updates -- Thread-safe communication between components - -### Rendering Pipeline -1. **Initialization**: Service connects to test layer, creates EGL context -2. **Buffer Management**: Hardware buffers allocated and shared between layers -3. **Event Processing**: Touch events processed and forwarded to Kernel -4. **Rendering Loop**: Continuous rendering with frame synchronization -5. **UI Updates**: RmlUi engine updates UI based on events and data - -### Data Flow -``` -User Touch Events → IMosisService.onTouch* → Kernel.process → RmlUi UI Updates -Service Initialized → IMosisListener.onServiceInitialized → Rendering Setup -Buffer Available → IMosisListener.onBufferAvailable → Texture Creation -Frame Available → IMosisListener.onFrameAvailable → Frame Rendering -``` - -## Key Technologies -- **Android NDK**: Native development with C++23 -- **CMake**: Build system for native libraries -- **RmlUi**: UI rendering engine with HTML/CSS-like markup -- **OpenGL ES 2.0**: Graphics rendering -- **GLAD**: OpenGL loader library -- **AIDL**: Inter-process communication -- **Binder**: Android's IPC mechanism - -## File Structure -- `mosis-service.cpp`: Main service implementation -- `mosis-test.cpp`: Test/rendering implementation -- `kernel.cpp`: Core rendering engine and event processing -- `assets_manager.cpp`: Asset loading utilities -- `egl_context.cpp`: EGL context management -- `render_target.cpp`: Framebuffer handling -- `logger.cpp`: Cross-platform logging - -## Code Standards -- Modern C++23 with smart pointers and RAII -- Thread-safe operations using std::mutex -- Resource management with proper cleanup -- Use of std::span, std::format for modern features -- Cross-platform compatibility through Android NDK \ No newline at end of file diff --git a/CLAUDE.md b/CLAUDE.md deleted file mode 100644 index f5d05d7..0000000 --- a/CLAUDE.md +++ /dev/null @@ -1,1254 +0,0 @@ -# CLAUDE.md - -This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. - -## Company - -**OmixLab LTD** - Package namespace: `com.omixlab` - -## Git Commit Guidelines - -**IMPORTANT**: When creating git commits: -- **DO NOT** add yourself as a co-author (no `Co-Authored-By` lines) -- **Commit messages must be a single line** - keep it concise and descriptive - -## Project Overview - -Mosis is a **virtual smartphone OS** for VR games and applications. It provides a phone-like device that users can interact with inside VR environments, with real smartphone functionality. - -### Current Status - -| Component | Status | Notes | -|-----------|--------|-------| -| MosisService | ✅ Working | RmlUi rendering, touch input, navigation | -| App Management | ✅ Working | Install/uninstall apps, sandbox integration | -| Lua Sandbox | ✅ Working | 149 security tests passing | -| Desktop Designer | ✅ Working | Hot-reload, hierarchy dump, recording | -| Designer Tests | ✅ 5/5 Passing | Navigation tests automated | -| MosisVR (Unity) | ✅ Building | OpenGL backend working, Vulkan in progress | -| MosisUnreal | ✅ Working | Vulkan texture import via UE5 RHI, phone actor with mesh | - -### Project Components - -| Component | Location | Purpose | -|-----------|----------|---------| -| Android Service | `src/main/` | Native service running RmlUi renderer | -| App Management | `src/main/cpp/apps/` | App install/uninstall/launch with sandbox | -| Lua Sandbox | `src/main/cpp/sandbox/` | Per-app Lua isolation (22 modules) | -| Desktop Designer | `designer/` | UI development with hot-reload | -| Designer Tests | `designer-test/` | Automated UI testing framework | -| Sandbox Tests | `sandbox-test/` | Lua sandbox security tests (149 tests) | -| UI Assets | `src/main/assets/` | Shared RML/RCSS/Lua assets | - -## Build Commands - -### Android (Gradle) - -```bash -# Build entire project -./gradlew build - -# Build debug APK -./gradlew assembleDebug - -# Build release APK -./gradlew assembleRelease - -# Clean build outputs -./gradlew clean - -# Build with verbose output -./gradlew build --info --stacktrace - -# Run lint checks -./gradlew lint - -# Run unit tests -./gradlew test - -# Run connected device tests -./gradlew connectedAndroidTest -``` - -### Desktop Designer (CMake) - -```bash -# Configure (from designer/ folder) -cmake -B build -DCMAKE_TOOLCHAIN_FILE=%VCPKG_ROOT%/scripts/buildsystems/vcpkg.cmake - -# Build -cmake --build build --config Debug - -# Run with hot-reload -./build/Debug/mosis-designer.exe ../src/main/assets/apps/home/home.rml - -# Run with logging and hierarchy dump -./build/Debug/mosis-designer.exe ../src/main/assets/apps/home/home.rml --log output.log --hierarchy hierarchy.json -``` - -### Designer Tests (CMake) - -```bash -# Configure (from designer-test/ folder) -cmake -B build -DCMAKE_TOOLCHAIN_FILE=%VCPKG_ROOT%/scripts/buildsystems/vcpkg.cmake - -# Build -cmake --build build --config Debug - -# Run tests -./build/Debug/designer-test.exe -``` - -### Sandbox Security Tests (CMake) - -```bash -# Configure (from sandbox-test/ folder) -cmake -B build -DCMAKE_TOOLCHAIN_FILE=%VCPKG_ROOT%/scripts/buildsystems/vcpkg.cmake - -# Build -cmake --build build --config Debug - -# Run all tests (uber command) -./run_tests.bat - -# Or run directly -./build/Debug/sandbox-test.exe - -# Run specific test -./build/Debug/sandbox-test.exe --test DangerousGlobals -./build/Debug/sandbox-test.exe --test Memory -./build/Debug/sandbox-test.exe --test CPU -``` - -## Environment Requirements - -Required environment variables: -- `ANDROID_HOME` - Android SDK path -- `ANDROID_NDK_HOME` - Android NDK path (version 29.0.14206865) -- `VCPKG_ROOT` - vcpkg package manager root - -## Architecture Overview - -MosisService is an Android application combining Kotlin UI with native C++ libraries for UI rendering via Android's Binder IPC system. - -### Two Native Libraries - -**mosis-service** (`libmosis-service.so`): -- Main Android Binder service implementation -- Implements `IMosisService.aidl` interface for touch events and initialization -- Contains the Kernel rendering engine with RmlUi integration -- Links against RmlUi for HTML/CSS-like UI rendering - -**mosis-test** (`libmosis-test.so`): -- Test/rendering client implementation -- Implements `IMosisListener.aidl` for receiving callbacks -- OpenGL ES 2.0 rendering pipeline using GLAD - -### IPC Flow - -``` -Kotlin NativeService → JNI → mosis-service (IMosisService) - ↓ - IMosisListener callbacks - ↓ - mosis-test (rendering client) -``` - -### Key Interfaces (AIDL) - -`IMosisService`: `initOS()`, `onTouchDown()`, `onTouchMove()`, `onTouchUp()` - -`IMosisListener` (oneway/async): `onServiceInitialized()`, `onBufferAvailable()`, `onFrameAvailable()` - -### Native Code Structure (src/main/cpp/) - -**Core:** -- `kernel.cpp` - Core rendering engine, RmlUi integration, event processing -- `mosis-service.cpp` - Binder service implementation, JNI entry points -- `mosis-test.cpp` - Test client implementation -- `egl_context.cpp` - OpenGL ES context management -- `render_target.cpp` - Framebuffer and buffer management -- `RmlUi_Renderer_GL3.cpp` - RmlUi OpenGL renderer backend -- `assets_manager.cpp` - Android AssetManager integration - -**App Management (`apps/`):** -- `app_manager.cpp` - App install/uninstall/launch lifecycle -- `app_api.cpp` - Lua API bindings for app management -- `update_service.cpp` - Background update checking - -**Lua Sandbox (`sandbox/`):** -- `sandbox_manager.cpp` - Multi-app sandbox orchestrator -- `lua_sandbox.cpp` - Core Lua sandbox with resource limits -- `permission_gate.cpp` - Permission system (normal/dangerous/signature) -- `virtual_fs.cpp` - Per-app virtual filesystem with quotas -- `database_manager.cpp` - SQLite database per app -- `network_manager.cpp` - HTTP request validation -- `websocket_manager.cpp` - WebSocket connections -- `timer_manager.cpp` - setTimeout/setInterval implementation -- `json_api.cpp` - Safe JSON encode/decode -- `crypto_api.cpp` - Cryptographic functions (SHA256, HMAC) -- `camera_interface.cpp` - Camera access with indicators -- `microphone_interface.cpp` - Microphone access with indicators -- `audio_output.cpp` - Audio playback -- `location_interface.cpp` - GPS with precision reduction -- `sensor_interface.cpp` - Accelerometer, gyroscope, etc. -- `bluetooth_interface.cpp` - Bluetooth device access -- `contacts_interface.cpp` - Contacts read/write -- `message_bus.cpp` - Inter-app communication - -## Code Style - -- C++23 standard with modern features (std::span, std::format) -- PascalCase for classes/functions, camelCase for variables -- RAII principles with smart pointers -- Kotlin code follows Android conventions - -## Dependencies - -- vcpkg manages native dependencies (RmlUi, GLFW, Freetype, Lua, libpng, nlohmann-json, minizip, sqlite3) -- CMake build system with vcpkg toolchain integration -- Android target architecture: arm64-v8a only -- Desktop target: Windows x64 (MSVC) - -## Desktop Designer - -The desktop designer (`designer/`) provides rapid UI development with: - -- **Hot-reload**: Automatically reloads when RML/RCSS/Lua files change -- **UI Hierarchy Dumping**: Exports element tree to JSON for inspection -- **Screenshot Capture**: PNG export via F12 key -- **Logging**: Detailed output for debugging navigation and events -- **Action Recording**: Record mouse/keyboard interactions to JSON -- **Action Playback**: Replay recorded interactions with timing - -### Key Files - -| File | Purpose | -|------|---------| -| `designer/src/main.cpp` | Main entry point, GLFW window, event loop | -| `designer/src/desktop_kernel.cpp` | RmlUi context management, rendering | -| `designer/src/testing/ui_inspector.cpp` | UI hierarchy JSON export | -| `designer/src/testing/visual_capture.cpp` | PNG screenshot capture and comparison | -| `designer/src/testing/action_recorder.cpp` | Record user interactions to JSON | -| `designer/src/testing/action_player.cpp` | Playback recorded actions | -| `designer/src/backend/RmlUi_Backend_GLFW_GL3.cpp` | GLFW backend with input hooks | - -### Command Line Options - -``` ---log Write logs to file ---hierarchy Dump UI hierarchy JSON each frame ---dump Single-shot dump mode (screenshot + hierarchy) ---record Enable recording mode (F5 to start/stop) ---playback Play back recorded actions from JSON -``` - -### Keyboard Controls - -| Key | Function | -|-----|----------| -| F5 | Start/stop recording (when --record is enabled) | -| F6 | Pause/resume playback (when --playback is enabled) | -| F12 | Take screenshot | - -## Automated Testing Framework - -The designer-test (`designer-test/`) provides automated UI testing: - -### Test Architecture - -1. **WindowController**: Finds designer window, sends mouse/keyboard events via Windows SendInput API -2. **HierarchyReader**: Parses UI hierarchy JSON to find elements by ID/class (with retry logic and exponential backoff) -3. **LogParser**: Monitors log file for navigation events -4. **TestRunner**: Orchestrates test execution, reports results -5. **UIInspector**: Dumps UI hierarchy with atomic writes (temp file + rename pattern) - -### Key Implementation Details - -- **Path Normalization**: RmlUi uses `|` instead of `:` in Windows paths (e.g., `D|\Dev\...`). The UIInspector normalizes paths for correct document matching. -- **Atomic File Writes**: Hierarchy files are written to `.tmp` then renamed to prevent partial reads. -- **Retry with Backoff**: HierarchyReader retries up to 10 times with exponential backoff (30ms base) and validates JSON completeness. -- **Dynamic Back Button**: `GoHome()` finds back buttons from hierarchy by class (`app-bar-nav` or `browser-nav-btn`) instead of fixed coordinates. - -### Writing Tests - -```cpp -// Find element by ID and click it -bool ClickById(TestContext& ctx, const std::string& id) { - ctx.hierarchy.Reload(); - auto element = ctx.hierarchy.FindById(id); - if (!element) return false; - - int x = element->bounds.centerX(); - int y = element->bounds.centerY(); - ScaleToPhysical(ctx, x, y); // Convert logical to physical coords - ctx.window.SendClick(x, y); - return true; -} - -// Test example -bool TestNavigateToDialer(TestContext& ctx) { - GoHome(ctx); - ctx.log.Clear(); - - if (!ClickById(ctx, "dock-phone")) return false; - - std::this_thread::sleep_for(std::chrono::milliseconds(1000)); - ctx.log.Reload(); - return ctx.log.Contains("Loaded screen: apps/dialer/dialer.rml"); -} -``` - -### Test Output - -Tests produce JSON results at `test_results.json`: -```json -{ - "name": "Mosis Designer UI Tests", - "summary": {"passed": 5, "failed": 0, "total": 5}, - "tests": [ - {"name": "Navigate to Dialer", "status": "passed", "duration_ms": 3500} - ] -} -``` - -## UI Assets Structure - -All UI assets are in `src/main/assets/`: - -``` -assets/ -├── apps/ # System apps (RML documents) -│ ├── home/home.rml # Home screen launcher -│ ├── dialer/dialer.rml # Phone dialer -│ ├── messages/ # Messages app -│ ├── contacts/ # Contacts app -│ ├── settings/ # Settings app -│ └── browser/ # Web browser -├── ui/ # Shared stylesheets -│ ├── html.rcss # Base HTML element styles -│ ├── theme.rcss # Design tokens (colors, typography) -│ └── components.rcss # Reusable UI components -├── scripts/ # Lua scripts -│ └── navigation.lua # Navigation system -├── icons/ # TGA icon files (24x24, 32x32) -└── fonts/ # TTF fonts (LatoLatin, Roboto) -``` - -### Navigation System - -Navigation is handled by `scripts/navigation.lua`: - -```lua --- Navigate to a screen -navigateTo('dialer') -- Push to history, animate forward - --- Go back -goBack() -- Pop from history, animate back - --- Go home -goHome() -- Clear history, return to home -``` - -### Element IDs for Testing - -Key elements have IDs for automated testing: - -| ID | Location | Purpose | -|----|----------|---------| -| `dock-phone` | home.rml | Phone dock icon | -| `dock-messages` | home.rml | Messages dock icon | -| `dock-contacts` | home.rml | Contacts dock icon | -| `dock-browser` | home.rml | Browser dock icon | -| `app-settings` | home.rml | Settings app icon | - -### CSS Classes for Navigation - -Back buttons use `app-bar-nav` class for automated GoHome: -```html -
- -
-``` - -Browser uses `browser-nav-btn` class for its toolbar back button: -```html -
- -
-``` - -The test framework's `FindBackButton()` searches for both classes to handle all screen layouts. - -## Material Design Resources - -Material Design icons and components are available in the MosisDesigner repository: - -### Material Design Icons - -**Location**: `D:\Dev\Mosis\MosisDesigner\material-design-icons` - -A comprehensive icon library from Google with 2000+ icons across 20 categories: - -| Category | Examples | -|----------|----------| -| action | home, search, settings, delete, info | -| alert | error, warning, notification | -| av | play, pause, volume, mic | -| communication | phone, message, email, contacts | -| content | add, remove, copy, paste | -| device | battery, wifi, bluetooth, gps | -| editor | format, text, color, brush | -| file | folder, attachment, download, upload | -| hardware | keyboard, mouse, phone, tablet | -| home | lightbulb, thermostat, security | -| image | camera, photo, filter, tune | -| maps | location, directions, navigation | -| navigation | arrow, chevron, menu, close | -| notification | sync, update, event | -| places | hotel, restaurant, airport | -| search | search variants | -| social | share, person, group, notifications | -| toggle | star, checkbox, radio | - -**Available Formats**: -- `src/` - SVG source files organized by category -- `png/` - PNG files at multiple DPIs (24dp, 36dp, 48dp) -- `font/` - Icon fonts (WOFF, TTF) -- `symbols/` - Material Symbols variable font (newer) -- `variablefont/` - Variable font files - -**Icon Styles**: -- Outlined (default) -- Filled -- Rounded -- Sharp -- Two-tone (Material Icons only) - -### Material Design Lite - -**Location**: `D:\Dev\Mosis\MosisDesigner\material-design-lite` - -CSS/JS component library implementing Material Design (reference implementation): - -| Directory | Contents | -|-----------|----------| -| `src/` | SASS source for components | -| `docs/` | Component documentation | -| `templates/` | Page templates | - -**Key Components** (for design reference): -- Buttons (raised, flat, FAB) -- Cards -- Dialogs -- Lists -- Menus -- Navigation drawers -- Progress indicators -- Sliders -- Snackbars -- Tables -- Tabs -- Text fields -- Tooltips - -### Using Icons in Mosis - -1. **Find icon** at https://fonts.google.com/icons -2. **Export SVG** from `material-design-icons/src///` -3. **Convert to TGA** using image tool (24x24 or 32x32, RGBA) -4. **Place in** `src/main/assets/icons/` -5. **Reference in RML**: `` - -## Android Device Testing - -### Prerequisites - -```bash -# Check connected device -adb devices -l - -# Verify Mosis app is installed -adb shell pm list packages | grep mosis -``` - -### Build and Install - -```bash -# Build debug APK -./gradlew assembleDebug - -# Install on device -adb install -r build/outputs/apk/debug/MosisService-debug.apk - -# Launch the app -adb shell am start -n com.omixlab.mosis/.MainActivity -``` - -### Run Gradle Connected Tests - -```bash -# Run all connected Android tests -./gradlew connectedAndroidTest -``` - -### Event Injection via ADB - -Inject touch events for automated testing: - -```bash -# Click at normalized coordinates (0.0-1.0) -adb shell am broadcast -a com.omixlab.mosis.INJECT_TOUCH \ - --es touch_type "click" --ef x 0.5 --ef y 0.5 - -# Touch down -adb shell am broadcast -a com.omixlab.mosis.INJECT_TOUCH \ - --es touch_type "down" --ef x 0.2 --ef y 0.9 - -# Touch up -adb shell am broadcast -a com.omixlab.mosis.INJECT_TOUCH \ - --es touch_type "up" --ef x 0.2 --ef y 0.9 -``` - -### Dock Element Coordinates (Normalized) - -| Element | X | Y | -|---------|---|---| -| dock-phone | 0.16 | 0.97 | -| dock-messages | 0.39 | 0.97 | -| dock-contacts | 0.61 | 0.97 | -| dock-browser | 0.84 | 0.97 | -| back-button | 0.10 | 0.05 | - -### Full Navigation Test Sequence - -```bash -# Clear logs and run navigation test sequence -adb logcat -c - -# Click Phone dock icon -adb shell am broadcast -a com.omixlab.mosis.INJECT_TOUCH \ - --es touch_type "click" --ef x 0.16 --ef y 0.97 -sleep 2 - -# Click back to return home -adb shell am broadcast -a com.omixlab.mosis.INJECT_TOUCH \ - --es touch_type "click" --ef x 0.1 --ef y 0.05 -sleep 2 - -# Click Messages dock icon -adb shell am broadcast -a com.omixlab.mosis.INJECT_TOUCH \ - --es touch_type "click" --ef x 0.39 --ef y 0.97 -sleep 2 - -# Click back to return home -adb shell am broadcast -a com.omixlab.mosis.INJECT_TOUCH \ - --es touch_type "click" --ef x 0.1 --ef y 0.05 -sleep 2 - -# Click Contacts dock icon -adb shell am broadcast -a com.omixlab.mosis.INJECT_TOUCH \ - --es touch_type "click" --ef x 0.61 --ef y 0.97 -sleep 2 - -# Click back to return home -adb shell am broadcast -a com.omixlab.mosis.INJECT_TOUCH \ - --es touch_type "click" --ef x 0.1 --ef y 0.05 -sleep 2 - -# Click Browser dock icon -adb shell am broadcast -a com.omixlab.mosis.INJECT_TOUCH \ - --es touch_type "click" --ef x 0.84 --ef y 0.97 -``` - -### Reading Logs - -```bash -# Filter for Mosis logs -adb logcat -s MosisTest ServiceTester RMLUI - -# Filter for navigation events -adb logcat -d | grep -iE "navigat|loaded|goBack|rml" - -# Save to file -adb logcat -s MosisTest > mosis-log.txt - -# Clear logs -adb logcat -c -``` - -### Expected Log Output - -Successful navigation shows these log patterns: -``` -RMLUI: navigateTo called with: dialer -Loading screen: apps/dialer/dialer.rml -RMLUI: Navigated to: dialer (history depth: 1) - -RMLUI: goBack called (history depth: 1) -Loading screen: apps/home/home.rml -RMLUI: Back to: home -``` - -## Documentation Guidelines - -**IMPORTANT**: Always document progress and new commands to avoid rediscovery. - -### Where to Put Documentation - -| Content Type | Location | -|--------------|----------| -| General concepts, architecture | `MosisService/CLAUDE.md` (this file) | -| Unreal plugin docs | `MosisUnreal/Plugins/MosisSDK/README.md` | -| Unity package docs | `MosisVR/Packages/com.omixlab.mosis_sdk/README.md` | -| Project-specific build commands | Each project's own docs | - -**DO NOT** put documentation in the root `D:\Dev\Mosis\` directory - it is not versioned. - -### What to Document - -1. **Build commands** - Every new build command discovered or created -2. **Environment setup** - Required environment variables, SDK versions -3. **Architecture decisions** - Why something was done a certain way -4. **Issues and solutions** - Problems encountered and how they were fixed -5. **File locations** - Where important files are and what they do - -### When to Document - -- After completing a milestone or feature -- When discovering new build commands -- When fixing a non-obvious issue -- When adding new dependencies or requirements - -## Game Engine Integrations - -MosisService provides a virtual phone that game engines can display and interact with. - -### Integration Architecture - -``` -┌─────────────────────────────────────────────────────────────────┐ -│ MosisService │ -│ (OpenGL ES rendering → AHardwareBuffer) │ -└───────────────────────────┬─────────────────────────────────────┘ - │ Binder IPC + Shared Memory - ┌─────────────────┴─────────────────┐ - ▼ ▼ -┌─────────────────────┐ ┌─────────────────────┐ -│ MosisUnreal │ │ MosisVR │ -│ (UE5.5 Plugin) │ │ (Unity Package) │ -│ Vulkan Import │ │ Vulkan/OpenGL │ -└─────────────────────┘ └─────────────────────┘ -``` - -### MosisUnreal (Unreal Engine 5.5) - -**Location**: `D:\Dev\Mosis\MosisUnreal\Plugins\MosisSDK\` - -#### Build Commands - -```batch -:: Windows Editor Build -"D:\Epic\UE_5.5\Engine\Build\BatchFiles\Build.bat" ^ - MosisUnrealEditor Win64 Development ^ - -Project="D:\Dev\Mosis\MosisUnreal\MosisUnreal.uproject" - -:: Android APK Build -"D:\Epic\UE_5.5\Engine\Build\BatchFiles\RunUAT.bat" ^ - BuildCookRun ^ - -project="D:\Dev\Mosis\MosisUnreal\MosisUnreal.uproject" ^ - -platform=Android -clientconfig=Development ^ - -build -cook -stage -pak -package -noP4 - -:: Clean Build (delete these folders first) -rmdir /s /q "Intermediate\Build" -rmdir /s /q "Binaries" -``` - -#### Output Files - -| Build | Output | -|-------|--------| -| Windows Editor | `Plugins/MosisSDK/Binaries/Win64/UnrealEditor-MosisSDK.dll` | -| Android APK | `Binaries/Android/MosisUnreal-arm64.apk` | -| Android OBB | `Binaries/Android/main.1.com.omixlab.MosisUnreal.obb` | - -#### Requirements - -- Android SDK Platform 36 (for AIDL binder headers) -- Android Build Tools 36.1.0 (for AIDL compiler) -- `ANDROID_HOME` environment variable set - -#### Quest Deployment - -**IMPORTANT**: Git Bash on Windows converts Unix paths to Windows paths. Use `MSYS_NO_PATHCONV=1` prefix for all adb push commands. - -```bash -# Set Quest device ID (check with: adb devices -l) -QUEST=2G0YC5ZF7W01T0 - -# Install APK -adb -s $QUEST install -r D:/Dev/Mosis/MosisUnreal/Binaries/Android/MosisUnreal-arm64.apk - -# Create OBB temp directory -adb -s $QUEST shell "mkdir -p /data/local/tmp/obb/com.omixlab.MosisUnreal" - -# Push OBB to temp location (MUST use MSYS_NO_PATHCONV=1) -MSYS_NO_PATHCONV=1 adb -s $QUEST push D:/Dev/Mosis/MosisUnreal/Binaries/Android/main.1.com.omixlab.MosisUnreal.obb /data/local/tmp/obb/com.omixlab.MosisUnreal/ - -# Move OBB to final location -adb -s $QUEST shell "rm -rf /sdcard/Android/obb/com.omixlab.MosisUnreal" -adb -s $QUEST shell "mv /data/local/tmp/obb/com.omixlab.MosisUnreal /sdcard/Android/obb/" - -# Start MosisService first, then MosisUnreal -adb -s $QUEST shell am start -n com.omixlab.mosis/.MainActivity -sleep 3 -adb -s $QUEST shell am start -n com.omixlab.MosisUnreal/com.epicgames.unreal.GameActivity - -# Monitor logs -adb -s $QUEST logcat -s MosisSDK MosisOS MosisTest -``` - -**Full rebuild and deploy sequence**: -```bash -# 1. Build Android APK+OBB -"D:\Epic\UE_5.5\Engine\Build\BatchFiles\RunUAT.bat" BuildCookRun \ - -project="D:\Dev\Mosis\MosisUnreal\MosisUnreal.uproject" \ - -platform=Android -clientconfig=Development \ - -build -cook -stage -pak -package -noP4 - -# 2. Stop app, push APK and OBB, restart -QUEST=2G0YC5ZF7W01T0 -adb -s $QUEST shell am force-stop com.omixlab.MosisUnreal -adb -s $QUEST install -r D:/Dev/Mosis/MosisUnreal/Binaries/Android/MosisUnreal-arm64.apk -adb -s $QUEST shell "mkdir -p /data/local/tmp/obb/com.omixlab.MosisUnreal" -MSYS_NO_PATHCONV=1 adb -s $QUEST push D:/Dev/Mosis/MosisUnreal/Binaries/Android/main.1.com.omixlab.MosisUnreal.obb /data/local/tmp/obb/com.omixlab.MosisUnreal/ -adb -s $QUEST shell "rm -rf /sdcard/Android/obb/com.omixlab.MosisUnreal && mv /data/local/tmp/obb/com.omixlab.MosisUnreal /sdcard/Android/obb/" -adb -s $QUEST shell am start -n com.omixlab.MosisUnreal/com.epicgames.unreal.GameActivity -``` - -**Common issues**: -- App stuck on loading screen: OBB not pushed or wrong version -- `adb push` path errors: Missing `MSYS_NO_PATHCONV=1` prefix -- Service not connecting: Start `com.omixlab.mosis` before the game - -#### VR Pointer Interaction - -The MosisSDK includes `UMosisPointerComponent` for VR ray-based touch interaction. - -**Key Files**: -- `Public/MosisPointerComponent.h` - Component header -- `Private/MosisPointerComponent.cpp` - Implementation - -**Features**: -- Raycast from component transform (attach as child of motion controller) -- Automatic detection of `AMosisPhoneActor` hits -- Touch state machine: Down/Move/Up events -- Optional Enhanced Input binding via `TriggerAction` property -- Manual control via `SetTriggerPressed(bool)` -- Debug ray visualization - -**Blueprint Setup**: -1. Add `MosisPointerComponent` as child of `MotionControllerComponent` -2. Set `TriggerAction` to your trigger input action (optional) -3. Touch events are sent automatically when pointing at phone and triggering - -**C++ Setup**: -```cpp -// In VR Pawn header -UPROPERTY(VisibleAnywhere) -UMosisPointerComponent* RightPointer; - -// In constructor -RightPointer = CreateDefaultSubobject(TEXT("RightPointer")); -RightPointer->SetupAttachment(RightMotionController); - -// In BeginPlay (if using Enhanced Input) -RightPointer->TriggerAction = TriggerInputAction; -``` - -**Manual Control (without Enhanced Input)**: -```cpp -// In your input handling code -void AMyVRPawn::OnTriggerPressed() -{ - RightPointer->SetTriggerPressed(true); -} - -void AMyVRPawn::OnTriggerReleased() -{ - RightPointer->SetTriggerPressed(false); -} -``` - -**Component Properties**: - -| Property | Type | Default | Description | -|----------|------|---------|-------------| -| `RayLength` | float | 500.0 | Maximum ray length in cm | -| `bShowDebugRay` | bool | false | Draw debug visualization | -| `DebugRayColor` | FLinearColor | Red | Ray color when not hitting | -| `DebugRayHitColor` | FLinearColor | Green | Ray color when hitting phone | -| `TraceChannel` | ECollisionChannel | Visibility | Collision channel for raycast | -| `TriggerAction` | UInputAction* | nullptr | Enhanced Input action for trigger | - -**Blueprint Functions**: - -| Function | Returns | Description | -|----------|---------|-------------| -| `IsPointingAtPhone()` | bool | True if ray hits a phone actor | -| `GetTargetPhone()` | AMosisPhoneActor* | Currently targeted phone (or nullptr) | -| `GetHitLocation()` | FVector | World location of ray hit | -| `GetRayOrigin()` | FVector | Ray start position | -| `GetRayDirection()` | FVector | Ray direction vector | -| `IsTriggerPressed()` | bool | Current trigger state | -| `SetTriggerPressed(bool)` | void | Manual trigger control | - -**Touch State Machine**: -``` -Idle ──[raycast hit]──► Hover ──[trigger]──► Pressed - ▲ │ │ - │ │ raycast miss │ trigger release - │ ▼ ▼ - └──────────────────── Idle ◄──────────────────── - -Events: -- Idle → Pressed: SendTouch(Down) -- Pressed + moving: SendTouch(Move) -- Pressed → Idle: SendTouch(Up) -``` - -### MosisVR (Unity 6000.3.2f1) - -**Location**: `D:\Dev\Mosis\MosisVR\Packages\com.omixlab.mosis_sdk\` - -#### Direct APK Build (Recommended) - -```batch -"C:\Program Files\Unity\Hub\Editor\6000.3.2f1\Editor\Unity.exe" ^ - -batchmode -quit -nographics ^ - -projectPath "D:\Dev\Mosis\MosisVR" ^ - -executeMethod BuildScript.BuildAndroidDirectCI ^ - -outputPath "D:\Dev\Mosis\Builds\Unity\Android\MosisVR.apk" -``` - -#### Export + Gradle Build - -For more control, export a Gradle project then build separately: - -```batch -:: Step 1: Export from Unity -"C:\Program Files\Unity\Hub\Editor\6000.3.2f1\Editor\Unity.exe" ^ - -batchmode -quit -nographics ^ - -projectPath "D:\Dev\Mosis\MosisVR" ^ - -executeMethod BuildScript.BuildAndroidCI ^ - -export true ^ - -outputPath "D:\Dev\Mosis\Builds\Unity\Android\MosisVR" - -:: Step 2: Build with Gradle -cd D:\Dev\Mosis\Builds\Unity\Android\MosisVR -gradle assembleRelease -:: APK at: launcher\build\outputs\apk\release\launcher-release.apk -``` - -#### Unity Editor Manual Build - -1. File > Build Settings > Android -2. Player Settings: IL2CPP, ARM64, Vulkan + OpenGLES3 -3. For direct APK: Uncheck "Export Project", click Build -4. For export: Check "Export Project", click Export - -#### Native Plugin Build (Manual) - -The native plugin builds automatically via CMake during Unity's build. To rebuild manually: - -```batch -cd Packages/com.omixlab.mosis_sdk/Plugins/Android/cpp -cmake -B build -DCMAKE_TOOLCHAIN_FILE=%ANDROID_NDK_HOME%/build/cmake/android.toolchain.cmake ^ - -DANDROID_ABI=arm64-v8a -DANDROID_PLATFORM=android-29 -cmake --build build -``` - -### Device Testing (Both Engines) - -```bash -# Install MosisService first -adb install -r MosisService-debug.apk - -# Install game client -adb install -r MosisUnreal-arm64.apk # or MosisVR.apk - -# Launch service -adb shell am start -n com.omixlab.mosis/.MainActivity - -# Launch client -adb shell am start -n com.omixlab.MosisUnreal/com.epicgames.unreal.GameActivity -# or for Unity: -adb shell am start -n com.omixlab.mosisvr/com.unity3d.player.UnityPlayerActivity - -# Monitor all Mosis logs -adb logcat -s MosisSDK MosisTest RMLUI Vulkan -``` - -## Vulkan HardwareBuffer Import - -Both game engines use Vulkan to import AHardwareBuffer from MosisService. - -### Required Vulkan Extensions - -``` -VK_ANDROID_external_memory_android_hardware_buffer -VK_KHR_external_memory -VK_KHR_dedicated_allocation -``` - -### Import Pattern - -```cpp -// 1. Query buffer properties -VkAndroidHardwareBufferPropertiesANDROID props = { - .sType = VK_STRUCTURE_TYPE_ANDROID_HARDWARE_BUFFER_PROPERTIES_ANDROID -}; -VkAndroidHardwareBufferFormatPropertiesANDROID formatProps = { - .sType = VK_STRUCTURE_TYPE_ANDROID_HARDWARE_BUFFER_FORMAT_PROPERTIES_ANDROID -}; -props.pNext = &formatProps; -vkGetAndroidHardwareBufferPropertiesANDROID(device, buffer, &props); - -// 2. Create image with external memory -VkExternalMemoryImageCreateInfo extInfo = { - .sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO, - .handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID -}; - -AHardwareBuffer_Desc desc; -AHardwareBuffer_describe(buffer, &desc); - -VkImageCreateInfo imageInfo = { - .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, - .pNext = &extInfo, - .imageType = VK_IMAGE_TYPE_2D, - .format = formatProps.format, - .extent = {desc.width, desc.height, 1}, - .mipLevels = 1, - .arrayLayers = 1, - .samples = VK_SAMPLE_COUNT_1_BIT, - .tiling = VK_IMAGE_TILING_OPTIMAL, - .usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT, - .sharingMode = VK_SHARING_MODE_EXCLUSIVE -}; -vkCreateImage(device, &imageInfo, nullptr, &image); - -// 3. Import memory from HardwareBuffer -VkImportAndroidHardwareBufferInfoANDROID importInfo = { - .sType = VK_STRUCTURE_TYPE_IMPORT_ANDROID_HARDWARE_BUFFER_INFO_ANDROID, - .buffer = buffer -}; - -VkMemoryDedicatedAllocateInfo dedicatedInfo = { - .sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO, - .pNext = &importInfo, - .image = image -}; - -VkMemoryAllocateInfo allocInfo = { - .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, - .pNext = &dedicatedInfo, - .allocationSize = props.allocationSize, - .memoryTypeIndex = FindMemoryType(props.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) -}; -vkAllocateMemory(device, &allocInfo, nullptr, &memory); -vkBindImageMemory(device, image, memory, 0); -``` - -### Synchronization - -Currently using CPU synchronization (`vkQueueWaitIdle` / `glFinish`). Future improvement: use Vulkan semaphores for GPU-GPU sync. - -### Double Buffering - -The imported image is copied to a local texture each frame to prevent data races with MosisService rendering. - -## Developer Portal - -The Developer Portal is a web application for app developers to publish and manage their Mosis apps. Planning documents are in `DEV_PORTAL_M01-M12.md` files. - -### Architecture Decisions - -| Component | Technology | Rationale | -|-----------|------------|-----------| -| Backend | Go 1.22+ | Simple, fast, single binary deployment | -| Router | Chi | Lightweight, idiomatic Go HTTP routing | -| Database | SQLite + Litestream | Zero-ops, continuous backup to NAS storage | -| Frontend | htmx + Go templates | Server-rendered, minimal JS, fast | -| Storage | Synology NAS filesystem | Self-hosted, local volume mounts | -| Auth | OAuth2 (GitHub/Google) + JWT | golang-jwt for tokens, Ed25519 for signing | -| CLI | Go + Cobra | Cross-platform single binary | -| Docs | Hugo + Docsy | Static site served from /docs/ | - -### Deployment Target - -Self-hosted on Synology NAS via Docker: - -``` -/volume1/mosis/ -├── data/ -│ ├── portal.db # Main SQLite database -│ └── telemetry.db # Separate telemetry database -├── packages/ # Uploaded app packages -│ └── {dev_id}/{app_id}/{version}/ -├── backups/ # Litestream replicas -└── docs/ # Hugo static site output -``` - -### Milestone Documents - -| File | Topic | Status | -|------|-------|--------| -| DEV_PORTAL_M01_OVERVIEW.md | Project overview | Decided | -| DEV_PORTAL_M02_WEB_STACK.md | Go + Chi + htmx | Decided | -| DEV_PORTAL_M03_DATABASE.md | SQLite + Litestream | Decided | -| DEV_PORTAL_M04_AUTH.md | OAuth2 + JWT + Ed25519 | Decided | -| DEV_PORTAL_M05_FRONTEND.md | htmx + Go templates | Decided | -| DEV_PORTAL_M06_API.md | REST API design | Decided | -| DEV_PORTAL_M07_STORAGE.md | NAS filesystem | Decided | -| DEV_PORTAL_M08_TELEMETRY.md | SQLite + background workers | Decided | -| DEV_PORTAL_M09_REVIEW.md | Go validation workers | Decided | -| DEV_PORTAL_M10_DEVICE.md | C++ AppManager | Decided | -| DEV_PORTAL_M11_CLI.md | Go + Cobra CLI | Decided | -| DEV_PORTAL_M12_DOCS.md | Hugo + Docsy | Decided | - -### Key Design Principles - -1. **Single container** - Portal runs as one Docker container on NAS -2. **No external services** - SQLite, local filesystem, Pagefind search -3. **Pure Go** - `modernc.org/sqlite` (no CGO required) -4. **Server-rendered** - htmx for interactivity, no SPA framework -5. **Background workers** - Go goroutines for aggregation/cleanup jobs - -## App Management System (M10) - -The device-side app management system handles installation, updates, and launching of third-party apps. - -### Architecture - -``` -┌─────────────────────────────────────────────────────────────┐ -│ AppManager │ -│ - Install/Uninstall apps from .mosis packages │ -│ - Track installed apps in JSON registry │ -│ - Manage app data/cache directories │ -└─────────────────────────┬───────────────────────────────────┘ - │ - ▼ -┌─────────────────────────────────────────────────────────────┐ -│ LuaSandboxManager │ -│ - StartApp/StopApp lifecycle │ -│ - Per-app isolated Lua environments │ -│ - Resource limits (memory, CPU, timers) │ -└─────────────────────────────────────────────────────────────┘ -``` - -### App Package Format (.mosis) - -Apps are distributed as ZIP files with `.mosis` extension: - -``` -myapp.mosis -├── manifest.json # Required: app metadata -├── main.rml # Entry point (RmlUi document) -├── styles.rcss # Stylesheets -├── scripts/ # Lua scripts -│ └── app.lua -└── assets/ # Icons, images, etc. -``` - -### Manifest Format - -```json -{ - "id": "com.example.myapp", - "name": "My App", - "version": "1.0.0", - "version_code": 1, - "entry": "main.rml", - "icon": "icon.tga", - "description": "App description", - "developer": { - "name": "Developer Name", - "email": "dev@example.com" - }, - "permissions": [ - "network", - "storage", - "camera" - ], - "min_api_version": 1 -} -``` - -### App Lifecycle - -```cpp -// Install from local file -app_manager->InstallFromFile("/path/to/app.mosis", [](auto progress) { - LOG_INFO("Install progress: %s %.0f%%", - InstallProgress::StageName(progress.stage), - progress.progress * 100); -}); - -// Launch app (starts sandbox) -app_manager->LaunchApp("com.example.myapp"); - -// Check if running -bool running = app_manager->IsAppRunning("com.example.myapp"); - -// Stop app (cleanup sandbox) -app_manager->StopApp("com.example.myapp"); - -// Uninstall (stops if running, removes files) -app_manager->Uninstall("com.example.myapp", false); // keep_data=false -``` - -### Directory Structure - -``` -/data/data/com.omixlab.mosis/files/ -├── apps/ -│ └── com.example.myapp/ -│ ├── package/ # Extracted app files -│ ├── data/ # App persistent data (VirtualFS) -│ ├── cache/ # App cache (clearable) -│ └── db/ # SQLite databases -├── downloads/ # Temporary download location -├── backups/ # App data backups -└── config/ - └── apps.json # Installed apps registry -``` - -## Lua Sandbox System - -The sandbox provides secure, isolated Lua environments for third-party apps. - -### Security Features - -| Feature | Implementation | -|---------|----------------| -| Dangerous globals removed | `os`, `io`, `loadfile`, `dofile`, `debug` | -| Memory limits | Configurable per-app (default 10MB) | -| CPU limits | Instruction counting with timeout | -| Bytecode rejected | Only source code allowed | -| Metatables protected | Cannot modify string/table metatables | -| Path traversal blocked | `../` and absolute paths rejected | - -### Permission Categories - -| Category | Auto-Grant | Examples | -|----------|------------|----------| -| Normal | Yes | `storage`, `network` | -| Dangerous | User consent | `camera`, `microphone`, `location`, `contacts` | -| Signature | System apps only | `system_settings`, `install_packages` | - -### Available APIs - -**Core APIs** (always available): -```lua --- Timers -local id = setTimeout(function() end, 1000) -clearTimeout(id) -local id = setInterval(function() end, 500) -clearInterval(id) - --- JSON -local obj = json.decode('{"key": "value"}') -local str = json.encode({key = "value"}) - --- Crypto -local bytes = crypto.randomBytes(16) -local hash = crypto.sha256("data") -local hmac = crypto.hmac("sha256", "key", "data") -``` - -**Storage APIs** (requires `storage` permission): -```lua --- Virtual filesystem (sandboxed to app directory) -fs.write("data.txt", "content") -local content = fs.read("data.txt") -local files = fs.list("/") -local stat = fs.stat("data.txt") -fs.delete("data.txt") - --- SQLite database -local db = database.open("mydb") -db:execute("CREATE TABLE IF NOT EXISTS items (id INTEGER PRIMARY KEY, name TEXT)") -db:execute("INSERT INTO items (name) VALUES (?)", {"item1"}) -local rows = db:query("SELECT * FROM items WHERE id = ?", {1}) -``` - -**Network APIs** (requires `network` permission): -```lua --- HTTP (HTTPS only, private IPs blocked) -local response = http.get("https://api.example.com/data") -local response = http.post("https://api.example.com/data", { - headers = {["Content-Type"] = "application/json"}, - body = json.encode({key = "value"}) -}) - --- WebSocket -local ws = websocket.connect("wss://example.com/ws") -ws:send("message") -ws:onMessage(function(data) end) -ws:close() -``` - -**Hardware APIs** (requires dangerous permissions + user gesture): -```lua --- Camera (requires camera permission) -camera.start(function(frame) end) -camera.stop() - --- Microphone (requires microphone permission) -microphone.start(function(samples) end) -microphone.stop() - --- Location (requires location permission) -location.getCurrentPosition(function(pos) - print(pos.latitude, pos.longitude) -end) - --- Sensors (requires sensors permission) -sensors.subscribe("accelerometer", function(data) - print(data.x, data.y, data.z) -end) -``` - -### Running Sandbox Tests - -```bash -cd sandbox-test -cmake -B build -DCMAKE_TOOLCHAIN_FILE=%VCPKG_ROOT%/scripts/buildsystems/vcpkg.cmake -cmake --build build --config Debug -./build/Debug/sandbox-test.exe - -# Output: 149 tests, all passing -``` - -### Test Categories - -| Category | Tests | Description | -|----------|-------|-------------| -| Security | 11 | Globals removal, bytecode, metatables | -| Resources | 8 | Memory, CPU limits, instruction counting | -| Permissions | 7 | Normal/dangerous/signature grants | -| Rate Limiting | 6 | API call throttling | -| Timers | 7 | setTimeout/setInterval behavior | -| JSON | 5 | Encode/decode, depth limits | -| Crypto | 4 | Random, SHA256, HMAC | -| VirtualFS | 8 | Read/write, quotas, traversal | -| Database | 8 | SQLite operations, injection prevention | -| Network | 8 | URL validation, private IP blocking | -| WebSocket | 7 | Connection limits, message size | -| Hardware | 42 | Camera, mic, location, sensors, bluetooth | -| IPC | 7 | Message bus between apps | -| Integration | 9 | Full app lifecycle | -| Fuzzing | 3 | Random input crash testing | diff --git a/AGENTS.md b/docs/AGENTS.md similarity index 100% rename from AGENTS.md rename to docs/AGENTS.md diff --git a/docs/ANDROID-TESTING.md b/docs/ANDROID-TESTING.md new file mode 100644 index 0000000..71991b2 --- /dev/null +++ b/docs/ANDROID-TESTING.md @@ -0,0 +1,129 @@ +# Android Device Testing + +## Prerequisites + +```bash +# Check connected device +adb devices -l + +# Verify Mosis app is installed +adb shell pm list packages | grep mosis +``` + +## Build and Install + +```bash +# Build debug APK +./gradlew assembleDebug + +# Install on device +adb install -r build/outputs/apk/debug/MosisService-debug.apk + +# Launch the app +adb shell am start -n com.omixlab.mosis/.MainActivity +``` + +## Run Gradle Connected Tests + +```bash +# Run all connected Android tests +./gradlew connectedAndroidTest +``` + +## Event Injection via ADB + +Inject touch events for automated testing: + +```bash +# Click at normalized coordinates (0.0-1.0) +adb shell am broadcast -a com.omixlab.mosis.INJECT_TOUCH \ + --es touch_type "click" --ef x 0.5 --ef y 0.5 + +# Touch down +adb shell am broadcast -a com.omixlab.mosis.INJECT_TOUCH \ + --es touch_type "down" --ef x 0.2 --ef y 0.9 + +# Touch up +adb shell am broadcast -a com.omixlab.mosis.INJECT_TOUCH \ + --es touch_type "up" --ef x 0.2 --ef y 0.9 +``` + +## Dock Element Coordinates (Normalized) + +| Element | X | Y | +|---------|---|---| +| dock-phone | 0.16 | 0.97 | +| dock-messages | 0.39 | 0.97 | +| dock-contacts | 0.61 | 0.97 | +| dock-browser | 0.84 | 0.97 | +| back-button | 0.10 | 0.05 | + +## Full Navigation Test Sequence + +```bash +# Clear logs and run navigation test sequence +adb logcat -c + +# Click Phone dock icon +adb shell am broadcast -a com.omixlab.mosis.INJECT_TOUCH \ + --es touch_type "click" --ef x 0.16 --ef y 0.97 +sleep 2 + +# Click back to return home +adb shell am broadcast -a com.omixlab.mosis.INJECT_TOUCH \ + --es touch_type "click" --ef x 0.1 --ef y 0.05 +sleep 2 + +# Click Messages dock icon +adb shell am broadcast -a com.omixlab.mosis.INJECT_TOUCH \ + --es touch_type "click" --ef x 0.39 --ef y 0.97 +sleep 2 + +# Click back to return home +adb shell am broadcast -a com.omixlab.mosis.INJECT_TOUCH \ + --es touch_type "click" --ef x 0.1 --ef y 0.05 +sleep 2 + +# Click Contacts dock icon +adb shell am broadcast -a com.omixlab.mosis.INJECT_TOUCH \ + --es touch_type "click" --ef x 0.61 --ef y 0.97 +sleep 2 + +# Click back to return home +adb shell am broadcast -a com.omixlab.mosis.INJECT_TOUCH \ + --es touch_type "click" --ef x 0.1 --ef y 0.05 +sleep 2 + +# Click Browser dock icon +adb shell am broadcast -a com.omixlab.mosis.INJECT_TOUCH \ + --es touch_type "click" --ef x 0.84 --ef y 0.97 +``` + +## Reading Logs + +```bash +# Filter for Mosis logs +adb logcat -s MosisTest ServiceTester RMLUI + +# Filter for navigation events +adb logcat -d | grep -iE "navigat|loaded|goBack|rml" + +# Save to file +adb logcat -s MosisTest > mosis-log.txt + +# Clear logs +adb logcat -c +``` + +## Expected Log Output + +Successful navigation shows these log patterns: +``` +RMLUI: navigateTo called with: dialer +Loading screen: apps/dialer/dialer.rml +RMLUI: Navigated to: dialer (history depth: 1) + +RMLUI: goBack called (history depth: 1) +Loading screen: apps/home/home.rml +RMLUI: Back to: home +``` diff --git a/docs/APP-MANAGEMENT.md b/docs/APP-MANAGEMENT.md new file mode 100644 index 0000000..78c1668 --- /dev/null +++ b/docs/APP-MANAGEMENT.md @@ -0,0 +1,99 @@ +# App Management System + +The device-side app management system handles installation, updates, and launching of third-party apps. + +## Architecture + +``` +┌─────────────────────────────────────────────────────────────┐ +│ AppManager │ +│ - Install/Uninstall apps from .mosis packages │ +│ - Track installed apps in JSON registry │ +│ - Manage app data/cache directories │ +└─────────────────────────┬───────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────┐ +│ LuaSandboxManager │ +│ - StartApp/StopApp lifecycle │ +│ - Per-app isolated Lua environments │ +│ - Resource limits (memory, CPU, timers) │ +└─────────────────────────────────────────────────────────────┘ +``` + +## App Package Format (.mosis) + +Apps are distributed as ZIP files with `.mosis` extension: + +``` +myapp.mosis +├── manifest.json # Required: app metadata +├── main.rml # Entry point (RmlUi document) +├── styles.rcss # Stylesheets +├── scripts/ # Lua scripts +│ └── app.lua +└── assets/ # Icons, images, etc. +``` + +## Manifest Format + +```json +{ + "id": "com.example.myapp", + "name": "My App", + "version": "1.0.0", + "version_code": 1, + "entry": "main.rml", + "icon": "icon.tga", + "description": "App description", + "developer": { + "name": "Developer Name", + "email": "dev@example.com" + }, + "permissions": [ + "network", + "storage", + "camera" + ], + "min_api_version": 1 +} +``` + +## App Lifecycle + +```cpp +// Install from local file +app_manager->InstallFromFile("/path/to/app.mosis", [](auto progress) { + LOG_INFO("Install progress: %s %.0f%%", + InstallProgress::StageName(progress.stage), + progress.progress * 100); +}); + +// Launch app (starts sandbox) +app_manager->LaunchApp("com.example.myapp"); + +// Check if running +bool running = app_manager->IsAppRunning("com.example.myapp"); + +// Stop app (cleanup sandbox) +app_manager->StopApp("com.example.myapp"); + +// Uninstall (stops if running, removes files) +app_manager->Uninstall("com.example.myapp", false); // keep_data=false +``` + +## Directory Structure + +``` +/data/data/com.omixlab.mosis/files/ +├── apps/ +│ └── com.example.myapp/ +│ ├── package/ # Extracted app files +│ ├── data/ # App persistent data (VirtualFS) +│ ├── cache/ # App cache (clearable) +│ └── db/ # SQLite databases +├── downloads/ # Temporary download location +├── backups/ # App data backups +└── config/ + └── apps.json # Installed apps registry +``` diff --git a/APP_SPECS.md b/docs/APP_SPECS.md similarity index 100% rename from APP_SPECS.md rename to docs/APP_SPECS.md diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md new file mode 100644 index 0000000..f58cff1 --- /dev/null +++ b/docs/ARCHITECTURE.md @@ -0,0 +1,105 @@ +# Architecture Overview + +MosisService is an Android application combining Kotlin UI with native C++ libraries for UI rendering via Android's Binder IPC system. + +## Two Native Libraries + +**mosis-service** (`libmosis-service.so`): +- Main Android Binder service implementation +- Implements `IMosisService.aidl` interface for touch events and initialization +- Contains the Kernel rendering engine with RmlUi integration +- Links against RmlUi for HTML/CSS-like UI rendering + +**mosis-test** (`libmosis-test.so`): +- Test/rendering client implementation +- Implements `IMosisListener.aidl` for receiving callbacks +- OpenGL ES 2.0 rendering pipeline using GLAD + +## IPC Flow + +``` +Kotlin NativeService → JNI → mosis-service (IMosisService) + ↓ + IMosisListener callbacks + ↓ + mosis-test (rendering client) +``` + +## Key Interfaces (AIDL) + +`IMosisService`: `initOS()`, `onTouchDown()`, `onTouchMove()`, `onTouchUp()` + +`IMosisListener` (oneway/async): `onServiceInitialized()`, `onBufferAvailable()`, `onFrameAvailable()` + +## Multi-threading Model + +- Service layer runs in main thread for event handling +- Rendering loop runs in dedicated thread managed by Kernel +- Async task processing for UI updates +- Thread-safe communication between components using std::mutex + +## Rendering Pipeline + +1. **Initialization**: Service connects to test layer, creates EGL context +2. **Buffer Management**: Hardware buffers allocated and shared between layers +3. **Event Processing**: Touch events processed and forwarded to Kernel +4. **Rendering Loop**: Continuous rendering with frame synchronization +5. **UI Updates**: RmlUi engine updates UI based on events and data + +### Data Flow +``` +User Touch Events → IMosisService.onTouch* → Kernel.process → RmlUi UI Updates +Service Initialized → IMosisListener.onServiceInitialized → Rendering Setup +Buffer Available → IMosisListener.onBufferAvailable → Texture Creation +Frame Available → IMosisListener.onFrameAvailable → Frame Rendering +``` + +## Native Code Structure (src/main/cpp/) + +**Core:** +- `kernel.cpp` - Core rendering engine, RmlUi integration, event processing +- `mosis-service.cpp` - Binder service implementation, JNI entry points +- `mosis-test.cpp` - Test client implementation +- `egl_context.cpp` - OpenGL ES context management +- `render_target.cpp` - Framebuffer and buffer management +- `RmlUi_Renderer_GL3.cpp` - RmlUi OpenGL renderer backend +- `assets_manager.cpp` - Android AssetManager integration + +**App Management (`apps/`):** +- `app_manager.cpp` - App install/uninstall/launch lifecycle +- `app_api.cpp` - Lua API bindings for app management +- `update_service.cpp` - Background update checking + +**Lua Sandbox (`sandbox/`):** +- `sandbox_manager.cpp` - Multi-app sandbox orchestrator +- `lua_sandbox.cpp` - Core Lua sandbox with resource limits +- `permission_gate.cpp` - Permission system (normal/dangerous/signature) +- `virtual_fs.cpp` - Per-app virtual filesystem with quotas +- `database_manager.cpp` - SQLite database per app +- `network_manager.cpp` - HTTP request validation +- `websocket_manager.cpp` - WebSocket connections +- `timer_manager.cpp` - setTimeout/setInterval implementation +- `json_api.cpp` - Safe JSON encode/decode +- `crypto_api.cpp` - Cryptographic functions (SHA256, HMAC) +- `camera_interface.cpp` - Camera access with indicators +- `microphone_interface.cpp` - Microphone access with indicators +- `audio_output.cpp` - Audio playback +- `location_interface.cpp` - GPS with precision reduction +- `sensor_interface.cpp` - Accelerometer, gyroscope, etc. +- `bluetooth_interface.cpp` - Bluetooth device access +- `contacts_interface.cpp` - Contacts read/write +- `message_bus.cpp` - Inter-app communication + +## Code Style + +- C++23 standard with modern features (std::span, std::format) +- PascalCase for classes/functions, camelCase for variables +- RAII principles with smart pointers +- Kotlin code follows Android conventions + +## Dependencies + +- vcpkg manages native dependencies (RmlUi, GLFW, Freetype, Lua, libpng, nlohmann-json, minizip, sqlite3) +- CMake build system with vcpkg toolchain integration +- Android target architecture: arm64-v8a only +- Desktop target: Windows x64 (MSVC) diff --git a/docs/BUILD-COMMANDS.md b/docs/BUILD-COMMANDS.md new file mode 100644 index 0000000..f37d2ba --- /dev/null +++ b/docs/BUILD-COMMANDS.md @@ -0,0 +1,86 @@ +# Build Commands + +## Android (Gradle) + +```bash +# Build entire project +./gradlew build + +# Build debug APK +./gradlew assembleDebug + +# Build release APK +./gradlew assembleRelease + +# Clean build outputs +./gradlew clean + +# Build with verbose output +./gradlew build --info --stacktrace + +# Run lint checks +./gradlew lint + +# Run unit tests +./gradlew test + +# Run connected device tests +./gradlew connectedAndroidTest +``` + +## Desktop Designer (CMake) + +```bash +# Configure (from designer/ folder) +cmake -B build -DCMAKE_TOOLCHAIN_FILE=%VCPKG_ROOT%/scripts/buildsystems/vcpkg.cmake + +# Build +cmake --build build --config Debug + +# Run with hot-reload +./build/Debug/mosis-designer.exe ../src/main/assets/apps/home/home.rml + +# Run with logging and hierarchy dump +./build/Debug/mosis-designer.exe ../src/main/assets/apps/home/home.rml --log output.log --hierarchy hierarchy.json +``` + +## Designer Tests (CMake) + +```bash +# Configure (from designer-test/ folder) +cmake -B build -DCMAKE_TOOLCHAIN_FILE=%VCPKG_ROOT%/scripts/buildsystems/vcpkg.cmake + +# Build +cmake --build build --config Debug + +# Run tests +./build/Debug/designer-test.exe +``` + +## Sandbox Security Tests (CMake) + +```bash +# Configure (from sandbox-test/ folder) +cmake -B build -DCMAKE_TOOLCHAIN_FILE=%VCPKG_ROOT%/scripts/buildsystems/vcpkg.cmake + +# Build +cmake --build build --config Debug + +# Run all tests (uber command) +./run_tests.bat + +# Or run directly +./build/Debug/sandbox-test.exe + +# Run specific test +./build/Debug/sandbox-test.exe --test DangerousGlobals +./build/Debug/sandbox-test.exe --test Memory +./build/Debug/sandbox-test.exe --test CPU +``` + +## Environment Requirements + +Required environment variables: +- `ANDROID_HOME` - Android SDK path +- `ANDROID_NDK_HOME` - Android NDK path (version 29.0.14206865) +- `VCPKG_ROOT` - vcpkg package manager root diff --git a/BUILD.md b/docs/BUILD.md similarity index 100% rename from BUILD.md rename to docs/BUILD.md diff --git a/docs/CLAUDE.md b/docs/CLAUDE.md new file mode 100644 index 0000000..97c69f4 --- /dev/null +++ b/docs/CLAUDE.md @@ -0,0 +1,106 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Company + +**OmixLab LTD** - Package namespace: `com.omixlab` + +## Git Commit Guidelines + +**IMPORTANT**: When creating git commits: +- **DO NOT** add yourself as a co-author (no `Co-Authored-By` lines) +- **Commit messages must be a single line** - keep it concise and descriptive + +## Project Overview + +Mosis is a **virtual smartphone OS** for VR games and applications. It provides a phone-like device that users can interact with inside VR environments, with real smartphone functionality. + +### Current Status + +| Component | Status | Notes | +|-----------|--------|-------| +| MosisService | ✅ Working | RmlUi rendering, touch input, navigation | +| App Management | ✅ Working | Install/uninstall apps, sandbox integration | +| Lua Sandbox | ✅ Working | 149 security tests passing | +| Desktop Designer | ✅ Working | Hot-reload, hierarchy dump, recording | +| Designer Tests | ✅ 5/5 Passing | Navigation tests automated | +| MosisVR (Unity) | ✅ Building | OpenGL backend working, Vulkan in progress | +| MosisUnreal | ✅ Working | Vulkan texture import via UE5 RHI, phone actor with mesh | + +### Project Components + +| Component | Location | Purpose | +|-----------|----------|---------| +| Android Service | `src/main/` | Native service running RmlUi renderer | +| App Management | `src/main/cpp/apps/` | App install/uninstall/launch with sandbox | +| Lua Sandbox | `src/main/cpp/sandbox/` | Per-app Lua isolation (22 modules) | +| Desktop Designer | `designer/` | UI development with hot-reload | +| Designer Tests | `designer-test/` | Automated UI testing framework | +| Sandbox Tests | `sandbox-test/` | Lua sandbox security tests (149 tests) | +| UI Assets | `src/main/assets/` | Shared RML/RCSS/Lua assets | + +## Detailed Documentation + +All detailed documentation is in `docs/`: + +| Document | Description | +|----------|-------------| +| [BUILD-COMMANDS.md](BUILD-COMMANDS.md) | Android, Desktop Designer, and test build commands | +| [ARCHITECTURE.md](ARCHITECTURE.md) | Native libraries, IPC flow, code structure | +| [DESKTOP-DESIGNER.md](DESKTOP-DESIGNER.md) | Hot-reload, recording, key files | +| [TESTING-FRAMEWORK.md](TESTING-FRAMEWORK.md) | Automated UI testing, writing tests | +| [UI-ASSETS.md](UI-ASSETS.md) | Asset structure, navigation system, element IDs | +| [MATERIAL-DESIGN.md](MATERIAL-DESIGN.md) | Icons, MDL components, usage guide | +| [ANDROID-TESTING.md](ANDROID-TESTING.md) | ADB commands, event injection, logs | +| [GAME-ENGINES.md](GAME-ENGINES.md) | Unreal & Unity integration, Vulkan import | +| [DEVELOPER-PORTAL.md](DEVELOPER-PORTAL.md) | Portal architecture, milestones | +| [APP-MANAGEMENT.md](APP-MANAGEMENT.md) | Package format, manifest, lifecycle | +| [LUA-SANDBOX.md](LUA-SANDBOX.md) | Security features, permissions, APIs | + +## Quick Reference + +### Environment Requirements + +- `ANDROID_HOME` - Android SDK path +- `ANDROID_NDK_HOME` - Android NDK path (version 29.0.14206865) +- `VCPKG_ROOT` - vcpkg package manager root + +### Common Build Commands + +```bash +# Android APK +./gradlew assembleDebug + +# Desktop Designer (from designer/) +cmake -B build -DCMAKE_TOOLCHAIN_FILE=%VCPKG_ROOT%/scripts/buildsystems/vcpkg.cmake +cmake --build build --config Debug + +# Sandbox Tests (from sandbox-test/) +./run_tests.bat +``` + +### Code Style + +- C++23 standard with modern features (std::span, std::format) +- PascalCase for classes/functions, camelCase for variables +- RAII principles with smart pointers +- Kotlin code follows Android conventions + +### Dependencies + +- vcpkg: RmlUi, GLFW, Freetype, Lua, libpng, nlohmann-json, minizip, sqlite3 +- Android: arm64-v8a only +- Desktop: Windows x64 (MSVC) + +## Documentation Guidelines + +**IMPORTANT**: Always document progress and new commands to avoid rediscovery. + +| Content Type | Location | +|--------------|----------| +| General concepts, architecture | `MosisService/docs/CLAUDE.md` or `docs/` | +| Unreal plugin docs | `MosisUnreal/Plugins/MosisSDK/README.md` | +| Unity package docs | `MosisVR/Packages/com.omixlab.mosis_sdk/README.md` | + +**DO NOT** put documentation in the root `D:\Dev\Mosis\` directory - it is not versioned. diff --git a/docs/DESKTOP-DESIGNER.md b/docs/DESKTOP-DESIGNER.md new file mode 100644 index 0000000..cd6db1a --- /dev/null +++ b/docs/DESKTOP-DESIGNER.md @@ -0,0 +1,40 @@ +# Desktop Designer + +The desktop designer (`designer/`) provides rapid UI development with: + +- **Hot-reload**: Automatically reloads when RML/RCSS/Lua files change +- **UI Hierarchy Dumping**: Exports element tree to JSON for inspection +- **Screenshot Capture**: PNG export via F12 key +- **Logging**: Detailed output for debugging navigation and events +- **Action Recording**: Record mouse/keyboard interactions to JSON +- **Action Playback**: Replay recorded interactions with timing + +## Key Files + +| File | Purpose | +|------|---------| +| `designer/src/main.cpp` | Main entry point, GLFW window, event loop | +| `designer/src/desktop_kernel.cpp` | RmlUi context management, rendering | +| `designer/src/testing/ui_inspector.cpp` | UI hierarchy JSON export | +| `designer/src/testing/visual_capture.cpp` | PNG screenshot capture and comparison | +| `designer/src/testing/action_recorder.cpp` | Record user interactions to JSON | +| `designer/src/testing/action_player.cpp` | Playback recorded actions | +| `designer/src/backend/RmlUi_Backend_GLFW_GL3.cpp` | GLFW backend with input hooks | + +## Command Line Options + +``` +--log Write logs to file +--hierarchy Dump UI hierarchy JSON each frame +--dump Single-shot dump mode (screenshot + hierarchy) +--record Enable recording mode (F5 to start/stop) +--playback Play back recorded actions from JSON +``` + +## Keyboard Controls + +| Key | Function | +|-----|----------| +| F5 | Start/stop recording (when --record is enabled) | +| F6 | Pause/resume playback (when --playback is enabled) | +| F12 | Take screenshot | diff --git a/docs/DEVELOPER-PORTAL.md b/docs/DEVELOPER-PORTAL.md new file mode 100644 index 0000000..7e449d9 --- /dev/null +++ b/docs/DEVELOPER-PORTAL.md @@ -0,0 +1,56 @@ +# Developer Portal + +The Developer Portal is a web application for app developers to publish and manage their Mosis apps. Planning documents are in `DEV_PORTAL_M01-M12.md` files. + +## Architecture Decisions + +| Component | Technology | Rationale | +|-----------|------------|-----------| +| Backend | Go 1.22+ | Simple, fast, single binary deployment | +| Router | Chi | Lightweight, idiomatic Go HTTP routing | +| Database | SQLite + Litestream | Zero-ops, continuous backup to NAS storage | +| Frontend | htmx + Go templates | Server-rendered, minimal JS, fast | +| Storage | Synology NAS filesystem | Self-hosted, local volume mounts | +| Auth | OAuth2 (GitHub/Google) + JWT | golang-jwt for tokens, Ed25519 for signing | +| CLI | Go + Cobra | Cross-platform single binary | +| Docs | Hugo + Docsy | Static site served from /docs/ | + +## Deployment Target + +Self-hosted on Synology NAS via Docker: + +``` +/volume1/mosis/ +├── data/ +│ ├── portal.db # Main SQLite database +│ └── telemetry.db # Separate telemetry database +├── packages/ # Uploaded app packages +│ └── {dev_id}/{app_id}/{version}/ +├── backups/ # Litestream replicas +└── docs/ # Hugo static site output +``` + +## Milestone Documents + +| File | Topic | Status | +|------|-------|--------| +| DEV_PORTAL_M01_OVERVIEW.md | Project overview | Decided | +| DEV_PORTAL_M02_WEB_STACK.md | Go + Chi + htmx | Decided | +| DEV_PORTAL_M03_DATABASE.md | SQLite + Litestream | Decided | +| DEV_PORTAL_M04_AUTH.md | OAuth2 + JWT + Ed25519 | Decided | +| DEV_PORTAL_M05_FRONTEND.md | htmx + Go templates | Decided | +| DEV_PORTAL_M06_API.md | REST API design | Decided | +| DEV_PORTAL_M07_STORAGE.md | NAS filesystem | Decided | +| DEV_PORTAL_M08_TELEMETRY.md | SQLite + background workers | Decided | +| DEV_PORTAL_M09_REVIEW.md | Go validation workers | Decided | +| DEV_PORTAL_M10_DEVICE.md | C++ AppManager | Decided | +| DEV_PORTAL_M11_CLI.md | Go + Cobra CLI | Decided | +| DEV_PORTAL_M12_DOCS.md | Hugo + Docsy | Decided | + +## Key Design Principles + +1. **Single container** - Portal runs as one Docker container on NAS +2. **No external services** - SQLite, local filesystem, Pagefind search +3. **Pure Go** - `modernc.org/sqlite` (no CGO required) +4. **Server-rendered** - htmx for interactivity, no SPA framework +5. **Background workers** - Go goroutines for aggregation/cleanup jobs diff --git a/DEV_PORTAL_M01_APP_PACKAGE.md b/docs/DEV_PORTAL_M01_APP_PACKAGE.md similarity index 100% rename from DEV_PORTAL_M01_APP_PACKAGE.md rename to docs/DEV_PORTAL_M01_APP_PACKAGE.md diff --git a/DEV_PORTAL_M02_WEB_STACK.md b/docs/DEV_PORTAL_M02_WEB_STACK.md similarity index 100% rename from DEV_PORTAL_M02_WEB_STACK.md rename to docs/DEV_PORTAL_M02_WEB_STACK.md diff --git a/DEV_PORTAL_M03_DATABASE.md b/docs/DEV_PORTAL_M03_DATABASE.md similarity index 100% rename from DEV_PORTAL_M03_DATABASE.md rename to docs/DEV_PORTAL_M03_DATABASE.md diff --git a/DEV_PORTAL_M04_AUTH.md b/docs/DEV_PORTAL_M04_AUTH.md similarity index 100% rename from DEV_PORTAL_M04_AUTH.md rename to docs/DEV_PORTAL_M04_AUTH.md diff --git a/DEV_PORTAL_M05_FRONTEND.md b/docs/DEV_PORTAL_M05_FRONTEND.md similarity index 100% rename from DEV_PORTAL_M05_FRONTEND.md rename to docs/DEV_PORTAL_M05_FRONTEND.md diff --git a/DEV_PORTAL_M06_API.md b/docs/DEV_PORTAL_M06_API.md similarity index 100% rename from DEV_PORTAL_M06_API.md rename to docs/DEV_PORTAL_M06_API.md diff --git a/DEV_PORTAL_M07_STORAGE.md b/docs/DEV_PORTAL_M07_STORAGE.md similarity index 100% rename from DEV_PORTAL_M07_STORAGE.md rename to docs/DEV_PORTAL_M07_STORAGE.md diff --git a/DEV_PORTAL_M08_TELEMETRY.md b/docs/DEV_PORTAL_M08_TELEMETRY.md similarity index 100% rename from DEV_PORTAL_M08_TELEMETRY.md rename to docs/DEV_PORTAL_M08_TELEMETRY.md diff --git a/DEV_PORTAL_M09_REVIEW.md b/docs/DEV_PORTAL_M09_REVIEW.md similarity index 100% rename from DEV_PORTAL_M09_REVIEW.md rename to docs/DEV_PORTAL_M09_REVIEW.md diff --git a/DEV_PORTAL_M10_DEVICE.md b/docs/DEV_PORTAL_M10_DEVICE.md similarity index 100% rename from DEV_PORTAL_M10_DEVICE.md rename to docs/DEV_PORTAL_M10_DEVICE.md diff --git a/DEV_PORTAL_M11_CLI.md b/docs/DEV_PORTAL_M11_CLI.md similarity index 100% rename from DEV_PORTAL_M11_CLI.md rename to docs/DEV_PORTAL_M11_CLI.md diff --git a/DEV_PORTAL_M12_DOCS.md b/docs/DEV_PORTAL_M12_DOCS.md similarity index 100% rename from DEV_PORTAL_M12_DOCS.md rename to docs/DEV_PORTAL_M12_DOCS.md diff --git a/DEV_PORTAL_MILESTONES.md b/docs/DEV_PORTAL_MILESTONES.md similarity index 100% rename from DEV_PORTAL_MILESTONES.md rename to docs/DEV_PORTAL_MILESTONES.md diff --git a/docs/GAME-ENGINES.md b/docs/GAME-ENGINES.md new file mode 100644 index 0000000..95f9d18 --- /dev/null +++ b/docs/GAME-ENGINES.md @@ -0,0 +1,348 @@ +# Game Engine Integrations + +MosisService provides a virtual phone that game engines can display and interact with. + +## Integration Architecture + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ MosisService │ +│ (OpenGL ES rendering → AHardwareBuffer) │ +└───────────────────────────┬─────────────────────────────────────┘ + │ Binder IPC + Shared Memory + ┌─────────────────┴─────────────────┐ + ▼ ▼ +┌─────────────────────┐ ┌─────────────────────┐ +│ MosisUnreal │ │ MosisVR │ +│ (UE5.5 Plugin) │ │ (Unity Package) │ +│ Vulkan Import │ │ Vulkan/OpenGL │ +└─────────────────────┘ └─────────────────────┘ +``` + +## MosisUnreal (Unreal Engine 5.5) + +**Location**: `D:\Dev\Mosis\MosisUnreal\Plugins\MosisSDK\` + +### Build Commands + +```batch +:: Windows Editor Build +"D:\Epic\UE_5.5\Engine\Build\BatchFiles\Build.bat" ^ + MosisUnrealEditor Win64 Development ^ + -Project="D:\Dev\Mosis\MosisUnreal\MosisUnreal.uproject" + +:: Android APK Build +"D:\Epic\UE_5.5\Engine\Build\BatchFiles\RunUAT.bat" ^ + BuildCookRun ^ + -project="D:\Dev\Mosis\MosisUnreal\MosisUnreal.uproject" ^ + -platform=Android -clientconfig=Development ^ + -build -cook -stage -pak -package -noP4 + +:: Clean Build (delete these folders first) +rmdir /s /q "Intermediate\Build" +rmdir /s /q "Binaries" +``` + +### Output Files + +| Build | Output | +|-------|--------| +| Windows Editor | `Plugins/MosisSDK/Binaries/Win64/UnrealEditor-MosisSDK.dll` | +| Android APK | `Binaries/Android/MosisUnreal-arm64.apk` | +| Android OBB | `Binaries/Android/main.1.com.omixlab.MosisUnreal.obb` | + +### Requirements + +- Android SDK Platform 36 (for AIDL binder headers) +- Android Build Tools 36.1.0 (for AIDL compiler) +- `ANDROID_HOME` environment variable set + +### Quest Deployment + +**IMPORTANT**: Git Bash on Windows converts Unix paths to Windows paths. Use `MSYS_NO_PATHCONV=1` prefix for all adb push commands. + +```bash +# Set Quest device ID (check with: adb devices -l) +QUEST=2G0YC5ZF7W01T0 + +# Install APK +adb -s $QUEST install -r D:/Dev/Mosis/MosisUnreal/Binaries/Android/MosisUnreal-arm64.apk + +# Create OBB temp directory +adb -s $QUEST shell "mkdir -p /data/local/tmp/obb/com.omixlab.MosisUnreal" + +# Push OBB to temp location (MUST use MSYS_NO_PATHCONV=1) +MSYS_NO_PATHCONV=1 adb -s $QUEST push D:/Dev/Mosis/MosisUnreal/Binaries/Android/main.1.com.omixlab.MosisUnreal.obb /data/local/tmp/obb/com.omixlab.MosisUnreal/ + +# Move OBB to final location +adb -s $QUEST shell "rm -rf /sdcard/Android/obb/com.omixlab.MosisUnreal" +adb -s $QUEST shell "mv /data/local/tmp/obb/com.omixlab.MosisUnreal /sdcard/Android/obb/" + +# Start MosisService first, then MosisUnreal +adb -s $QUEST shell am start -n com.omixlab.mosis/.MainActivity +sleep 3 +adb -s $QUEST shell am start -n com.omixlab.MosisUnreal/com.epicgames.unreal.GameActivity + +# Monitor logs +adb -s $QUEST logcat -s MosisSDK MosisOS MosisTest +``` + +**Full rebuild and deploy sequence**: +```bash +# 1. Build Android APK+OBB +"D:\Epic\UE_5.5\Engine\Build\BatchFiles\RunUAT.bat" BuildCookRun \ + -project="D:\Dev\Mosis\MosisUnreal\MosisUnreal.uproject" \ + -platform=Android -clientconfig=Development \ + -build -cook -stage -pak -package -noP4 + +# 2. Stop app, push APK and OBB, restart +QUEST=2G0YC5ZF7W01T0 +adb -s $QUEST shell am force-stop com.omixlab.MosisUnreal +adb -s $QUEST install -r D:/Dev/Mosis/MosisUnreal/Binaries/Android/MosisUnreal-arm64.apk +adb -s $QUEST shell "mkdir -p /data/local/tmp/obb/com.omixlab.MosisUnreal" +MSYS_NO_PATHCONV=1 adb -s $QUEST push D:/Dev/Mosis/MosisUnreal/Binaries/Android/main.1.com.omixlab.MosisUnreal.obb /data/local/tmp/obb/com.omixlab.MosisUnreal/ +adb -s $QUEST shell "rm -rf /sdcard/Android/obb/com.omixlab.MosisUnreal && mv /data/local/tmp/obb/com.omixlab.MosisUnreal /sdcard/Android/obb/" +adb -s $QUEST shell am start -n com.omixlab.MosisUnreal/com.epicgames.unreal.GameActivity +``` + +**Common issues**: +- App stuck on loading screen: OBB not pushed or wrong version +- `adb push` path errors: Missing `MSYS_NO_PATHCONV=1` prefix +- Service not connecting: Start `com.omixlab.mosis` before the game + +### VR Pointer Interaction + +The MosisSDK includes `UMosisPointerComponent` for VR ray-based touch interaction. + +**Key Files**: +- `Public/MosisPointerComponent.h` - Component header +- `Private/MosisPointerComponent.cpp` - Implementation + +**Features**: +- Raycast from component transform (attach as child of motion controller) +- Automatic detection of `AMosisPhoneActor` hits +- Touch state machine: Down/Move/Up events +- Optional Enhanced Input binding via `TriggerAction` property +- Manual control via `SetTriggerPressed(bool)` +- Debug ray visualization + +**Blueprint Setup**: +1. Add `MosisPointerComponent` as child of `MotionControllerComponent` +2. Set `TriggerAction` to your trigger input action (optional) +3. Touch events are sent automatically when pointing at phone and triggering + +**C++ Setup**: +```cpp +// In VR Pawn header +UPROPERTY(VisibleAnywhere) +UMosisPointerComponent* RightPointer; + +// In constructor +RightPointer = CreateDefaultSubobject(TEXT("RightPointer")); +RightPointer->SetupAttachment(RightMotionController); + +// In BeginPlay (if using Enhanced Input) +RightPointer->TriggerAction = TriggerInputAction; +``` + +**Manual Control (without Enhanced Input)**: +```cpp +// In your input handling code +void AMyVRPawn::OnTriggerPressed() +{ + RightPointer->SetTriggerPressed(true); +} + +void AMyVRPawn::OnTriggerReleased() +{ + RightPointer->SetTriggerPressed(false); +} +``` + +**Component Properties**: + +| Property | Type | Default | Description | +|----------|------|---------|-------------| +| `RayLength` | float | 500.0 | Maximum ray length in cm | +| `bShowDebugRay` | bool | false | Draw debug visualization | +| `DebugRayColor` | FLinearColor | Red | Ray color when not hitting | +| `DebugRayHitColor` | FLinearColor | Green | Ray color when hitting phone | +| `TraceChannel` | ECollisionChannel | Visibility | Collision channel for raycast | +| `TriggerAction` | UInputAction* | nullptr | Enhanced Input action for trigger | + +**Blueprint Functions**: + +| Function | Returns | Description | +|----------|---------|-------------| +| `IsPointingAtPhone()` | bool | True if ray hits a phone actor | +| `GetTargetPhone()` | AMosisPhoneActor* | Currently targeted phone (or nullptr) | +| `GetHitLocation()` | FVector | World location of ray hit | +| `GetRayOrigin()` | FVector | Ray start position | +| `GetRayDirection()` | FVector | Ray direction vector | +| `IsTriggerPressed()` | bool | Current trigger state | +| `SetTriggerPressed(bool)` | void | Manual trigger control | + +**Touch State Machine**: +``` +Idle ──[raycast hit]──► Hover ──[trigger]──► Pressed + ▲ │ │ + │ │ raycast miss │ trigger release + │ ▼ ▼ + └──────────────────── Idle ◄──────────────────── + +Events: +- Idle → Pressed: SendTouch(Down) +- Pressed + moving: SendTouch(Move) +- Pressed → Idle: SendTouch(Up) +``` + +## MosisVR (Unity 6000.3.2f1) + +**Location**: `D:\Dev\Mosis\MosisVR\Packages\com.omixlab.mosis_sdk\` + +### Direct APK Build (Recommended) + +```batch +"C:\Program Files\Unity\Hub\Editor\6000.3.2f1\Editor\Unity.exe" ^ + -batchmode -quit -nographics ^ + -projectPath "D:\Dev\Mosis\MosisVR" ^ + -executeMethod BuildScript.BuildAndroidDirectCI ^ + -outputPath "D:\Dev\Mosis\Builds\Unity\Android\MosisVR.apk" +``` + +### Export + Gradle Build + +For more control, export a Gradle project then build separately: + +```batch +:: Step 1: Export from Unity +"C:\Program Files\Unity\Hub\Editor\6000.3.2f1\Editor\Unity.exe" ^ + -batchmode -quit -nographics ^ + -projectPath "D:\Dev\Mosis\MosisVR" ^ + -executeMethod BuildScript.BuildAndroidCI ^ + -export true ^ + -outputPath "D:\Dev\Mosis\Builds\Unity\Android\MosisVR" + +:: Step 2: Build with Gradle +cd D:\Dev\Mosis\Builds\Unity\Android\MosisVR +gradle assembleRelease +:: APK at: launcher\build\outputs\apk\release\launcher-release.apk +``` + +### Unity Editor Manual Build + +1. File > Build Settings > Android +2. Player Settings: IL2CPP, ARM64, Vulkan + OpenGLES3 +3. For direct APK: Uncheck "Export Project", click Build +4. For export: Check "Export Project", click Export + +### Native Plugin Build (Manual) + +The native plugin builds automatically via CMake during Unity's build. To rebuild manually: + +```batch +cd Packages/com.omixlab.mosis_sdk/Plugins/Android/cpp +cmake -B build -DCMAKE_TOOLCHAIN_FILE=%ANDROID_NDK_HOME%/build/cmake/android.toolchain.cmake ^ + -DANDROID_ABI=arm64-v8a -DANDROID_PLATFORM=android-29 +cmake --build build +``` + +## Device Testing (Both Engines) + +```bash +# Install MosisService first +adb install -r MosisService-debug.apk + +# Install game client +adb install -r MosisUnreal-arm64.apk # or MosisVR.apk + +# Launch service +adb shell am start -n com.omixlab.mosis/.MainActivity + +# Launch client +adb shell am start -n com.omixlab.MosisUnreal/com.epicgames.unreal.GameActivity +# or for Unity: +adb shell am start -n com.omixlab.mosisvr/com.unity3d.player.UnityPlayerActivity + +# Monitor all Mosis logs +adb logcat -s MosisSDK MosisTest RMLUI Vulkan +``` + +## Vulkan HardwareBuffer Import + +Both game engines use Vulkan to import AHardwareBuffer from MosisService. + +### Required Vulkan Extensions + +``` +VK_ANDROID_external_memory_android_hardware_buffer +VK_KHR_external_memory +VK_KHR_dedicated_allocation +``` + +### Import Pattern + +```cpp +// 1. Query buffer properties +VkAndroidHardwareBufferPropertiesANDROID props = { + .sType = VK_STRUCTURE_TYPE_ANDROID_HARDWARE_BUFFER_PROPERTIES_ANDROID +}; +VkAndroidHardwareBufferFormatPropertiesANDROID formatProps = { + .sType = VK_STRUCTURE_TYPE_ANDROID_HARDWARE_BUFFER_FORMAT_PROPERTIES_ANDROID +}; +props.pNext = &formatProps; +vkGetAndroidHardwareBufferPropertiesANDROID(device, buffer, &props); + +// 2. Create image with external memory +VkExternalMemoryImageCreateInfo extInfo = { + .sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO, + .handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID +}; + +AHardwareBuffer_Desc desc; +AHardwareBuffer_describe(buffer, &desc); + +VkImageCreateInfo imageInfo = { + .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, + .pNext = &extInfo, + .imageType = VK_IMAGE_TYPE_2D, + .format = formatProps.format, + .extent = {desc.width, desc.height, 1}, + .mipLevels = 1, + .arrayLayers = 1, + .samples = VK_SAMPLE_COUNT_1_BIT, + .tiling = VK_IMAGE_TILING_OPTIMAL, + .usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT, + .sharingMode = VK_SHARING_MODE_EXCLUSIVE +}; +vkCreateImage(device, &imageInfo, nullptr, &image); + +// 3. Import memory from HardwareBuffer +VkImportAndroidHardwareBufferInfoANDROID importInfo = { + .sType = VK_STRUCTURE_TYPE_IMPORT_ANDROID_HARDWARE_BUFFER_INFO_ANDROID, + .buffer = buffer +}; + +VkMemoryDedicatedAllocateInfo dedicatedInfo = { + .sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO, + .pNext = &importInfo, + .image = image +}; + +VkMemoryAllocateInfo allocInfo = { + .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, + .pNext = &dedicatedInfo, + .allocationSize = props.allocationSize, + .memoryTypeIndex = FindMemoryType(props.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) +}; +vkAllocateMemory(device, &allocInfo, nullptr, &memory); +vkBindImageMemory(device, image, memory, 0); +``` + +### Synchronization + +Currently using CPU synchronization (`vkQueueWaitIdle` / `glFinish`). Future improvement: use Vulkan semaphores for GPU-GPU sync. + +### Double Buffering + +The imported image is copied to a local texture each frame to prevent data races with MosisService rendering. diff --git a/docs/LUA-SANDBOX.md b/docs/LUA-SANDBOX.md new file mode 100644 index 0000000..11b8dc1 --- /dev/null +++ b/docs/LUA-SANDBOX.md @@ -0,0 +1,126 @@ +# Lua Sandbox System + +The sandbox provides secure, isolated Lua environments for third-party apps. + +## Security Features + +| Feature | Implementation | +|---------|----------------| +| Dangerous globals removed | `os`, `io`, `loadfile`, `dofile`, `debug` | +| Memory limits | Configurable per-app (default 10MB) | +| CPU limits | Instruction counting with timeout | +| Bytecode rejected | Only source code allowed | +| Metatables protected | Cannot modify string/table metatables | +| Path traversal blocked | `../` and absolute paths rejected | + +## Permission Categories + +| Category | Auto-Grant | Examples | +|----------|------------|----------| +| Normal | Yes | `storage`, `network` | +| Dangerous | User consent | `camera`, `microphone`, `location`, `contacts` | +| Signature | System apps only | `system_settings`, `install_packages` | + +## Available APIs + +**Core APIs** (always available): +```lua +-- Timers +local id = setTimeout(function() end, 1000) +clearTimeout(id) +local id = setInterval(function() end, 500) +clearInterval(id) + +-- JSON +local obj = json.decode('{"key": "value"}') +local str = json.encode({key = "value"}) + +-- Crypto +local bytes = crypto.randomBytes(16) +local hash = crypto.sha256("data") +local hmac = crypto.hmac("sha256", "key", "data") +``` + +**Storage APIs** (requires `storage` permission): +```lua +-- Virtual filesystem (sandboxed to app directory) +fs.write("data.txt", "content") +local content = fs.read("data.txt") +local files = fs.list("/") +local stat = fs.stat("data.txt") +fs.delete("data.txt") + +-- SQLite database +local db = database.open("mydb") +db:execute("CREATE TABLE IF NOT EXISTS items (id INTEGER PRIMARY KEY, name TEXT)") +db:execute("INSERT INTO items (name) VALUES (?)", {"item1"}) +local rows = db:query("SELECT * FROM items WHERE id = ?", {1}) +``` + +**Network APIs** (requires `network` permission): +```lua +-- HTTP (HTTPS only, private IPs blocked) +local response = http.get("https://api.example.com/data") +local response = http.post("https://api.example.com/data", { + headers = {["Content-Type"] = "application/json"}, + body = json.encode({key = "value"}) +}) + +-- WebSocket +local ws = websocket.connect("wss://example.com/ws") +ws:send("message") +ws:onMessage(function(data) end) +ws:close() +``` + +**Hardware APIs** (requires dangerous permissions + user gesture): +```lua +-- Camera (requires camera permission) +camera.start(function(frame) end) +camera.stop() + +-- Microphone (requires microphone permission) +microphone.start(function(samples) end) +microphone.stop() + +-- Location (requires location permission) +location.getCurrentPosition(function(pos) + print(pos.latitude, pos.longitude) +end) + +-- Sensors (requires sensors permission) +sensors.subscribe("accelerometer", function(data) + print(data.x, data.y, data.z) +end) +``` + +## Running Sandbox Tests + +```bash +cd sandbox-test +cmake -B build -DCMAKE_TOOLCHAIN_FILE=%VCPKG_ROOT%/scripts/buildsystems/vcpkg.cmake +cmake --build build --config Debug +./build/Debug/sandbox-test.exe + +# Output: 149 tests, all passing +``` + +## Test Categories + +| Category | Tests | Description | +|----------|-------|-------------| +| Security | 11 | Globals removal, bytecode, metatables | +| Resources | 8 | Memory, CPU limits, instruction counting | +| Permissions | 7 | Normal/dangerous/signature grants | +| Rate Limiting | 6 | API call throttling | +| Timers | 7 | setTimeout/setInterval behavior | +| JSON | 5 | Encode/decode, depth limits | +| Crypto | 4 | Random, SHA256, HMAC | +| VirtualFS | 8 | Read/write, quotas, traversal | +| Database | 8 | SQLite operations, injection prevention | +| Network | 8 | URL validation, private IP blocking | +| WebSocket | 7 | Connection limits, message size | +| Hardware | 42 | Camera, mic, location, sensors, bluetooth | +| IPC | 7 | Message bus between apps | +| Integration | 9 | Full app lifecycle | +| Fuzzing | 3 | Random input crash testing | diff --git a/docs/MATERIAL-DESIGN.md b/docs/MATERIAL-DESIGN.md new file mode 100644 index 0000000..88ef175 --- /dev/null +++ b/docs/MATERIAL-DESIGN.md @@ -0,0 +1,79 @@ +# Material Design Resources + +Material Design icons and components are available in the MosisDesigner repository. + +## Material Design Icons + +**Location**: `D:\Dev\Mosis\MosisDesigner\material-design-icons` + +A comprehensive icon library from Google with 2000+ icons across 20 categories: + +| Category | Examples | +|----------|----------| +| action | home, search, settings, delete, info | +| alert | error, warning, notification | +| av | play, pause, volume, mic | +| communication | phone, message, email, contacts | +| content | add, remove, copy, paste | +| device | battery, wifi, bluetooth, gps | +| editor | format, text, color, brush | +| file | folder, attachment, download, upload | +| hardware | keyboard, mouse, phone, tablet | +| home | lightbulb, thermostat, security | +| image | camera, photo, filter, tune | +| maps | location, directions, navigation | +| navigation | arrow, chevron, menu, close | +| notification | sync, update, event | +| places | hotel, restaurant, airport | +| search | search variants | +| social | share, person, group, notifications | +| toggle | star, checkbox, radio | + +**Available Formats**: +- `src/` - SVG source files organized by category +- `png/` - PNG files at multiple DPIs (24dp, 36dp, 48dp) +- `font/` - Icon fonts (WOFF, TTF) +- `symbols/` - Material Symbols variable font (newer) +- `variablefont/` - Variable font files + +**Icon Styles**: +- Outlined (default) +- Filled +- Rounded +- Sharp +- Two-tone (Material Icons only) + +## Material Design Lite + +**Location**: `D:\Dev\Mosis\MosisDesigner\material-design-lite` + +CSS/JS component library implementing Material Design (reference implementation): + +| Directory | Contents | +|-----------|----------| +| `src/` | SASS source for components | +| `docs/` | Component documentation | +| `templates/` | Page templates | + +**Key Components** (for design reference): +- Buttons (raised, flat, FAB) +- Cards +- Dialogs +- Lists +- Menus +- Navigation drawers +- Progress indicators +- Sliders +- Snackbars +- Tables +- Tabs +- Text fields +- Tooltips + +## Using Icons in Mosis + +1. **Find icon** at https://fonts.google.com/icons +2. **Export SVG** from `material-design-icons/src///` +3. **Convert to TGA** using image tool (24x24 or 32x32, RGBA) +4. **Place in** `src/main/assets/icons/` +5. **Reference in RML**: `` diff --git a/MILESTONE-2.md b/docs/MILESTONE-2.md similarity index 100% rename from MILESTONE-2.md rename to docs/MILESTONE-2.md diff --git a/MILESTONE-3.md b/docs/MILESTONE-3.md similarity index 100% rename from MILESTONE-3.md rename to docs/MILESTONE-3.md diff --git a/MILESTONE-4.md b/docs/MILESTONE-4.md similarity index 100% rename from MILESTONE-4.md rename to docs/MILESTONE-4.md diff --git a/MILESTONE-5.md b/docs/MILESTONE-5.md similarity index 100% rename from MILESTONE-5.md rename to docs/MILESTONE-5.md diff --git a/MILESTONE-6.md b/docs/MILESTONE-6.md similarity index 100% rename from MILESTONE-6.md rename to docs/MILESTONE-6.md diff --git a/MILESTONE-7.md b/docs/MILESTONE-7.md similarity index 100% rename from MILESTONE-7.md rename to docs/MILESTONE-7.md diff --git a/PLAN.md b/docs/PLAN.md similarity index 100% rename from PLAN.md rename to docs/PLAN.md diff --git a/ROADMAP.md b/docs/ROADMAP.md similarity index 100% rename from ROADMAP.md rename to docs/ROADMAP.md diff --git a/SANDBOX.md b/docs/SANDBOX.md similarity index 100% rename from SANDBOX.md rename to docs/SANDBOX.md diff --git a/SANDBOX_MILESTONES.md b/docs/SANDBOX_MILESTONES.md similarity index 100% rename from SANDBOX_MILESTONES.md rename to docs/SANDBOX_MILESTONES.md diff --git a/SANDBOX_MILESTONE_1.md b/docs/SANDBOX_MILESTONE_1.md similarity index 100% rename from SANDBOX_MILESTONE_1.md rename to docs/SANDBOX_MILESTONE_1.md diff --git a/SANDBOX_MILESTONE_10.md b/docs/SANDBOX_MILESTONE_10.md similarity index 100% rename from SANDBOX_MILESTONE_10.md rename to docs/SANDBOX_MILESTONE_10.md diff --git a/SANDBOX_MILESTONE_11.md b/docs/SANDBOX_MILESTONE_11.md similarity index 100% rename from SANDBOX_MILESTONE_11.md rename to docs/SANDBOX_MILESTONE_11.md diff --git a/SANDBOX_MILESTONE_12.md b/docs/SANDBOX_MILESTONE_12.md similarity index 100% rename from SANDBOX_MILESTONE_12.md rename to docs/SANDBOX_MILESTONE_12.md diff --git a/SANDBOX_MILESTONE_13.md b/docs/SANDBOX_MILESTONE_13.md similarity index 100% rename from SANDBOX_MILESTONE_13.md rename to docs/SANDBOX_MILESTONE_13.md diff --git a/SANDBOX_MILESTONE_14.md b/docs/SANDBOX_MILESTONE_14.md similarity index 100% rename from SANDBOX_MILESTONE_14.md rename to docs/SANDBOX_MILESTONE_14.md diff --git a/SANDBOX_MILESTONE_15.md b/docs/SANDBOX_MILESTONE_15.md similarity index 100% rename from SANDBOX_MILESTONE_15.md rename to docs/SANDBOX_MILESTONE_15.md diff --git a/SANDBOX_MILESTONE_16.md b/docs/SANDBOX_MILESTONE_16.md similarity index 100% rename from SANDBOX_MILESTONE_16.md rename to docs/SANDBOX_MILESTONE_16.md diff --git a/SANDBOX_MILESTONE_17.md b/docs/SANDBOX_MILESTONE_17.md similarity index 100% rename from SANDBOX_MILESTONE_17.md rename to docs/SANDBOX_MILESTONE_17.md diff --git a/SANDBOX_MILESTONE_18.md b/docs/SANDBOX_MILESTONE_18.md similarity index 100% rename from SANDBOX_MILESTONE_18.md rename to docs/SANDBOX_MILESTONE_18.md diff --git a/SANDBOX_MILESTONE_19.md b/docs/SANDBOX_MILESTONE_19.md similarity index 100% rename from SANDBOX_MILESTONE_19.md rename to docs/SANDBOX_MILESTONE_19.md diff --git a/SANDBOX_MILESTONE_2.md b/docs/SANDBOX_MILESTONE_2.md similarity index 100% rename from SANDBOX_MILESTONE_2.md rename to docs/SANDBOX_MILESTONE_2.md diff --git a/SANDBOX_MILESTONE_20.md b/docs/SANDBOX_MILESTONE_20.md similarity index 100% rename from SANDBOX_MILESTONE_20.md rename to docs/SANDBOX_MILESTONE_20.md diff --git a/SANDBOX_MILESTONE_3.md b/docs/SANDBOX_MILESTONE_3.md similarity index 100% rename from SANDBOX_MILESTONE_3.md rename to docs/SANDBOX_MILESTONE_3.md diff --git a/SANDBOX_MILESTONE_4.md b/docs/SANDBOX_MILESTONE_4.md similarity index 100% rename from SANDBOX_MILESTONE_4.md rename to docs/SANDBOX_MILESTONE_4.md diff --git a/SANDBOX_MILESTONE_5.md b/docs/SANDBOX_MILESTONE_5.md similarity index 100% rename from SANDBOX_MILESTONE_5.md rename to docs/SANDBOX_MILESTONE_5.md diff --git a/SANDBOX_MILESTONE_6.md b/docs/SANDBOX_MILESTONE_6.md similarity index 100% rename from SANDBOX_MILESTONE_6.md rename to docs/SANDBOX_MILESTONE_6.md diff --git a/SANDBOX_MILESTONE_7.md b/docs/SANDBOX_MILESTONE_7.md similarity index 100% rename from SANDBOX_MILESTONE_7.md rename to docs/SANDBOX_MILESTONE_7.md diff --git a/SANDBOX_MILESTONE_8.md b/docs/SANDBOX_MILESTONE_8.md similarity index 100% rename from SANDBOX_MILESTONE_8.md rename to docs/SANDBOX_MILESTONE_8.md diff --git a/SANDBOX_MILESTONE_9.md b/docs/SANDBOX_MILESTONE_9.md similarity index 100% rename from SANDBOX_MILESTONE_9.md rename to docs/SANDBOX_MILESTONE_9.md diff --git a/docs/TESTING-FRAMEWORK.md b/docs/TESTING-FRAMEWORK.md new file mode 100644 index 0000000..1681eda --- /dev/null +++ b/docs/TESTING-FRAMEWORK.md @@ -0,0 +1,60 @@ +# Automated Testing Framework + +The designer-test (`designer-test/`) provides automated UI testing. + +## Test Architecture + +1. **WindowController**: Finds designer window, sends mouse/keyboard events via Windows SendInput API +2. **HierarchyReader**: Parses UI hierarchy JSON to find elements by ID/class (with retry logic and exponential backoff) +3. **LogParser**: Monitors log file for navigation events +4. **TestRunner**: Orchestrates test execution, reports results +5. **UIInspector**: Dumps UI hierarchy with atomic writes (temp file + rename pattern) + +## Key Implementation Details + +- **Path Normalization**: RmlUi uses `|` instead of `:` in Windows paths (e.g., `D|\Dev\...`). The UIInspector normalizes paths for correct document matching. +- **Atomic File Writes**: Hierarchy files are written to `.tmp` then renamed to prevent partial reads. +- **Retry with Backoff**: HierarchyReader retries up to 10 times with exponential backoff (30ms base) and validates JSON completeness. +- **Dynamic Back Button**: `GoHome()` finds back buttons from hierarchy by class (`app-bar-nav` or `browser-nav-btn`) instead of fixed coordinates. + +## Writing Tests + +```cpp +// Find element by ID and click it +bool ClickById(TestContext& ctx, const std::string& id) { + ctx.hierarchy.Reload(); + auto element = ctx.hierarchy.FindById(id); + if (!element) return false; + + int x = element->bounds.centerX(); + int y = element->bounds.centerY(); + ScaleToPhysical(ctx, x, y); // Convert logical to physical coords + ctx.window.SendClick(x, y); + return true; +} + +// Test example +bool TestNavigateToDialer(TestContext& ctx) { + GoHome(ctx); + ctx.log.Clear(); + + if (!ClickById(ctx, "dock-phone")) return false; + + std::this_thread::sleep_for(std::chrono::milliseconds(1000)); + ctx.log.Reload(); + return ctx.log.Contains("Loaded screen: apps/dialer/dialer.rml"); +} +``` + +## Test Output + +Tests produce JSON results at `test_results.json`: +```json +{ + "name": "Mosis Designer UI Tests", + "summary": {"passed": 5, "failed": 0, "total": 5}, + "tests": [ + {"name": "Navigate to Dialer", "status": "passed", "duration_ms": 3500} + ] +} +``` diff --git a/TESTING.md b/docs/TESTING.md similarity index 100% rename from TESTING.md rename to docs/TESTING.md diff --git a/docs/UI-ASSETS.md b/docs/UI-ASSETS.md new file mode 100644 index 0000000..57bd648 --- /dev/null +++ b/docs/UI-ASSETS.md @@ -0,0 +1,67 @@ +# UI Assets Structure + +All UI assets are in `src/main/assets/`: + +``` +assets/ +├── apps/ # System apps (RML documents) +│ ├── home/home.rml # Home screen launcher +│ ├── dialer/dialer.rml # Phone dialer +│ ├── messages/ # Messages app +│ ├── contacts/ # Contacts app +│ ├── settings/ # Settings app +│ └── browser/ # Web browser +├── ui/ # Shared stylesheets +│ ├── html.rcss # Base HTML element styles +│ ├── theme.rcss # Design tokens (colors, typography) +│ └── components.rcss # Reusable UI components +├── scripts/ # Lua scripts +│ └── navigation.lua # Navigation system +├── icons/ # TGA icon files (24x24, 32x32) +└── fonts/ # TTF fonts (LatoLatin, Roboto) +``` + +## Navigation System + +Navigation is handled by `scripts/navigation.lua`: + +```lua +-- Navigate to a screen +navigateTo('dialer') -- Push to history, animate forward + +-- Go back +goBack() -- Pop from history, animate back + +-- Go home +goHome() -- Clear history, return to home +``` + +## Element IDs for Testing + +Key elements have IDs for automated testing: + +| ID | Location | Purpose | +|----|----------|---------| +| `dock-phone` | home.rml | Phone dock icon | +| `dock-messages` | home.rml | Messages dock icon | +| `dock-contacts` | home.rml | Contacts dock icon | +| `dock-browser` | home.rml | Browser dock icon | +| `app-settings` | home.rml | Settings app icon | + +## CSS Classes for Navigation + +Back buttons use `app-bar-nav` class for automated GoHome: +```html +
+ +
+``` + +Browser uses `browser-nav-btn` class for its toolbar back button: +```html +
+ +
+``` + +The test framework's `FindBackButton()` searches for both classes to handle all screen layouts. diff --git a/test-apps/com.mosis.sandbox-test.mosis b/test-apps/com.mosis.sandbox-test.mosis new file mode 100644 index 0000000000000000000000000000000000000000..123336c50be16e342f1eb4fb11a294404cdd2510 GIT binary patch literal 20480 zcmeHN$!_Dw8TQ$L-=V@r12kgMl5DGI6x&E|GwonDv7RW3D*| z$RU?J-8@OYzZSA6lP$TY4Ga*tpAioS=p;X@|>}qY{B!Vg~4k4O?~%z?2@O^ zB)eljibArN%jMoK2I-*~GZ`;LUL3GLU4H-F0Soi{rOXakA+wy%#NO`SZjyyOVYw() zi7eO|`|xu$I$foqjI)#_+00)Qv;DnYhL7ZvLeYGsHt+S^tCvp$#J6+&K0E?0#gM}!Ui#P=!;MNt?Nwy-*m zK4LChFap>7Xz<1R<-h;dsA*eVrwas)SnOvF2C)98g_Zm#E*A*O6G{*!ku7_>B_i~w zCVb;k1(ByRs;4dU4w(0Tm8Nkz3xWVoGGB=jv6=4y*IZ&&=b=(VgdowzXjsE+l}ZG$ z?+z+G7ZHZjII;BAAS}8_rO0o1;#<3|wdAIislR{E1{H3U&7N`(W`);d4VWS<4I>eV zndUw}V@C~P3lqWf(g81mF*QF9*Z@trbi{EMG0P=1-o|pyF5VcwWmd=>A@Z#QU^R7u^q{`clpQo~;Z`cj zNS7Q9fwm=pdPSnR(MEqjq&?3D+H^2@7G_b65>1AK^g|WTlrID^Q`UM1)i_dPMH9zE zXD|g3Fn|C?Vg?6ng0CS%+amx~_oE-C;FWqJ|Eb8*KwIOJ2`0|enhZ_7KPgb&>M4V? zAvByKacj9kX3A?51{lrDraVqW1eC}`6D9g-;fh2BS8L1Bu4&TJHT~$3d|mO5rM(ux~R8tk;N zI(9VeR*lWOR?wV6rc`hmpswSG)vFvWtKnhc)}3jW|CNfl$sxFO08fx2HjSKh4O#G7 zt?*41t7n>PR__bi+H-~RwpE)W9kKOGN0T6D2_|QfI4rdhtg2Eu#Z zAKrm09FrV>-{C>h*vGX1>iq(|Yp!6&*?KBAl=0=em&22hMy@p@HKcQ1%qv8TIR~Tf z%|()A>^94j$XzEOOVQU`6~Um;)M^caL^vk=&?1k9dM{^MIx*0v4gif*sj>zFO`3Al zHr6qq>85hDeeK=1FJEs5$AX6y9CJMRo>qu(dW8rPZ~C>s)($)1b#REg1spQLLIbiV zfrjke4w%-gaQcX@vTZw+?hmR5Jfc@U(DA5lb*axGP3_7|HTNrXe))V2>o$*NZI_`2 zzQ3kwE&}!7tNleF?^OQ0Qw287(d7#CARNES4H-gXx$CK-0WIDJik?H(}6LJ?|mZ@!AxUk>;qV z!$57J6$+X(aFEGt5f|v);oR(wX!>I@;EPiO@?E#wv(vrN6E&%PoG_;YqCne ziM)Jh$dE|TSz;>~G^s5K{nVN!C298xZNLBR4@WQh z)&1{qMEAcZgYN$K^MJoW$|+)hzPEci#}bHd>UgIzmWeobuZ2$!^_U?@;`Exy`z21> zQryYIJS+;2&2uq5^9p&7^)66DhgM<{=J8UpA`j0zuHQI#=ck8ynA9H{{il;Gy0=_W ze8a*7*IbIJH{rQwrj(fvr+X>CVH275V(bh&hTGf(S7T@1EPKVnYk%KmS0aifPpt~Y zbN}ooMD)+fTv%<$)1Fd2^H6jdvjsUZVaZNjQr+FT^bL*amnC}_o`cCpk z+)!C{L~TDxY8)YCbY*MTrStDH#6{oiHF8Z>QsN4~#FB2wlp=yt-P-9p;L}4*>Ecv# zn(X*g&(t-@M#tQX73CM%Dx$63(wCCnIVn;%mrv@GIWTSXs(wWJ6u-14ikI?CKR&5% zb~m%pxwZYvr+8;{mF}76C-v{We&5xYSya4KX7(l2`D!!?z}!jnBr-$467lGAorv6RftfM~0-7BJ{h(hTzM@wO zm>a0KC?4(!@_Qn+J5NKcmR}6hcOYln3`at55^&%kCTb{!y)t1hf4FeoQJY>;e_36M zxe-aW6gh;LS3jjk-;&EX@$iKvX3P^XE+Ku9xcQ@o1i#eHz|YzZh{A;dor4O{{Z+5) z*!-v$`E5^TT#zmZ7@nw8ffreL$cy07);rrEG&UWor%W4y_q5g5>d{Wi_ z@sR3&_@dkYx54>Y$7xxy6*0v%UT?}5adJOq-*B1Xzc~Ga&TTbW;HChTJc^cPbb@{L zEXVuzs265QmXF!9!I1vUyd9jX4+hl>(g0@w+Rj_WQCut&4yDsX+*y72*^A;_sDDB- z#{Ji=nZbR<3|o9D7AXD@{Y2zuW-j8{9JgdAA2(18@Ml<-Jc{t072?yG}E6LAHC5^-yQpJYj7fW<$jn#E}^5mN-jWQ;bz zcx%O$5vUX4Tw^I3e4+@JF*RY!PdcK^2KNx+E|#gl}WG$H%oF-YGqJ6?e`7L+V?R_v(%>a8XT1o6&moFnSht> znA+B9P>!}oR%XkwTQl03joBDZzeF#nBQWMDa_oxRf%uY-UI=cvo|(P1Wl-U;jcbvm z84mEFg~N?tl^G3S_apiTW39m6&e4r}O6iy_bJ4rax$Tfu)TUDvHDuan?cklT z4uKAV4uKAV4uKAV4uKAV4uKAV4uKAV4uKAV4uKAV4uKAV4uKAV4uKAV4uKAV4uKAV c4uKAV4uKAV4uKAV4uKAV4uKAV|3?J=2ja8dG5`Po literal 0 HcmV?d00001 diff --git a/test-apps/com.mosis.sandbox-test/app.lua b/test-apps/com.mosis.sandbox-test/app.lua new file mode 100644 index 0000000..4281682 --- /dev/null +++ b/test-apps/com.mosis.sandbox-test/app.lua @@ -0,0 +1,195 @@ +-- Sandbox Test App +-- Tests: timers, JSON, crypto, storage + +local results = {} + +local function log(msg) + table.insert(results, os.date("%H:%M:%S") .. " " .. msg) + local el = document:GetElementById("results") + if el then + el.inner_rml = table.concat(results, "\n") + end +end + +local function setStatus(id, status, success) + local el = document:GetElementById(id) + if el then + if success then + el.inner_rml = "✓ " .. status + else + el.inner_rml = "✗ " .. status + end + end +end + +-- Timer test +function testTimer() + setStatus("timer-status", "Running...", true) + log("Starting timer test...") + + local count = 0 + local timerId = nil + + timerId = setInterval(function() + count = count + 1 + log("Timer tick: " .. count) + + if count >= 3 then + clearInterval(timerId) + setStatus("timer-status", "Passed (3 ticks)", true) + log("Timer test complete!") + end + end, 1000) + + log("Timer started with ID: " .. tostring(timerId)) +end + +-- JSON test +function testJSON() + log("Starting JSON test...") + + local success = true + local msg = "" + + -- Test encode + local data = { + name = "test", + value = 42, + nested = { a = 1, b = 2 } + } + + local encoded = json.encode(data) + if encoded then + log("Encoded: " .. encoded) + else + success = false + msg = "encode failed" + end + + -- Test decode + if success then + local decoded = json.decode(encoded) + if decoded and decoded.name == "test" and decoded.value == 42 then + log("Decoded successfully, name=" .. decoded.name) + else + success = false + msg = "decode failed" + end + end + + if success then + setStatus("json-status", "Passed", true) + log("JSON test complete!") + else + setStatus("json-status", "Failed: " .. msg, false) + end +end + +-- Crypto test +function testCrypto() + log("Starting crypto test...") + + local success = true + local msg = "" + + -- Test random bytes + local bytes = crypto.randomBytes(16) + if bytes and #bytes == 16 then + log("Random bytes (hex): " .. bytes:gsub(".", function(c) + return string.format("%02x", c:byte()) + end)) + else + success = false + msg = "randomBytes failed" + end + + -- Test SHA256 + if success then + local hash = crypto.sha256("hello world") + if hash then + log("SHA256: " .. hash:sub(1, 32) .. "...") + else + success = false + msg = "sha256 failed" + end + end + + -- Test HMAC + if success then + local hmac = crypto.hmac("sha256", "secret", "message") + if hmac then + log("HMAC: " .. hmac:sub(1, 32) .. "...") + else + success = false + msg = "hmac failed" + end + end + + if success then + setStatus("crypto-status", "Passed", true) + log("Crypto test complete!") + else + setStatus("crypto-status", "Failed: " .. msg, false) + end +end + +-- Storage test +function testStorage() + log("Starting storage test...") + + local success = true + local msg = "" + + -- Test write + local writeOk = fs.write("test.txt", "Hello from sandbox!") + if writeOk then + log("Write successful") + else + success = false + msg = "write failed" + end + + -- Test read + if success then + local content = fs.read("test.txt") + if content == "Hello from sandbox!" then + log("Read successful: " .. content) + else + success = false + msg = "read mismatch" + end + end + + -- Test list + if success then + local files = fs.list("/") + if files then + log("Files in root: " .. #files) + for _, f in ipairs(files) do + log(" - " .. f) + end + end + end + + -- Test delete + if success then + local deleteOk = fs.delete("test.txt") + if deleteOk then + log("Delete successful") + else + success = false + msg = "delete failed" + end + end + + if success then + setStatus("storage-status", "Passed", true) + log("Storage test complete!") + else + setStatus("storage-status", "Failed: " .. msg, false) + end +end + +-- Initialize +log("Sandbox Test App loaded") +log("Lua version: " .. (_VERSION or "unknown")) diff --git a/test-apps/com.mosis.sandbox-test/main.rml b/test-apps/com.mosis.sandbox-test/main.rml new file mode 100644 index 0000000..e14c55d --- /dev/null +++ b/test-apps/com.mosis.sandbox-test/main.rml @@ -0,0 +1,46 @@ + + + Sandbox Test + + + + +
+
+ +
+
Sandbox Test
+
+ +
+
+
Timer Test
+
Not started
+ +
+ +
+
JSON Test
+
Not tested
+ +
+ +
+
Crypto Test
+
Not tested
+ +
+ +
+
Storage Test
+
Not tested
+ +
+ +
+
Results
+
Click buttons above to run tests
+
+
+ +
diff --git a/test-apps/com.mosis.sandbox-test/manifest.json b/test-apps/com.mosis.sandbox-test/manifest.json new file mode 100644 index 0000000..33c82ba --- /dev/null +++ b/test-apps/com.mosis.sandbox-test/manifest.json @@ -0,0 +1,18 @@ +{ + "id": "com.mosis.sandbox-test", + "name": "Sandbox Test", + "version": "1.0.0", + "version_code": 1, + "entry": "main.rml", + "icon": "icon.tga", + "description": "Tests sandbox APIs: timers, storage, JSON, crypto", + "developer": { + "name": "Mosis Team", + "email": "dev@mosis.dev" + }, + "permissions": [ + "storage", + "network" + ], + "min_api_version": 1 +} diff --git a/test-apps/com.mosis.sandbox-test/styles.rcss b/test-apps/com.mosis.sandbox-test/styles.rcss new file mode 100644 index 0000000..7e41fcc --- /dev/null +++ b/test-apps/com.mosis.sandbox-test/styles.rcss @@ -0,0 +1,85 @@ +body { + font-family: LatoLatin; + font-size: 16dp; + background-color: #121212; + color: #ffffff; +} + +.app-bar { + display: flex; + flex-direction: row; + align-items: center; + height: 56dp; + background-color: #1e1e1e; + padding: 0 8dp; +} + +.app-bar-nav { + width: 40dp; + height: 40dp; + display: flex; + align-items: center; + justify-content: center; + border-radius: 20dp; +} + +.app-bar-nav:hover { + background-color: #333333; +} + +.icon { + font-size: 24dp; +} + +.app-bar-title { + font-size: 20dp; + font-weight: bold; + margin-left: 16dp; +} + +.content { + padding: 16dp; +} + +.card { + background-color: #1e1e1e; + border-radius: 12dp; + padding: 16dp; + margin-bottom: 12dp; +} + +.card-title { + font-size: 18dp; + font-weight: bold; + margin-bottom: 8dp; + color: #bb86fc; +} + +button { + background-color: #bb86fc; + color: #000000; + border: none; + border-radius: 8dp; + padding: 12dp 24dp; + font-size: 14dp; + font-weight: bold; + margin-top: 8dp; +} + +button:hover { + background-color: #cf9fff; +} + +button:active { + background-color: #9a67ea; +} + +#results { + font-family: monospace; + font-size: 12dp; + background-color: #0d0d0d; + padding: 12dp; + border-radius: 8dp; + white-space: pre-wrap; + color: #00ff00; +} diff --git a/test-apps/package.bat b/test-apps/package.bat new file mode 100644 index 0000000..94c90ce --- /dev/null +++ b/test-apps/package.bat @@ -0,0 +1,21 @@ +@echo off +REM Package test apps as .mosis files + +setlocal enabledelayedexpansion + +for /d %%d in (*) do ( + if exist "%%d\manifest.json" ( + echo Packaging %%d... + cd %%d + if exist "..\%%d.mosis" del "..\%%d.mosis" + tar -a -cf "..\%%d.mosis" * + cd .. + echo Created %%d.mosis + ) +) + +echo. +echo Done! Package files: +dir /b *.mosis 2>nul + +endlocal