diff --git a/docs/APP-DISCOVERY.md b/docs/APP-DISCOVERY.md new file mode 100644 index 0000000..dc91600 --- /dev/null +++ b/docs/APP-DISCOVERY.md @@ -0,0 +1,184 @@ +# App Discovery System + +Local app discovery without requiring a backend server. + +## Status + +| Feature | Status | +|---------|--------| +| Directory scanning | β Implemented | +| Manifest parsing | β Implemented | +| Home screen rendering | β Implemented | +| App launching | π In Progress | +| Package installation | β³ Planned | + +## Overview + +``` +βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ +β Home Screen β +β βββββββββββ βββββββββββ βββββββββββ βββββββββββ β +β β Dialer β β Messagesβ β TestApp β β MyApp β β scanned β +β βββββββββββ βββββββββββ βββββββββββ βββββββββββ from β +β apps/ β +βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ + β + βΌ +βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ +β /data/data/com.omixlab.mosis/files/ β +β βββ apps/ β Installed third-party apps β +β β βββ com.mosis.testapp/ β +β β β βββ manifest.json β App metadata β +β β β βββ main.rml β Entry point β +β β β βββ app.lua β +β β β βββ icon.tga β +β β βββ com.example.myapp/ β +β β βββ ... β +β βββ downloads/ β Pending .mosis packages β +β βββ config/ β +β βββ apps.json β App registry (optional cache) β +βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ +``` + +## Discovery Flow + +### Phase 1: Direct Folder Discovery (MVP) β + +1. **On boot**: Home screen calls `mosis.apps.getInstalled()` +2. **AppManager** scans `/files/apps/` for folders containing `manifest.json` +3. **For each app**: Read manifest, extract name/icon/entry point +4. **Home screen**: Render app grid with icons +5. **On tap**: Launch app via `mosis.apps.launch(package_id)` + +### Phase 2: Package Installation (Future) + +1. User copies `.mosis` file to `/files/downloads/` +2. Store app or file manager shows pending packages +3. User taps "Install" β `mosis.apps.install(path)` +4. Package extracted to `/files/apps/{package_id}/` +5. Home screen refreshes to show new app + +## Implementation + +### AppManager (C++) + +The `AppManager::ScanAppsDirectory()` method scans for installed apps: + +```cpp +// app_manager.cpp +void AppManager::ScanAppsDirectory() { + std::string apps_dir = m_data_root + "/apps"; + for (const auto& entry : fs::directory_iterator(apps_dir)) { + if (!entry.is_directory()) continue; + + // Look for manifest.json + std::string manifest_path = entry.path().string() + "/manifest.json"; + // Parse and register app... + } +} +``` + +### Lua API + +```lua +-- Get all installed apps (system + third-party) +local apps = mosis.apps.getInstalled() +-- Returns: [{package_id, name, icon_path, entry, version, is_system_app}, ...] + +-- Launch an app +mosis.apps.launch("com.mosis.testapp") + +-- Check if app is running +local running = mosis.apps.isRunning("com.mosis.testapp") +``` + +### Home Screen (home.lua) + +The home screen dynamically renders third-party apps: + +```lua +function initHome(doc) + -- Get installed apps, filter to third-party only + local apps = mosis.apps.getInstalled() + for _, app in ipairs(apps) do + if not app.is_system_app then + -- Render app icon + end + end +end + +function launchThirdPartyApp(package_id) + mosis.apps.launch(package_id) +end +``` + +## Test App Deployment + +To deploy a test app manually: + +```bash +# 1. Push to temp location (avoid permission issues) +adb push test-apps/com.mosis.sandbox-test/ //data/local/tmp/com.mosis.sandbox-test/ + +# 2. Copy to app's private storage +adb shell "run-as com.omixlab.mosis cp -r /data/local/tmp/com.mosis.sandbox-test /data/data/com.omixlab.mosis/files/apps/" + +# 3. Verify +adb shell "run-as com.omixlab.mosis ls /data/data/com.omixlab.mosis/files/apps/com.mosis.sandbox-test/" +``` + +For Desktop Designer: +```bash +# Copy to designer's sandbox_data folder +cp -r test-apps/com.mosis.sandbox-test/ designer/build/Debug/sandbox_data/apps/ +``` + +## App Manifest Format + +```json +{ + "id": "com.mosis.testapp", + "name": "Test App", + "version": "1.0.0", + "version_code": 1, + "entry": "main.rml", + "icon": "icon.tga", + "description": "A test application", + "permissions": ["storage", "network"] +} +``` + +## Directory Structure + +### System Apps (bundled in APK assets) +``` +assets/apps/ +βββ home/ # Home screen (always loaded) +βββ dialer/ +βββ messages/ +βββ contacts/ +βββ settings/ +βββ browser/ +βββ store/ +``` + +### Third-Party Apps (in private storage) +``` +/data/data/com.omixlab.mosis/files/apps/ +βββ com.mosis.testapp/ +β βββ manifest.json +β βββ main.rml +β βββ app.lua +β βββ styles.rcss +β βββ icon.tga +βββ com.example.game/ + βββ ... +``` + +## Future Enhancements + +1. **Package signature verification** before installation +2. **Version tracking** in apps.json registry +3. **Update detection** by comparing version_code +4. **Uninstall** with data cleanup option +5. **Store integration** for HTTP-based discovery diff --git a/src/main/assets/apps/home/home.lua b/src/main/assets/apps/home/home.lua new file mode 100644 index 0000000..dca426b --- /dev/null +++ b/src/main/assets/apps/home/home.lua @@ -0,0 +1,151 @@ +-- home.lua - Home screen dynamic app rendering +-- Handles system apps and discovered third-party apps + +-- System apps with their navigation keys and colors +local system_apps = { + -- Row 1 + {name = "Phone", icon = "phone", color = "#4CAF50", nav = "dialer"}, + {name = "Messages", icon = "message", color = "#2196F3", nav = "messages"}, + {name = "Contacts", icon = "contacts", color = "#FF9800", nav = "contacts"}, + {name = "Browser", icon = "browser", color = "#F44336", nav = "browser"}, + -- Row 2 + {name = "Gallery", icon = "gallery", color = "#9C27B0", nav = nil}, + {name = "Camera", icon = "camera", color = "#00BCD4", nav = "camera"}, + {name = "Settings", icon = "settings", color = "#607D8B", nav = "settings"}, + {name = "Music", icon = "music", color = "#E91E63", nav = "music"}, + -- Row 3 + {name = "Calendar", icon = "calendar", color = "#3F51B5", nav = nil}, + {name = "Clock", icon = "clock", color = "#009688", nav = nil}, + {name = "Notes", icon = "notes", color = "#795548", nav = nil}, + {name = "Maps", icon = "maps", color = "#FF5722", nav = nil}, + -- Row 4 + {name = "Store", icon = "store", color = "#8BC34A", nav = "store"}, + {name = "Files", icon = "files", color = "#CDDC39", nav = nil}, + {name = "Calculator", icon = "calculator", color = "#FFC107", nav = nil}, + {name = "Weather", icon = "weather", color = "#673AB7", nav = nil}, +} + +-- State +local installed_apps = {} +local home_document = nil -- Store document reference + +-- Initialize on load (receives document from onload event) +function initHome(doc) + print("[Home] Initializing home screen...") + home_document = doc + + -- Get installed third-party apps + if mosis and mosis.apps then + installed_apps = mosis.apps.getInstalled() or {} + print("[Home] Found " .. #installed_apps .. " installed apps") + + -- Filter to only third-party (non-system) apps + local third_party = {} + for _, app in ipairs(installed_apps) do + if not app.is_system_app then + table.insert(third_party, app) + print("[Home] Third-party app: " .. app.name .. " (" .. app.package_id .. ")") + end + end + installed_apps = third_party + else + print("[Home] Warning: mosis.apps API not available") + installed_apps = {} + end + + -- Render dynamic apps + renderThirdPartyApps() +end + +-- Generate a color based on package_id +function getAppColor(package_id) + local colors = { + "#BB86FC", "#03DAC6", "#FF9800", "#2196F3", + "#4CAF50", "#F44336", "#E91E63", "#3F51B5", + "#009688", "#795548", "#FF5722", "#673AB7" + } + + -- Simple hash of package_id to pick a color + local hash = 0 + for i = 1, #package_id do + hash = hash + package_id:byte(i) + end + + return colors[(hash % #colors) + 1] +end + +-- Get first letter for placeholder icon +function getAppInitial(name) + return name:sub(1, 1):upper() +end + +-- Render third-party apps into the grid +function renderThirdPartyApps() + -- Use stored document reference + if not home_document then + print("[Home] Could not get document reference") + return + end + + local grid = home_document:GetElementById("third-party-apps") + if not grid then + print("[Home] third-party-apps container not found") + return + end + + -- Clear existing content + grid.inner_rml = "" + + if #installed_apps == 0 then + print("[Home] No third-party apps to display") + return + end + + -- Build HTML for each app + local html = "" + for _, app in ipairs(installed_apps) do + local color = getAppColor(app.package_id) + local initial = getAppInitial(app.name) + local icon_html + + -- Check if app has an icon + if app.icon and app.icon ~= "" then + -- Third-party app icon path would be in their install directory + -- For now, use initial as we need file:// protocol support + icon_html = '' .. initial .. '' + else + icon_html = '' .. initial .. '' + end + + html = html .. [[ +
+ ]] + end + + grid.inner_rml = html + print("[Home] Rendered " .. #installed_apps .. " third-party apps") +end + +-- Launch a third-party app +function launchThirdPartyApp(package_id) + print("[Home] Launching app: " .. package_id) + + if mosis and mosis.apps then + local success = mosis.apps.launch(package_id) + if success then + print("[Home] App launched: " .. package_id) + else + print("[Home] Failed to launch app: " .. package_id) + end + else + print("[Home] Cannot launch app: mosis.apps not available") + end +end + +-- initHome() is called via onload in home.rml diff --git a/src/main/assets/apps/home/home.rml b/src/main/assets/apps/home/home.rml index ca70240..b42c94b 100644 --- a/src/main/assets/apps/home/home.rml +++ b/src/main/assets/apps/home/home.rml @@ -4,6 +4,7 @@ +