# 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 `<packageName>.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 `<packageName>.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 (`<packageName>.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
```
