oPlayer Theme Development

Build custom themes for oPlayer.

Create HTML, CSS, and JavaScript interfaces that run inside oPlayer's Android WebView. The native app owns playback, storage, permissions, and the media database; your theme owns the visual experience.

Runtime Model

Imported themes are served from https://app-theme.local/user_themes/<theme-id>/index.html. That domain is local to the app; oPlayer intercepts it and routes requests to bundled assets, imported theme files, cached artwork, media content proxies, or local JSON endpoints.

External themes execute JavaScript and can read visible oPlayer library metadata. They should be treated as trusted UI code, even though the native backend blocks app-management and destructive actions for imported themes.

Native app

Playback, queue, library database, scanning, settings, imports, permissions, haptics, and system UI.

WebView theme

Your index.html, style.css, theme.js, and local assets render the interface.

Bridge

oplayer-bridge.js exposes window.Bridge for allowed reads, playback controls, events, and fullscreen setup.

Package Structure

Your distributable ZIP must contain these files at the archive root. Do not zip a parent folder.

manifest.json
index.html
style.css
theme.js

You may include local images, fonts, icons, and data files. The importer accepts html, css, js, json, png, jpg, jpeg, svg, gif, woff, woff2, ttf, and otf.

Minimum manifest

{
  "id": "my_theme",
  "name": "My Theme",
  "title": "My Theme",
  "version": "1.0.0",
  "author": "Your Name"
}

Theme IDs may contain only letters, numbers, and underscores. Do not use built-in IDs such as ipod, classic, dark, glideform_touch, or glideform_retro.

Bridge include

<script src="https://app-theme.local/assets/core/oplayer-bridge.js"></script>
<script src="theme.js" defer></script>

React and bundled JavaScript

React, Vite, and other bundlers are fine for authoring themes, but the installed ZIP must still be static files at the theme root. Include oplayer-bridge.js before your bundle, and call the bridge as window.Bridge from module code.

The installed theme must run fully offline through app-theme.local. Do not ship CDN React scripts, remote fonts, remote images, or code that assumes a local dev server is running. Bundle framework code, components, CSS, and small required assets into the static package files, and reference larger assets as local files inside the ZIP.

<script src="https://app-theme.local/assets/core/oplayer-bridge.js"></script>
<script type="module" src="theme.js"></script>

React authoring workflow

  1. Author the theme in a source folder such as src_react/.
  2. Configure the bundler to emit static files into the package folder, such as src/.
  3. Name the bundled JavaScript theme.js and the bundled stylesheet style.css.
  4. Keep manifest.json, index.html, style.css, and theme.js at the package root.
  5. Zip the contents of the package folder, not the parent folder.

ReactiveTheme in this repository uses that split: builds/reactiveTheme/src_react/ is the React authoring project, while builds/reactiveTheme/src/ is the importable static theme folder.

cd builds/reactiveTheme/src_react
npm ci
npm run build
cd ../src
zip -r ../reactiveTheme.zip .

After this, reactiveTheme.zip contains only the offline package files that oPlayer imports.

Fullscreen Themes

Fullscreen themes must set all fullscreen manifest fields consistently and should also request the fullscreen shell after the DOM is ready.

{
  "id": "my_fullscreen_theme",
  "name": "My Fullscreen Theme",
  "version": "1.0.0",
  "author": "Your Name",
  "deviceModel": "FULLSCREEN",
  "layout": "fullscreen",
  "fullscreen": true,
  "themeDarkMode": true,
  "systemBarStyle": "dark"
}

Use themeDarkMode and systemBarStyle for dark fullscreen themes so Android can set readable status/navigation icons before theme JavaScript runs.

function requestFullscreenShell() {
  try {
    var settings = Bridge.getSettings ? Bridge.getSettings() : {};
    if (settings.deviceModel !== "FULLSCREEN") {
      Bridge.setSetting("deviceModel", "FULLSCREEN");
    }
    if (settings.uiOrientationLock !== "PORTRAIT") {
      Bridge.setSetting("uiOrientationLock", "PORTRAIT");
    }
    if (settings.themeDarkMode !== true) {
      Bridge.setSetting("themeDarkMode", "true");
    }
    if (settings.systemBarsEnabled !== true) {
      Bridge.setSetting("systemBarsEnabled", "true");
    }
  } catch (e) {}
}

document.addEventListener("DOMContentLoaded", requestFullscreenShell);

Bridge Policy

Built-in bundled themes can use the full native bridge. Imported themes get a narrow allowlist facade. This means future native methods are not exposed to imported themes unless they are deliberately added to the external bridge.

Allowed

  • Library and metadata reads
  • Normal playback controls
  • Input helpers and haptic feedback
  • Fullscreen startup settings and dark system-bar contrast
  • escapeExternalTheme()

Blocked for imports

  • Theme switching or deletion
  • Backup, restore, import, permission, and review flows
  • Playlist, favorite, podcast, radio, folder, and EQ mutation
  • External browser helpers through Bridge

Author guidance

  • Use visible controls for playback changes
  • Debounce searches and large reads
  • Do not use library metadata outside the theme experience
  • Keep an exit action available for fullscreen themes

API Availability

API group Imported themes Notes
Library reads, playlists reads, podcasts reads, radio reads, videos reads External allowed Use async or paginated methods for large lists.
Playback controls, queue state, current track, sleep timer, volume External allowed Keep changes tied to user-visible controls.
setSetting("deviceModel"), setSetting("uiOrientationLock"), setSetting("themeDarkMode"), setSetting("systemBarsEnabled") External limited Allowed only for documented fullscreen startup and system-bar visibility behavior.
Theme management, imports, backup/restore, permissions, review prompts Built-in only Blocked for imported themes.
Playlist/favorite mutation, podcast/radio mutation, folder include/exclude, EQ mutation Built-in only Official store themes should not rely on these from imported packages.

The raw Markdown guide contains the long-form method reference and should remain the version-controlled source for detailed API notes.

Glideform Studio Store Review

The official theme listing is curated. Review reduces risk for users who install from Glideform Studio, while sideloaded ZIPs from other sources remain outside the official review process.

  1. The ZIP imports cleanly and includes all required root files.
  2. Manifest fields are accurate and fullscreen fields are consistent.
  3. No imported-theme code calls built-in-only Bridge APIs.
  4. No hidden tracking, surprise navigation, deceptive links, or off-purpose metadata use.
  5. No startup code changes playback, volume, sleep timer, haptics, keyboard state, or native shell except documented fullscreen setup.
  6. Large lists are paginated, search is debounced, and the UI stays responsive on large libraries.
  7. Hardware handlers exist and do not duplicate native play/pause/next/previous actions.
  8. Classic themes are tested in the retro shell; fullscreen themes are tested on fresh import, restart, and exit to the built-in fallback.

Testing and Submission

Use browser preview for layout screenshots, then test the actual ZIP inside oPlayer. The browser mock is not the real backend and may return simplified values.

Install test

  1. Copy the ZIP to your Android device.
  2. Open oPlayer Settings, then Themes.
  3. Enable external visual themes and import the ZIP.
  4. Apply the theme and inspect it through Chrome at chrome://inspect.