# Quest App Launcher — Documentation ## Table of Contents 1. [Overview](#overview) 2. [Architecture](#architecture) 3. [Configuration](#configuration) 4. [App Name Overrides](#app-name-overrides) 5. [Custom Icons](#custom-icons) 6. [Tab / Category System](#tab--category-system) 7. [Asset Auto-Update](#asset-auto-update) 8. [Managed Policies](#managed-policies) 9. [On-Device File Locations](#on-device-file-locations) --- ## Overview Quest App Launcher is a VR home-screen replacement for Meta Quest 2. It lists all installed non-system apps in a scrollable grid, organized into tabs. Apps can be launched by pointing and clicking with the controller. --- ## Architecture ### Data Flow 1. **AppInfo.java** (Android plugin) — queries the Android package manager for installed apps, decodes icons as bitmaps, and reads usage statistics via `UsageStatsManager`. 2. **AppProcessor.cs** — calls the Java plugin over JNI, applies name overrides from `appnames*.json`, filters excluded packages, assigns apps to auto tabs (Quest / 2D), and returns a `ProcessedApp` dictionary. 3. **GridPopulation.cs** — takes the processed app list, sorts it (A–Z or most-recently-used), builds tab containers, and instantiates `AppEntry` prefab tiles in the grid. 4. **AppEntry.cs** — represents a single icon tile. Lazy-loads and unloads its texture based on scroll position to keep memory use bounded. ### Key Scripts | File | Role | |------|------| | `Assets/Scripts/AppProcessor.cs` | App discovery, name overrides, exclusion lists, icon loading | | `Assets/Scripts/GridPopulation.cs` | Grid layout, tab assignment, sort order | | `Assets/Scripts/AppEntry.cs` | Per-tile icon loading / unloading | | `Assets/Scripts/Config.cs` | Settings model and JSON persistence | | `Assets/Scripts/AssetsDownloader.cs` | Downloads icon packs and app name files from GitHub | | `Assets/Scripts/SkyboxHandler.cs` | Background skybox selection and loading | | `Assets/Scripts/SettingsHandler.cs` | Settings UI wiring | | `Assets/Scripts/GlobalState.cs` | Cross-scene singleton (DontDestroyOnLoad) | | `Assets/Plugins/Android/AppInfo.java` | Android interop layer | --- ## Configuration Settings are stored in `config.json` in the app's persistent data path on-device. The file is created with defaults on first run. You can push a custom `config.json` via `adb`: ``` adb push config.json /sdcard/Android/data/dev.oxmc.QuestAppLauncher/files/ ``` ### config.json fields | Field | Type | Default | Description | |-------|------|---------|-------------| | `gridSize.rows` | int | 3 | Number of rows in the grid | | `gridSize.cols` | int | 3 | Number of columns in the grid | | `sortMode` | string | `"az"` | Sort order: `"az"` (alphabetical) or `"mostRecent"` | | `show2D` | bool | `true` | Whether to show 2D (non-VR) apps | | `autoCategory` | string | `"top"` | Where auto tabs appear: `"top"`, `"left"`, `"right"`, or `"off"` | | `customCategory` | string | `"right"` | Where custom tabs appear: same options as above | | `autoUpdate` | bool | `true` | Automatically check for and download asset updates | | `background` | string | `"default"` | Background image path, or `"default"` for the built-in skybox | | `downloadRepos` | array | GitHub default | List of repos to pull icon packs and app names from | | `managedPolicyEndpoint` | string | `""` | URL to fetch managed policy JSON from (IT/MDM use) | ### Example config.json ```json { "gridSize": { "rows": 3, "cols": 4 }, "sortMode": "az", "show2D": true, "autoCategory": "top", "customCategory": "right", "autoUpdate": true, "background": "default", "downloadRepos": [ { "repoUri": "hooverhigh/QuestAppLauncher_Assets/releases/latest", "type": "github" } ] } ``` --- ## App Name Overrides Place `appnames.json` (or any `appnames*.json` file) in the persistent data path to rename apps and assign them to custom tabs. ### Format (`appnames.json`) ```json { "com.example.myapp": { "Name": "My App", "Category": "Games", "Category2": "Favorites" } } ``` - **Name** — display name override (leave blank to keep the original name) - **Category** / **Category2** — optional tab names (up to two per app) ### Pushing to the device ``` adb push appnames.json /sdcard/Android/data/dev.oxmc.QuestAppLauncher/files/ ``` --- ## Custom Icons Place `.jpg` files named `.jpg` in the persistent data path, or bundle them into a zip file named `iconpack*.zip`. ### Individual icons ``` adb push com.example.myapp.jpg /sdcard/Android/data/dev.oxmc.QuestAppLauncher/files/ ``` ### Icon packs (zip) Create a zip containing `.jpg` files at the root level: ``` iconpack_custom.zip com.example.myapp.jpg com.example.otherapp.jpg ``` ``` adb push iconpack_custom.zip /sdcard/Android/data/dev.oxmc.QuestAppLauncher/files/ ``` The launcher extracts icon packs automatically on startup. Multiple icon packs are supported and applied in alphabetical order (later packs override earlier ones). --- ## Tab / Category System ### Auto tabs Apps are automatically assigned to one of two built-in tabs based on their package metadata: | Tab | Criteria | |-----|----------| | **Quest** | App declares the `android.hardware.vr.headtracking` feature, or any other VR app | | **2D** | App has no VR features | The **All** tab always shows every app regardless of category. Auto tabs can be placed at the top, left, or right of the panel, or disabled entirely via `autoCategory` in `config.json`. ### Custom tabs Assign apps to custom tabs using the `Category` / `Category2` fields in `appnames.json`. Custom tabs appear sorted alphabetically. Their position is controlled by `customCategory` in `config.json`. --- ## Asset Auto-Update When `autoUpdate` is `true`, the launcher checks configured repos for updated icon packs and app name files on startup (rate-limited to once every 5 minutes). If updates are found, they are downloaded to the `download_cache` folder inside the persistent data path and the scene is reloaded. ### Adding a custom HTTP repo In addition to GitHub releases, you can host assets on any plain HTTP server by providing a manifest JSON: ```json { "files": [ { "name": "iconpack_school.zip", "url": "https://yourserver.com/iconpack_school.zip", "updatedAt": "2025-01-01T00:00:00Z" }, { "name": "appnames.json", "url": "https://yourserver.com/appnames.json", "updatedAt": "2025-01-01T00:00:00Z" } ] } ``` Add it to `downloadRepos` in `config.json`: ```json { "repoUri": "https://yourserver.com/manifest.json", "type": "http" } ``` --- ## Managed Policies For IT/MDM deployments, the launcher can fetch a policy JSON from a URL on every startup. Set `managedPolicyEndpoint` in `config.json` (or define `DEV_ENDPOINT` at compile time). Supported policy fields: | Field | Type | Description | |-------|------|-------------| | `hiddenApps` | string[] | Package names to hide from the launcher | | `appNames` | object | Same format as `appnames.json` — name/category overrides | | `disableSettings` | bool | Hides the settings button | | `wallpaper` | string | URL to a background image (overrides user setting) | --- ## On-Device File Locations All user-editable files live in the app's persistent data path: ``` /sdcard/Android/data/dev.oxmc.QuestAppLauncher/files/ ``` | File / Folder | Purpose | |---------------|---------| | `config.json` | App settings | | `appnames*.json` | App name and category overrides | | `iconpack*.zip` | Custom icon packs | | `*.jpg` | Individual icon overrides (`.jpg`) | | `excludedpackages.txt` | One package name per line — these apps are hidden | | `download_cache/` | Downloaded icon packs and app name files (managed automatically) | ### Useful adb commands ```bash # View Unity logs live adb logcat -s Unity # List files in persistent data path adb shell ls /sdcard/Android/data/dev.oxmc.QuestAppLauncher/files/ # Push a config file adb push config.json /sdcard/Android/data/dev.oxmc.QuestAppLauncher/files/ # Pull all app data (for debugging) adb pull /sdcard/Android/data/dev.oxmc.QuestAppLauncher/files/ ./device_files ```