Move to JSON only, add examples
Build / Build launcher (push) Has been cancelled

This commit is contained in:
2026-03-18 09:30:23 -07:00
parent cb8c3eb5df
commit 77799c1182
11 changed files with 168 additions and 129 deletions
-101
View File
@@ -40,7 +40,6 @@ namespace QuestAppLauncher
}
// File name of app name overrides
const string AppNameOverrideTxtFileSearch = "appnames*.txt";
const string AppNameOverrideJsonFileSearch = "appnames*.json";
// Rename file names
@@ -214,19 +213,11 @@ namespace QuestAppLauncher
private static void ProcessAppNameOverrideFiles(bool isRenameMode, Dictionary<string, ProcessedApp> apps, string path)
{
// Process appname*.json files, sorted by name
foreach (var filePath in Directory.GetFiles(
path, AppNameOverrideJsonFileSearch).OrderBy(f => f))
{
ProcessAppNameOverrideJsonFile(isRenameMode, apps, filePath);
}
// Process appname*.txt files, sorted by name
foreach (var filePath in Directory.GetFiles(
path, AppNameOverrideTxtFileSearch).OrderBy(f => f))
{
ProcessAppNameOverrideTxtFile(isRenameMode, apps, filePath);
}
}
private static void ProcessAppNameOverrideJsonFile(bool isRenameMode, Dictionary<string, ProcessedApp> apps, string appNameOverrideFilePath)
@@ -320,98 +311,6 @@ namespace QuestAppLauncher
}
}
private static void ProcessAppNameOverrideTxtFile(bool isRenameMode, Dictionary<string, ProcessedApp> apps, string appNameOverrideFilePath)
{
// Override app names, if any
// This is just a file with comma-separated packageName,appName[,category1[, category2]]
// Category1 and category2 are optional categories (tabs).
Debug.Log("Found file: " + appNameOverrideFilePath);
string[] lines = File.ReadAllLines(appNameOverrideFilePath, Encoding.UTF8);
foreach (string line in lines)
{
line.Trim();
if (line.StartsWith("#"))
{
// Skip comments
continue;
}
// Parse line
var entry = line.Split(',');
if (entry.Length < 2)
{
// We expect at least two entries
continue;
}
var packageName = entry[0];
var appName = entry[1];
if (!apps.ContainsKey(packageName))
{
// App is not installed
if (isRenameMode)
{
// If rename mode, so just add it
apps[packageName] = new ProcessedApp
{
PackageName = packageName,
AppName = appName,
};
}
continue;
}
// Get the custom tab names, if any
string autoTabName = null;
var tab1 = entry.Length > 2 ? entry[2] : null;
var tab2 = entry.Length > 3 ? entry[3] : null;
if (tab1 != null && tab1.Length == 0)
{
tab1 = null;
}
if (tab2 != null && tab2.Length == 0)
{
tab2 = null;
}
if (tab1 != null && tab2 != null && tab1.Equals(tab2, StringComparison.OrdinalIgnoreCase))
{
tab2 = null;
}
// Override auto tab name if custom name matches built-in tab name
if (tab1 != null && Auto_Tabs.Contains(tab1, StringComparer.OrdinalIgnoreCase))
{
autoTabName = tab1;
tab1 = null;
}
if (tab2 != null && Auto_Tabs.Contains(tab2, StringComparer.OrdinalIgnoreCase))
{
autoTabName = tab2;
tab2 = null;
}
// Update entry
apps[packageName] = new ProcessedApp
{
PackageName = apps[entry[0]].PackageName,
Index = apps[entry[0]].Index,
AppName = appName,
AutoTabName = autoTabName ?? apps[entry[0]].AutoTabName,
Tab1Name = tab1 ?? apps[entry[0]].Tab1Name,
Tab2Name = tab2 ?? apps[entry[0]].Tab2Name,
LastTimeUsed = apps[entry[0]].LastTimeUsed
};
}
}
private static void ExtractIconPacks(AndroidJavaObject currentActivity, string iconPacksPath)
{
if (!Directory.Exists(iconPacksPath))
+34 -13
View File
@@ -2,18 +2,28 @@
[![Build](https://github.com/HooverHigh/QuestAppLauncher/actions/workflows/build.yml/badge.svg?branch=master)](https://github.com/HooverHigh/QuestAppLauncher/actions/workflows/build.yml)
An app launcher for Hoover High School's Meta Quest 2 headsets, built in Unity. Displays all installed apps in a configurable grid with tabs, supports custom icons, app name overrides, and automatic asset updates from GitHub.
A VR home-screen replacement for Meta Quest 2, built in Unity by [oxmc](https://github.com/oxmc) for Hoover High School. Replaces the default Quest shell with a fully configurable app grid — custom icons, renamed apps, user-defined tabs, and automatic asset updates from any HTTP or GitHub source.
## Features
- Scrollable grid of all installed apps, configurable rows × columns
- Auto-categorized tabs (Quest / 2D) and user-defined custom tabs
- App name overrides and custom icons via JSON or zip icon packs
- Asset auto-update from GitHub releases or any plain HTTP endpoint — not just one source, any number of repos can be configured
- Sort by alphabetical or most recently used
- Configurable background skybox
- Managed policy support for IT/MDM deployments (hidden apps, name overrides, wallpaper, disable settings)
- Memory-efficient lazy icon loading — textures are loaded/unloaded based on scroll position
## Requirements
- Meta Quest 2
- Unity (see project settings for version)
- Unity (see `ProjectSettings/ProjectVersion.txt` for version)
- Meta XR SDK (included via Package Manager)
- Git LFS (run `git lfs pull` after cloning)
## Building
Builds are handled by GitHub Actions — there is no local build command.
Builds are handled entirely by GitHub Actions — there is no local build command.
- **`build.yml`** — runs tests (on master), builds the Android APK, uploads to GitHub Releases as `HooverHighQuestAppLauncher.apk`
- **`activation.yml`** — manual trigger to request a Unity license activation file
@@ -30,19 +40,30 @@ Builds are handled by GitHub Actions — there is no local build command.
## Configuration
User settings are stored in `config.json` in the app's persistent data path on-device (not in the repo). Key settings:
Settings are stored in `config.json` in the app's persistent data path on-device. The file is created with defaults on first run and can be pushed via `adb`:
- **Grid size** — rows × columns
- **Sort mode** — alphabetical or most recently used
- **Auto category** — places Quest / 2D apps into tabs automatically
- **Custom category** — user-defined tabs via `appnames.json`
- **Background** — skybox selection
- **Auto update** — downloads icon packs and app name files from configured GitHub repos
```bash
adb push config.json /sdcard/Android/data/dev.oxmc.QuestAppLauncher/files/
```
See [docs.md](docs.md) for full configuration and customization details.
Key settings:
- **Grid size** — rows × columns (110 each)
- **Sort mode** — `az` (alphabetical) or `mostRecent`
- **Show 2D apps** — toggle non-VR apps on or off
- **Auto category** — places Quest / 2D apps into auto tabs; position: `top`, `left`, `right`, or `off`
- **Custom category** — user-defined tabs from `appnames.json`; same position options
- **Background** — path to a skybox image, or `"default"`
- **Auto update** — checks configured repos for new icon packs and app name files on startup
- **Download repos** — list of asset sources; supports GitHub releases (`type: "github"`) and any plain HTTP manifest endpoint (`type: "http"`) — multiple sources can be combined
### Custom app names and icons
Place `appnames.json` or `appnames*.txt` in the persistent data path to rename apps and assign them to tabs. Place `iconpack*.zip` files (containing `<packageName>.jpg` entries) or individual `<packageName>.jpg` files to override icons.
See [docs.md](docs.md) for the full format reference, all config fields, managed policy details, and useful `adb` commands.
## Credits
- [tverona1](https://github.com/tverona1) — original creator
- [fecheva](https://github.com/fecheva) — app names & icon packs
- noxx — original app icon
+6 -15
View File
@@ -25,7 +25,7 @@ Quest App Launcher is a VR home-screen replacement for Meta Quest 2. It lists al
### 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/txt`, filters excluded packages, assigns apps to auto tabs (Quest / 2D), and returns a `ProcessedApp` dictionary.
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 (AZ 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.
@@ -89,9 +89,9 @@ adb push config.json /sdcard/Android/data/dev.oxmc.QuestAppLauncher/files/
## App Name Overrides
Place `appnames.json` (or `appnames*.json` / `appnames*.txt`) in the persistent data path to rename apps and assign them to custom tabs.
Place `appnames.json` (or any `appnames*.json` file) in the persistent data path to rename apps and assign them to custom tabs.
### JSON format (`appnames.json`)
### Format (`appnames.json`)
```json
{
@@ -106,16 +106,7 @@ Place `appnames.json` (or `appnames*.json` / `appnames*.txt`) in the persistent
- **Name** — display name override (leave blank to keep the original name)
- **Category** / **Category2** — optional tab names (up to two per app)
### Text format (`appnames.txt`)
```
# Lines starting with # are comments
com.example.myapp,My App,Games,Favorites
```
Format: `packageName,displayName[,category1[,category2]]`
### Pushing files to the device
### Pushing to the device
```
adb push appnames.json /sdcard/Android/data/dev.oxmc.QuestAppLauncher/files/
@@ -168,7 +159,7 @@ Auto tabs can be placed at the top, left, or right of the panel, or disabled ent
### Custom tabs
Assign apps to custom tabs using the `Category` / `Category2` fields in `appnames.json` (or columns 34 in `appnames.txt`). Custom tabs appear sorted alphabetically. Their position is controlled by `customCategory` in `config.json`.
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`.
---
@@ -223,7 +214,7 @@ All user-editable files live in the app's persistent data path:
| File / Folder | Purpose |
|---------------|---------|
| `config.json` | App settings |
| `appnames*.json` / `appnames*.txt` | App name and category overrides |
| `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 |
+19
View File
@@ -0,0 +1,19 @@
{
"com.beatgames.beatsaber": {
"Name": "Beat Saber",
"Category": "Games"
},
"com.facebook.bigbox": {
"Name": "Meta Quest Browser",
"Category": "Apps"
},
"com.oculus.socialplatform": {
"Name": "Horizon Worlds",
"Category": "Social",
"Category2": "Apps"
},
"com.example.hiddenname": {
"Name": "",
"Category": "Games"
}
}
+19
View File
@@ -0,0 +1,19 @@
{
"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"
}
],
"managedPolicyEndpoint": ""
}
+19
View File
@@ -0,0 +1,19 @@
{
"gridSize": {
"rows": 3,
"cols": 3
},
"sortMode": "az",
"show2D": true,
"autoCategory": "top",
"customCategory": "right",
"autoUpdate": true,
"background": "default",
"downloadRepos": [
{
"repoUri": "https://yourserver.com/quest/manifest.json",
"type": "http"
}
],
"managedPolicyEndpoint": ""
}
+19
View File
@@ -0,0 +1,19 @@
{
"gridSize": {
"rows": 3,
"cols": 3
},
"sortMode": "az",
"show2D": true,
"autoCategory": "top",
"customCategory": "right",
"autoUpdate": true,
"background": "default",
"downloadRepos": [
{
"repoUri": "hooverhigh/QuestAppLauncher_Assets/releases/latest",
"type": "github"
}
],
"managedPolicyEndpoint": "https://yourserver.com/quest/policy.json"
}
+14
View File
@@ -0,0 +1,14 @@
{
"gridSize": {
"rows": 4,
"cols": 4
},
"sortMode": "mostRecent",
"show2D": false,
"autoCategory": "top",
"customCategory": "off",
"autoUpdate": false,
"background": "default",
"downloadRepos": [],
"managedPolicyEndpoint": ""
}
+6
View File
@@ -0,0 +1,6 @@
# excludedpackages.txt
# One package name per line. Apps listed here will be hidden from the launcher.
# Lines starting with # are ignored.
com.example.unwantedapp
com.oculus.someapp
+14
View File
@@ -0,0 +1,14 @@
{
"files": [
{
"name": "iconpack_school.zip",
"url": "https://yourserver.com/quest/iconpack_school.zip",
"updatedAt": "2025-01-01T00:00:00Z"
},
{
"name": "appnames.json",
"url": "https://yourserver.com/quest/appnames.json",
"updatedAt": "2025-01-01T00:00:00Z"
}
]
}
+18
View File
@@ -0,0 +1,18 @@
{
"hiddenApps": [
"com.example.blockedapp",
"com.oculus.socialplatform"
],
"appNames": {
"com.beatgames.beatsaber": {
"Name": "Beat Saber",
"Category": "Games"
},
"com.facebook.bigbox": {
"Name": "Browser",
"Category": "Apps"
}
},
"disableSettings": false,
"wallpaper": "https://yourserver.com/quest/background.jpg"
}