# Themes A theme is a folder with a `config.json` and, optionally, a picture: a `wallpaper.jpg` or a `wallpapers/` collection. Pushing one changes the launcher's dock, layout and look on the TV without rebuilding the APK: the launcher reads `config.json` as an overlay over its built-in `config.xml` the next time it starts. Choices made in Launcher settings take priority until you choose Follow theme. Pushing a theme or using `tools/theme.sh --reset` also clears those on-TV column and motion choices. ```bash TV_HOST=192.168.1.50 tools/theme.sh themes/aurora # push a theme and restart Home TV_HOST=192.168.1.50 tools/theme.sh --reset # back to the built-in config.xml powershell -ExecutionPolicy Bypass -File tools\theme.ps1 -TvHost 192.168.1.50 themes\aurora ``` `tools/venv/bin/python -I tools/manage.py list themes` and `list switches` list them from this file, `show ` gives one with its screenshot, and `manage.py set --theme ` writes a key into a theme folder of your own and pushes it with `theme.sh` (`theme.ps1` on Windows). The folder has to be outside this checkout: one under `themes/` or `examples/` is refused (copy a theme first), a `config.json` it cannot read is named before anything is written or pushed, and a folder that is not there is refused too rather than made - an empty folder of your own is fine, `set` starts the `config.json` in it. Pushing replaces the theme on the TV and empties the pictures it pushed before, so a mistyped path would leave a one-key theme behind. On Windows, the manager's theme operations need no Bash: they use `pwsh`, or `powershell` if `pwsh` is not on PATH, without loading profiles or changing execution policy. If the computer's policy blocks scripts, the PowerShell error is reported rather than bypassed. From the repository root in PowerShell: ```powershell $env:TV_HOST = '192.168.1.50' .\tools\venv\Scripts\python.exe -I .\tools\manage.py list themes .\tools\venv\Scripts\python.exe -I .\tools\manage.py install aurora Copy-Item .\themes\aurora "$HOME\my-theme" -Recurse .\tools\venv\Scripts\python.exe -I .\tools\manage.py set columns 6 --theme "$HOME\my-theme" ``` `install ` takes only the theme's name, not script arguments. `set` saves the local `config.json` before pushing it; if the push fails, the edit stays on disk and the error says the TV may be only partly updated. Add-ons with custom shell installers still need Bash; this native Windows support covers themes. | Theme | What it does | Screenshot | |---|---|---| | `default/` | The built-in look written out as JSON, as a starting point for your own | default theme | | `dock-glass/` | A single frosted-glass dock row at the bottom: six columns with even padding, the focused app's name above it, no grid rows below, icon-only top buttons, and the app-colour glow over a near-black sky. No wallpaper of its own: add a `wallpaper.jpg` to put a picture behind the glass | dock-glass theme | | `ambient-glass/` | A dark five-column grid with a frosted-glass dock, round icon-only buttons, and a rotating collection of high-resolution alpine and desert landscapes. The photos are under the Unsplash License: see `ambient-glass/CREDITS.md` | ambient-glass theme | | `aurora/` | A dark aurora wallpaper (teal and violet on navy, darker at the bottom) with a matching background colour | aurora theme | | `blocky/` | Eleven blocky pixel-art scenes that follow the time of day: a meadow by day, at dusk and at night, plains, desert, a snowy forest, jungle, ocean, a blossom grove, a cave and a lava cavern, each with its name under the buttons and behind the clock screensaver; the sun, moon and clouds sit behind the hills and trees. Drawn in code by `tools/blocky_wallpapers.py`, and the header rows stay plain sky | blocky theme | | `cinematic/` | OLED and cinema look: true-colour wallpaper, a 6-column frosted-glass dock, no ambient glow and the idle clock after two minutes. The wallpaper is CC0, not MIT: see `cinematic/CREDITS.md` | cinematic theme | | `dussehra/` | Festive skin for Dussehra (ends 21 Oct 2026): a marigold toran, a faint gold rangoli, a diya for Wi-Fi, a bell for Inputs, a marigold for Settings and a petal shower | dussehra theme | | `family/` | A family in single-line drawings on black, recoloured for the time of day with a sun or moon drawn in code: the whole family in the morning and evening, the parents alone under the moon at night (`wallpaper_moods`, icon-only buttons, and the clock screensaver over the picture). The drawings are CC0, not MIT: see `family/CREDITS.md` | family theme | | `glass/` | Dark navy wallpaper with soft teal and violet light, the cinematic top shelf, a frosted-glass dock, icon-only buttons and the focus glow in the app's colour, gliding without overshoot | glass theme | | `glass-gallery/` | Seven slots in a translucent dock, an app library, white icon-only header controls and three original abstract wallpapers rotated every 30 minutes. No app-colour glow or advanced effects. The wallpapers are CC0: see [CREDITS.md](glass-gallery/CREDITS.md) | glass-gallery theme on a Kenwood K32M15G6 | | `christmas/` | A snowy Christmas night, an illustration of our own (CC0, drawn by `make_wallpaper.py`): Santa's sleigh and reindeer over a lit cabin and fir trees, with a frosted-glass dock, faded rows and the picture behind the idle clock. No start or end date: push it on the day you want it | christmas theme | | `diwali/` | Diwali until 11 Nov 2026: eight Diwali photos from Wikimedia Commons (a row of diyas, diyas in a rangoli and among marigolds, akash kandils in Pune, fireworks over Melbourne's Yarra and over a lake in Chennai, a street in lights in Karnal, a diya with four flames), a new one every 20 minutes with its photographer under the buttons (credited in `diwali/CREDITS.md`; CC BY / CC BY-SA, not MIT; rebuilt by `prepare_wallpapers.py`). The dock sits low so the picture shows, and the festive skin turns Wi-Fi into a diya, Inputs into a bell and Settings into a marigold, greets you with Happy Diwali and switches itself off after 11 November | diwali theme | | `halloween/` | Halloween until 1 Nov 2026: an illustrated haunted house (our own, CC0) and seven night photos from Wikimedia Commons (jack-o'-lanterns by candlelight, a full moon behind a sequoia grove and over a Swedish fjord, a moonrise, two cemeteries in fog), a new one every 30 minutes (credited in `halloween/CREDITS.md`, not on screen), and a frosted-glass dock. With a launcher that knows `seasonal_skin`, the Halloween skin too: a jack-o'-lantern for Wi-Fi that glows while online, a bat for Inputs, a ghost for Settings, bats over the first Home of the day and "Happy Halloween"; it switches itself off after 1 November, the pictures stay until `--reset`. The illustration is CC0; the photos are CC0, public domain, CC BY and CC BY-SA, not MIT: see `halloween/CREDITS.md` | halloween theme | | `istanbul/` | Istanbul drawn in flat layers by code alone: Topkapı Palace, Hagia Sophia, the Blue Mosque, Galata Tower and the Bosphorus Bridge over the water with a ferry, under a faint eight-pointed-star lattice, in a morning, evening and night picture (`wallpaper_moods`, icon-only buttons). The pictures are CC0, not MIT: see `istanbul/CREDITS.md` | istanbul theme | | `istanbul_v2/` | Three illustrated Istanbul skylines, from the historic peninsula to Galata Tower and the Bosphorus Bridge, in the morning, at sunset and at night (`wallpaper_moods`, a light 15% dim, icon-only buttons). The pictures are CC0, not MIT: see `istanbul_v2/CREDITS.md` | istanbul_v2 theme | | `minimal/` | A quiet, flat look: a flat graphite background with no wallpaper, glow or dock band, so the tiles float on their own; five columns with one row of the grid under the dock, no greeting and no big app name, icon-only buttons in the top bar and the glass highlight gliding without overshoot | minimal theme | | `nature-calm/` | Twelve calm nature photos from Wikimedia Commons (misty karst and forest, autumn lakes, spring bluebells, winter frost, mossy woods, a rainforest lake, waterfalls), a new one every five minutes in random order with its place and photographer under the buttons; six small tiles in a frosted-glass dock, round icon-only buttons, no hero name. The photos are CC BY / CC BY-SA, not MIT: see `nature-calm/CREDITS.md` | nature-calm theme | | `red-rain/` | A red digital code rain: pure black with frozen columns of katakana, letters and digits in dark crimson on three depth layers, drawn in code and dimmed behind the clock, the buttons and the dock; the cinematic frosted-glass dock, icon-only buttons, no glow, and the clock screensaver over the same rain. The wallpaper is CC0, not MIT: see `red-rain/CREDITS.md` | red-rain theme | | `ruska/` | A Finnish autumn ("ruska") dusk over a lake: spruce shoreline, birches in autumn colour, a few falling leaves, drawn in code by `make_wallpaper.py`. The app-colour glow stays off the picture so the autumn colours keep their own light; the dock is 70% opaque so the lake shows through, and the focus glows in the app's colour without overshoot | ruska theme | | `scenes/` | 14 dark illustrated landscapes in `wallpapers/`, a new one every hour; drawn from code by `tools/scenes_wallpapers.py` | scenes theme | | `six-columns/` | Six smaller tiles per row and one grid row visible under the dock | six-columns theme | | `retro/` | Pure black, a dock with no band or border, a rim-only focus highlight (`glass_style: rim`), no colour gradient, icon-only top buttons, and a wallpaper for morning, afternoon and night. The three pictures are the author's own; see `retro/CREDITS.md`. By [REGT-URRED](https://github.com/REGT-URRED) | retro theme | | `weeb/` | Pure black with five original neon-outline anime characters, a new one every 30 minutes in order, a frosted-glass dock and round icon-only buttons. The pictures were made with Google Gemini; see `weeb/CREDITS.md`. Bring your own in [Wallpapers](#wallpapers) | weeb theme | | `storm/` | Four lightning photos from Wikimedia Commons (a strike over North Texas, bolts ahead of a shelf cloud, three strikes in one exposure, a thunderstorm near Pritzerbe), a new one every hour in order with its photographer under the buttons, darkened under the clock and the dock; five tiles in a frosted-glass dock, round icon-only buttons, no hero name and no app-colour glow. The photos are CC BY / CC BY-SA, not MIT: see `storm/CREDITS.md` | storm theme | | `sweden/` | Three photos of Sweden from Wikimedia Commons (the aurora over Abisko, Vasabron at dusk, Skeppsbron in the evening), a new one every hour with its place and photographer under the buttons; six small tiles in a frosted-glass dock, round icon-only buttons, no hero name. The photos are CC BY / CC BY-SA, not MIT: see `sweden/CREDITS.md` | sweden theme | | `stockholm/` | Riddarfjärden from Södermalm, drawn in code by `make_wallpapers.py` (CC0): Stadshuset with its three crowns, Riddarholmen's openwork spire, Gamla stan and the Royal Palace in eight lights, from dawn, morning mist, golden hour and blue hour to a moonlit night, the northern lights, a snowy winter night and a midsummer night. `wallpaper_moods` shows the morning pictures in the morning, golden and blue hour in the evening and the night ones after dark, a new one every 20 minutes with its name under the buttons; a frosted-glass dock, round icon-only buttons, no hero name over the skyline, and the clock screensaver over the picture after five idle minutes | stockholm theme | | `aerial/` | Inspired by Apple TV's Aerial screensavers: ten photos NASA astronauts took from the International Space Station (aurora over Scandinavia, sunrise over northern Europe, an orbital sunset, the Nile at night, Moscow under the Moon), shuffled every 15 minutes with the place and expedition under the buttons, on pure black with a frosted-glass dock and round icon-only buttons. Back on Home opens the clock over the photo, as Menu does on an Apple TV, and so do three idle minutes. The photos are public domain, not MIT: see `aerial/CREDITS.md` | aerial theme | | `twilight/` | Three original anime landscape wallpapers in `wallpapers/` (Mount Fuji twilight, seaside coastal train, ancient stone arch), rotating with caption under the buttons; frosted-glass dock at 60% opacity, ambient glow disabled so the artwork stays calm. See `twilight/CREDITS.md` | twilight theme | Every theme folder has a `screenshot.jpg` of Home with that theme on a real TV. `tools/theme.sh` never pushes it; it is only here so you can see a theme before you try it. A theme that did not draw its own pictures says where each one came from, and under which licence, in a `CREDITS.md` in its folder (`docs/DOS-AND-DONTS.md` has the rule and `themes/CREATE_A_THEME.md` the format). Its row in the table above names that file, so a reader picking a theme can see before pushing it whether its pictures are MIT or, say, CC BY-SA. ## Keys Every key is optional; a missing key keeps the value from `config.xml`, an unknown or wrongly typed key is ignored, and a file that doesn't parse is ignored as a whole, so a theme can never break Home. Each key has (or is waiting for) a screenshot of it on a real TV in [`docs/switches.md`](../docs/switches.md). A new key needs one there in the same pull request. | Key | Type | Meaning | |---|---|---| | `dock_apps` | list of package names | The dock row, in order; missing apps are skipped | | `fill_dock` | bool | Fill the dock slots `dock_apps` leaves free with the first grid apps (true by default); false leaves them empty | | `hidden_apps` | list of package names | Never shown as tiles | | `live_tv_package`, `live_tv_action` | string | The Live TV app and, if it has no leanback entry, the action to start it with | | `live_tv_input_id` | string | Live TV source for the Inputs menu: an exact system tuner ID opens its channel collection. The special value `content://android.media.tv/channel` opens the stock launcher's root channel URI on Xiaomi MiTV-MSSP1 (#1941). Empty keeps automatic selection. Missing input or unsupported collection falls back to the app's normal launch. [HAAM satellite example](../docs/devices/haam-r4.md) | | `source_panel_service`, `source_panel_action` | string | The TV's own source panel, for sets with no TvInputManager: a service, or an activity (BYINTEK U4). Without a Live TV app, the Inputs pill opens it directly | | `source_panel_extra` | `"name=value"` | One int extra sent with the source panel (Coocaa OS: `control_center_command=100`) | | `coocaa_sources` | bool | Coocaa OS (Skyworth, China): list the sources from `com.skyworth.tv` in the Inputs menu, ahead of the source panel | | `launch_actions` | list of `"package=action"` | Tiles and `top_bar_apps` buttons that open a screen of the app instead of its start screen; an action that no longer resolves opens the app normally (MagentaTV's guide: `com.telekom.onetv.tv.pl=com.telekom.onetv.tv.pl.action.PROGRAM_GUIDE`); `"package=input:"` makes a tile switch to that TV input instead (ids from `dumpsys tv_input`), e.g. `com.mediatek.wwtv.tvcenter=input:com.mediatek.tvinput/.hdmi.HDMIInputService/HW5`; `"package=view:uri"` opens that URI in the package instead (Bbox TV's live channels: `fr.bouyguestelecom.tv.bbui=view:bbox://live`) | | `app_labels` | list of `"package=Name"` | A tile's name instead of the app's own (MagentaTV as `com.telekom.onetv.tv.pl=TV`) | | `app_banners` | list of `"package=banner_name"` | A tile's artwork from a `banner_*` drawable bundled with the launcher (`banner_tv`: magenta with the Live TV glyph) | | `input_action` | string | Action sent with an HDMI/AV row's channel URI when nothing handles the standard `VIEW` (XGIMI: `com.xgimi.action.hdmiPlayer`). Only a handler-less set gets here; any app that claims the channel MIME type takes the row instead, so check with `query-activities` ([#805](https://github.com/Gathin23/cleanyourtv/issues/805)). Written as `package/.Activity` it names the vendor's own source picker, which every row starts first (Redmi X65, MiTV-NSOP0: `com.xiaomi.mitv.tvplayer/.ExternalSourceActivity`) | | `input_uri_scheme` | string | Scheme an HDMI/AV row's passthrough URI is sent with instead of `content://`, with no MIME type (YaOS: `live-tv`, which its source player answers) | | `input_sources` | list | Vendor source numbers for the Inputs menu, `HW=` by the input id's last part; a mapped input opens by sending `input_action` with the number as the int extra `input`, and `HW=-` leaves a port the set doesn't have out of the menu. Xiaomi/Redmi China firmware: [redmi-mitv-mftp0](../examples/redmi-mitv-mftp0/) | | `columns` | 1 to 8 | Tiles per row; tiles shrink to fit from 5 up | | `rows_below_dock` | 0 to 3 | Grid rows left visible under the dock; tiles shrink if the dock, its title and that many rows would not fit the screen height | | `dock_title` | bool | The focused dock app's name in large type above the dock (`true`); `false` keeps only the glow | | `dock_row` | bool | The dock as its own row with a band (`true`); `false` makes the dock apps the first tiles of one plain grid | | `reorder_button` | bool | A reorder button in the top bar (`false` by default); reordering is always in the Settings button's long-press menu | | `show_network_pill` | bool | The Wi-Fi/Ethernet pill in the top bar (`true` by default); `false` hides it | | `top_bar_apps` | list of package names | Apps shown as round buttons with their own logo in the top bar, left of the network pill; they leave the grid | | `show_inputs_pill` | bool | The Inputs pill in the top bar, shown when the TV has inputs (`true` by default); `false` hides it, and with `top_menu` it also leaves Inputs out of the gear's menu (streamers with no HDMI input, such as the Chromecast with Google TV); what the remote's Input button does is unchanged | | `icon_only_buttons` | bool | Wi-Fi and Settings as round icon buttons; Inputs keeps its label. The VPN badge is drawn in the Wi-Fi pill's label, so with `wifi_label_width` at 0 it goes with the label: add `vpn_pill` to keep the VPN state on screen (`false` by default) | | `icon_only_inputs` | bool | The Inputs pill as a round icon button too, its label kept as the TalkBack description (`false` by default). Works with or without `icon_only_buttons` | | `top_menu` | bool | Inputs, Wi-Fi and Settings folded into one round gear, centred on the header: OK drops a menu with the three plus Reorder apps, hold OK keeps the launcher menu (`false` by default) | | `launcher_settings_button` | bool | Make the header Settings action open Launcher settings directly with a matching 22dp home icon; also changes the Settings row when `top_menu` is on. Hold OK keeps module-card and ordering tools. The settings page includes Reorder apps and TV settings (`false` by default) | | `wifi_status_color` | bool | The Wi-Fi pill's icon green while the TV is online, white while it is not (`false` by default) | | `wifi_status_warnings` | bool | With `wifi_status_color`, the icon orange while connected without internet (a captive portal waiting for its login page) and red while offline, instead of white (`false` by default) | | `wifi_label_width` | 0 to 300 | With `icon_only_buttons`, keep the network name on the Wi-Fi pill up to this width in dp, cut with an ellipsis; 0 leaves only the icon | | `library_button` | bool | The app library as a round button in the top bar, beside Settings; needs `app_library`. Closing the library returns to it | | `show_library_pill` | bool | The App library pill after the last Home row (`true` by default); `false` hides it while `library_button` is on, so the top-bar button stays the way in and Down from the last row stays put, as with `app_library` off | | `always_show_names` | bool | Names under every tile, dimmed until focused, the dock included (its band grows to hold them); `false` by default shows only the focused tile's name | | `wallpaper_true_color` | bool | Decode wallpapers in 32-bit colour (8 MB at 1080p) instead of RGB_565 (4 MB), which bands dark gradients such as a night sky; for TVs with 2 GB or more (`false` by default) | | `app_library` | bool | An App library pill after the last Home row: every installed app, add one to Home or take it off (`false` by default). The choices stay on the TV when a theme is pushed or reset; see [docs/app-library.md](../docs/app-library.md) | | `uniform_tiles` | bool | Each app tile is its icon on a card tinted from that icon, with the name only under the focused tile, instead of the app's own banner (`false` by default) | | `icon_tiles` | list of package names, `config.xml` only | Apps drawn from their icon and name instead of their banner, for banners that crop badly; empty by default. Not read from a theme | | `app_library_selection` | bool | OK toggles Home checkmarks in the library; checked apps first, both groups alphabetized. Hiding keeps focus on the adjacent next app (previous at the end). Requires `app_library`; `false` preserves the standard open/long-press behavior | | `library_ok_opens` | bool | OK opens every app in the library, not only the ones on Home, for a library that holds the apps `hidden_apps` keeps off Home; holding OK still offers Add to Home, and cards drop their "Add to Home" line. Requires `app_library`; `app_library_selection` wins when both are on; `false` by default | | `library_apps` | list of package names | Packages pre-seeded into the App library's "added" set at startup (config.xml `library_apps`, or a theme's `config.json`). They show on Home as if the owner had added them in the App library UI, and stay removable there (a removed one stays hidden); empty (off) by default | | `library_excluded_apps` | list of package names | Packages the App library catalogue never offers, even with `app_library` on. Different from `hidden_apps`, which only keeps a package off Home while still listing it in the catalogue; empty (off) by default | | `shamsi_date` | bool | Show the Solar Hijri date above the Gregorian one | | `lunar_date` | bool | Show the lunisolar date under the Gregorian one, e.g. `LUNAR 13/9 · YEAR OF THE HORSE`, or `13/9 ÂM LỊCH · BÍNH NGỌ` in Vietnamese; a leap month is marked. Off by default | | `lunar_utc_offset` | 0 to 14 | Hours east of UTC the lunar calendar is computed for: `7` is the Vietnamese calendar, `8` the Chinese one, `9` the Korean one; they differ by a day now and then (and by a month in 1985). Unset: 7 when the TV is in Vietnamese, 9 in Korean, 8 otherwise | | `date_below_clock` | bool | The date (with the Shamsi and lunar lines) on its own line under the clock instead of beside it, for a top bar whose pills leave the date too little room (`false` by default) | | `locale` | string | The language the launcher shows itself in, as a language tag (`"vi"`, `"pt-BR"`, `"ar-EG-u-nu-latn"` for Arabic with Western digits), instead of the TV's: for firmware with no menu entry for a language the launcher speaks (a China-market Xiaomi offers Chinese and English only). Empty by default, which follows the TV | | `greeting_name` | string | A name after the greeting, e.g. `"Mr. Trịnh"` for GOOD MORNING, MR. TRỊNH; empty for the greeting alone | | `show_greeting` | bool | The greeting above the clock (`true` by default); `false` hides it | | `header_white` | bool | The date and the line between the clock and the date in the clock's full white instead of dimmed (`false` by default) | | `module_cards` | bool | Look for module cards and ask about them (`true`); `false` turns the feature off | | `reduced_motion` | bool | Calmer motion for weak GPUs: the highlight slides without bouncing and settles within 5 frames, every animation lasts 90 ms or less, names, the top shelf, the glow and the highlight appear instead of fading, and there is no tile entrance (`false` by default) | | `boot_to_live_tv` | bool | Open the Live TV app (`live_tv_package`) on the first Home after the TV starts, once per boot | | `open_on_start_choice` | bool | `true` adds an **Open on start** row to Launcher settings: pick an app on the TV, and the first Home after each boot opens it instead (once per boot; `boot_to_live_tv` wins when both are set; an app that is gone or disabled leaves Home on screen). Off by default | | `open_on_start_network_wait` | 0 to 600 | Seconds the first Home after the TV starts waits for a working internet connection before it opens the `boot_to_live_tv` or Open on start app. A streaming app opened before the network is back shows an error, and after a power cut Wi-Fi can take minutes, as the router starts again too. Home stays on screen meanwhile, and leaving Home cancels the opening. `0`, the default, opens the app at once | | `open_on_start_on_wake` | bool | With `open_on_start_choice`, the picked app also opens each time the screen comes back on, for TVs whose remote powers them into a light sleep with no boot. It opens over whatever was in front, an HDMI input included, and `open_on_start_network_wait` applies. Off by default | | `focus_overshoot` | bool | The highlight and the focused tile swing past their target before settling (`true` by default); `false` glides straight there with smooth scrolling | | `vpn_app` | string | App the network pill opens when a VPN is configured; also shows a red "VPN off" badge while no VPN is up | | `vpn_pill` | bool | The VPN as its own pill after the network pill instead of a badge inside it: solid green while a VPN is up, solid red "VPN off" while none is and `vpn_app` is installed, hidden otherwise. Pressing it opens `vpn_app` (without it, the App library, or VPN settings with `app_library` off); it keeps its label with `icon_only_buttons`; the network pill then opens Wi-Fi settings. Ignored with `top_menu`. A VPN app's kill switch holds a VPN up while the app says disconnected, so the pill stays green then (`false` by default) | | `icon_only_vpn` | bool | With `vpn_pill`, the VPN pill as a round icon button too, matching `icon_only_buttons`: an open lock on the plain glass button while no VPN is up, the closed lock in a green outline while one is, solid green only while focused, like the pill; "VPN" / "VPN off" become its TalkBack description (`false` by default) | | `animations` | bool | Basic focus, glass, title and tile entrance motion; true by default | | `advanced_effects` | bool | Optional lifted tile, icon zoom, smooth ambient colour and app opening effects; false by default | | `show_home_settings_shortcut` | bool | Keep the optional Default-home tile visible when the TV has a home picker; false by default | | `show_stats` | bool | One line under the clock with CPU, free memory and free storage. Off unless set | | `game_day` | string | Each team's next game in the F1 panel's place under the clock ("Minnesota Wild at Dallas Stars", "Puck drop in 2h 18m", "Live now"), rotating every few seconds with the F1 weekend when `race_calendar` is on: the package of an add-on that publishes them, such as the Game Day add-on (`io.github.justinnewman.gameday`). The launcher only reads it and stays offline. Off (empty) unless set | | `handle_view_inputs` | bool | Answer the system input switcher (`VIEW_INPUTS`, what the remote's Input button becomes) with the Inputs menu, for sets whose stock launcher was its only handler (Hisense huangshan); `false` by default so a TV with its own input picker never gets a chooser | | `background_color` | `#RRGGBB` | Colour behind everything; black by default | | `sky_intensity` | 0 to 100 | Time-of-day backdrop: a dark gradient that follows the clock, 0 hides it (default) | | `wallpaper` | bool | Show the theme's picture, `wallpaper.jpg` or its `wallpapers/` collection (`true`), or draw neither (`false`) | | `wallpaper_rotation_minutes` | 0 to 1440 | Rotate images in the theme's `wallpapers/` folder at this interval; 0 keeps the single-image behaviour. A negative or non-numeric value is ignored, and 0 means "no rotation", not "every minute" | | `wallpaper_order` | `shuffle` or `sequential` | How a collection advances. `shuffle` (the default, and what launchers did before this key existed) avoids repeating the current picture; `sequential` walks the folder in filename order and wraps at the end. An unknown value falls back to `shuffle` | | `wallpaper_dim` | 0 to 90 | Black overlay over wallpaper images, in percent; 0 by default and no shade view at all | | `dock_opacity` | 30 to 100 | Opacity of the dock's band, in percent, so a wallpaper shows through it; 100 by default (the cinematic shelf keeps its own translucent band). Launcher settings on the TV offers 100, 70, 50 or 30, which wins until the next theme push or Follow theme | | `wallpaper_captions` | bool | With a rotating collection, the shown picture's name under the top-right buttons, from `wallpapers/captions.txt` (`001.jpgDusk over the peaks`, one per line); a picture without a line shows no name (`false` by default) | | `wallpaper_moods` | object | A rotating collection grouped by time of day: `{"morning": [...], "evening": [...], "night": [...]}` with file names from `wallpapers/`. Morning runs from sunrise to noon, evening until half an hour after sunset, night the rest, with the sun at config.xml's `latitude` and `longitude` (07:00 and 18:30 without them); a missing group rotates through every picture, and so does a group whose names are not the pictures in `wallpapers/` (a renamed picture, a suffix the launcher does not read), since those names are dropped one by one; every group is read by these three names only. Unset by default | | `tile_menu` | bool | Hold OK or press the remote's Menu key on a tile to open a small menu: Move, Remove from Home (with `app_library`) and App info. `true` by default since launcher 3.3; `false` keeps holding OK as a shortcut to the app's App info page and leaves Menu unchanged | | `festival` | string | Turns on a festive skin: the network pill becomes a diya whose flame follows the connection, Inputs a temple bell, Settings a marigold, and the first Home of the day gets a petal shower. `dussehra` and `diwali` have built-in greetings. `halloween` has its own skin: a jack-o'-lantern whose carved face glows while the TV is online and goes dark when it drops, a bat for Inputs that flaps when focused, a ghost for Settings that floats up, bats flying up over the first Home of the day, "Happy Halloween" and a pumpkin-orange glow. Launchers before it show any name as the diya skin, so a Halloween theme names it with `seasonal_skin` instead | | `seasonal_skin` | string | Names a festive skin like `festival` (`halloween`), read only when `festival` is empty or missing. Launchers that predate it ignore the key, so a theme that sets it shows the rest of its look there without the wrong skin; `festival_until`, `greeting` and `accent_color` apply to it as to `festival` | | `festival_until` | `YYYY-MM-DD` | Last day the festive skin shows; it switches itself off after that. A malformed date turns it off | | `greeting` | string | Replaces the time-of-day greeting while the festive skin is on | | `accent_color` | `#RRGGBB` | Colour of the focus glow while the festive skin is on; diya gold if not set, pumpkin orange for `halloween` | | `settings_key_scancodes` | list, `config.xml` only | The same buttons by Linux scancode, for `HomeKeyService` (`HOME_KEY_SERVICE=1`); the Xiaomi/Fengmi projector remote's gear is scan 384. `Config.java` reads `settings_key_keycodes` out of a theme but never this one, so a `config.json` that sets it is dropped - it belongs in `config.xml` before the build. Find a code with `tools/tv.py 'getevent -lt'` | | `settings_key_keycodes` | list of keycodes | Remote buttons that open Settings, for `HomeKeyService` (TCL Google TV gear: `[83]`) | | `settings_key_panel` | bool | Those buttons open a small quick settings panel (TV settings, Display, Network, Apps, Launcher settings) instead of the TV's Settings | | `settings_fallback_components` | list, `config.xml` only | Vendor screens tried in order when nothing usable answers the standard Settings action (Xiaomi China, TCL/FFALCON, and Fire OS, where the real menu lives in its launcher), each as `package/activity`; the first one whose package is installed wins, and when none is, the standard intent is sent anyway and may open nothing. Fire OS answers that intent with a `CTSDummy` stub, which counts as missing so the fallback runs. A package named here also has to be listed in the launcher's `` (Android 11 package visibility), so a set whose settings app is neither of these needs a build, not a theme push. Not read from a theme | | `restore_key_services` | bool | After a force stop of the launcher, Home puts the remote's accessibility helpers (`HomeKeyService`, `InputKeyService`) back on the enabled list; needs `RESTORE_KEY_SERVICES=1` (`-RestoreKeyServices` on Windows) on every install, since an install without it takes the grant back. Off by default | | `input_key_keycodes` | list of keycodes | More remote buttons that open the input switcher, for `InputKeyService` (`KEYCODE_TV` and `KEYCODE_TV_INPUT` always do) | | `app_keys` | list of strings | Remote buttons that open an app, as `"190:com.google.android.youtube.tv"` or `"KEYCODE_BUTTON_3:com.google.android.youtube.tv"`, or by scancode as `"scan:490:com.wbd.hbomax"` for remotes that share one keycode between app buttons (Sony BRAVIA), for `InputKeyService` or `HomeKeyService`. `"input:"` in place of the package switches to a TV input instead, e.g. `"scan:227:input:com.tcl.tvinput/.passthroughinput.TvPassThroughService/HW1413744640"` for an HDMI port (ids from `dumpsys tv_input`); the switch asks `live_tv_package` first, so set it, or an app that also claims the channel URI (a file manager on a TCL G03) can take the press and turn it into a chooser, while `input_uri_scheme` and `input_action` shape the Inputs rows and a tile's `launch_actions` rather than this target. `"inputs"` in place of the package opens the Inputs menu, for a remote whose source button sends a key Android does not map, e.g. `"scan:365:inputs"` (TCL 65C745). An action word in place of the package runs that action: `back`, `home`, `recents`, `mute`, `sleep` (standby, Android 9 and later), `screensaver`, `settings`, `wifi`, `library` (Home with the App library open, for a remote's Apps button, `"284:library"`; needs `app_library`) or `ignore` (swallows the button so it does nothing, for a shortcut pressed by accident), e.g. `"KEYCODE_PROG_YELLOW:mute"` or `"scan:586:ignore"`. Prefix an entry with `hold:` to make it the long-press action, e.g. `"hold:190:com.netflix.ninja"`, while the unprefixed entry still opens on a tap. Prefix it with `front:` for what a tap does while the button's own app is already in front (`HomeKeyService` only), and use `"guide:"` as a target to open that Live TV app's program guide: `"172:com.vt.tv.tvcenter"` with `"front:172:guide:com.vt.tv.tvcenter"` opens Live TV, then its guide, on a Hisense hengshan set whose firmware sends the Live TV button (`KEYCODE_GUIDE`) only to the stock launcher | | `key_filter` | bool | `false` makes the key helpers stop filtering keys while staying enabled. Use it on firmware where any key-filtering accessibility service breaks long-press repeat (TCL Google TV on Android 12): the remote buttons above stop working, and volume repeat comes back | | `gamepad_passthrough` | bool | `true` makes the key helpers stop filtering keys while a game controller is connected, so games see its buttons (Steam Link with an Xbox controller, Moonlight with two controllers); the remote buttons above do nothing until it disconnects. Needs `key_filter` on. A voice remote that reports a joystick does not count. Off by default | | `home_ignore_channel_keys` | bool | `true` makes Channel Up/Down do nothing while Home is in front, where they would otherwise open the TV's Live TV app (Sony BRAVIA, Android 9). Inside other apps the keys work as before, and no key helper is needed. Off by default. | | `consume_volume_keys` | bool | `true` makes the key helpers swallow Volume Up/Down and repeat a held one themselves (after 500 ms, every 60 ms). Use it on firmware that applies a volume key both before the helper and when the helper hands it back, so one tap moves two steps (TCL G03 / BeyondTV4, Android 11, #1100). Needs `key_filter` on. Off by default | | `stock_home_redirect` | string | Stock launcher package; if the firmware turns it back on (XGIMI), its home screen is sent straight back here (`HOME_KEY_SERVICE=1`). The Home-key half (a stock home already on screen when Home is pressed, #1116) needs `key_filter` on: with `key_filter: false` it stays on the stock home until another app opens (#2779) | | `power_on_home` | bool | For firmware that opens the Live TV app (`live_tv_package`) by itself at every power-on (Changhong / Homwee): until the first key press after boot or screen-on, that Live TV window is sent back to Home (`HOME_KEY_SERVICE=1`). Ignored with `boot_to_live_tv`, or with Live TV as the Open on start app. Off by default | | `home_long_press_home` | bool | Every Home press opens Home, a held one too. Android turns a Home press held past 400 ms into ALL_APPS for the system-image launcher, which nothing answers once the stock launcher is disabled, so a firm press on a slow remote did nothing (Philips TPM191E, Android 12, where `long_press_timeout` does not move that limit). Needs `key_filter` (`HOME_KEY_SERVICE=1`), so held keys such as Volume Up/Down stop repeating, confirmed on that Philips; single presses still step. Off by default | | `power_on_home_always` | bool | Home at every power-on, whatever the TV put in front: the last HDMI input, Live TV or an app, for TVs that come back to the last source (Philips). Unlike `power_on_home`, it does not wait for a window to show: the Home screen comes up about a second after the screen turns on (`HOME_KEY_SERVICE=1`). A key pressed before that wins. Ignored with `boot_to_live_tv`, or with an Open on start app. Off by default | | `inputs_connected_only` | bool | The Inputs list shows only HDMI ports and CEC devices that report connected; empty ports, the AV jacks and devices asleep in standby fold behind a "More inputs" row that expands them. Some TVs (TCL) report an empty HDMI port as connected-standby, the same state a sleeping device has, so a device in standby is folded too. An HDMI row whose state the TV cannot be trusted with stays (`false` by default) | | `inputs_cec_state_only` | bool | Only CEC device rows in the Inputs list show a connection state ("Steam Deck · HDMI 1", Connected); bare HDMI ports show none. For TVs that put port states on the wrong port: TCL UnionTV (Android 9) reports its empty HDMI 1 as connected and a device on HDMI 2 nowhere, while its CEC device list stays right. A device without CEC shows no state (`false` by default) | | `clock_format` | `12`, `24` or `system` | The launcher's clocks (Home and the clock screensaver) in 12- or 24-hour time whatever the TV is set to; `system` (the default) follows the TV. Launcher settings' Clock row sets it too | | `race_calendar` | list of races | F1 race-weekend mode: from 12 hours before a weekend's first session until 4 hours after the race starts, a panel under the clock shows the next session with a countdown (or the live one) and the weekend's session times in the TV's time zone. Write it with `tools/venv/bin/python -I tools/race_calendar.py `, which fetches the season from the free Jolpica API; the launcher itself never goes online | | `home_idle_clock_minutes` | 0 to 1440 | Minutes Home may sit untouched before it shows the clock screensaver (a thin clock and date drifting over a slow glow, as the system "Clock" screensaver does). Any key returns to Home. It starts only from Home, never over Live TV or an app; 0 is off (default) | | `back_opens_clock` | bool | Back on the Home screen opens the clock screensaver (the same screen as `home_idle_clock_minutes`) instead of doing nothing. Back inside apps, Settings and the App library is unchanged; any key leaves the clock for Home. `false` by default | | `clock_wallpaper` | bool | The clock screensaver over Home's wallpaper instead of black with a coloured glow; the clock still moves every minute (`false` by default) | | `clock_wallpaper_dim` | 0 to 90 | How dark that wallpaper is behind the clock, in percent (50 by default) | | `clock_glow` | bool | The clock screensaver's coloured glow (`true` by default); `false` leaves the moving clock on plain black, e.g. for an OLED | | `glow_style` | string | Shape of the glow behind the focused app, in its colour: `radial` (default) or `horizon`, the colour washing down from the top edge to black; the cinematic shelf keeps its own. Any other value falls back to `radial`; there is no value for "none", so a theme without a glow sets `ambient_glow` and `app_glow` to `false` | | `glass_style` | string | Look of the focus highlight: `glass` (default: a translucent slab with a bright top sheen, a crisp rim and a faint outer glow), `rim` (the crisp rim alone, no sheen, fill or glow, for a quiet focus over a wallpaper) or `halo` (the rim with a lighter outer glow). Any other value falls back to `glass` | | `cinematic_home` | bool | Cinematic top shelf: the focused dock app's artwork, softened, fills the top of the screen in its colour, with a larger name, "Last opened" (apps opened from Home), dock position dots, a focus glow in the app's colour and a translucent dock band; off by default | | `cinematic_dots` | bool | With `cinematic_home`, the dock position dots under the app's name (`true` by default); `false` leaves them out | | `cinematic_white_focus` | bool | With `cinematic_home`, the focus highlight stays white on the dock instead of taking the focused app's colour (`false` by default); `app_glow` still colours every tile | | `cinematic_scrim` | 0 to 100 | With `cinematic_home`, the strength of the dark scrim behind the shelf's name (`100` by default); lower lets a wallpaper show through | | `cinematic_art` | 0 to 100 | With `cinematic_home`, the opacity of the focused app's softened artwork (`50` by default); `0` leaves the wallpaper uncovered | | `dock_even_padding` | bool | The dock band's padding equals the gap between its tiles on all four sides, as far as the grid's edges leave room (`false` by default) | | `dock_glass` | bool | The dock band as light frosted glass like the top bar's pills (`false` by default); suits `sky_intensity` or a wallpaper, where the solid band reads as a dark slab | | `dock_band` | bool | Draw the dock band and its edge (`true` by default); `false` leaves the tiles floating on the wallpaper with no band, border or dark slab | | `single_row` | bool | Every app in the dock's row, the dock apps first, scrolling sideways; no grid below (`false` by default; needs `dock_row`) | | `dock_band_all` | bool | With `single_row`, the band runs behind every app in the row, not only the dock apps (`false` by default) | | `ambient_glow` | bool | The glow behind the dock in the focused app's colour (`true` by default); `false` keeps only `background_color` or the wallpaper, e.g. for a calm photo background | | `glow_snap` | bool | The ambient glow takes the next app's colour in one frame instead of dimming and fading back in. Each frame of the fade repaints the whole screen, so on a slow GPU (a Mali-G52 on a Xiaomi MiTV-MOEU0) it cut the late frames on Home from 60% to 27%. `false` by default | | `app_glow` | bool | The focus highlight glows in the focused app's colour on every tile, as `cinematic_home` does for the dock (`false` by default) | | `fade_rows` | bool | The rows fade out as they scroll up under the clock instead of being cut off at a hard line; the wallpaper or sky shows through. The grid then draws into a hardware layer (about 6 MB of graphics memory at 1080p) (`false` by default) | ## Wallpapers `wallpaper.jpg` is drawn full screen behind the launcher, centre-cropped. Keep it dark, 1920×1080 and under about 300 KB: the text, the hero name and the dock band are designed for black, and a 1 GB TV decodes the picture into memory on every Home start. Pictures larger than the screen are scaled down to it; a picture that is still over 4K (about 8.4 million pixels) after it is sampled down to the screen is not shown at all, and the plain colour stays. For a wallpaper collection, place JPEG, PNG or WebP files in the theme’s `wallpapers/` folder beside `config.json`, then set: ```json { "wallpaper": true, "wallpaper_rotation_minutes": 30, "wallpaper_dim": 40, "wallpaper_order": "sequential" } ``` The last displayed picture is remembered on the TV, so a restart or reboot resumes from it rather than starting over at the first image in `sequential` mode. Missing an interval while the TV is asleep or unplugged does not replay the missed pictures: Home moves on at most once when it wakes. Replacing the collection starts playback from the new collection's first picture. A collection with one picture is shown once and never blanked on a timer. Images are centre-cropped to fill the 16:9 screen; dimming is drawn as a black overlay above the picture and below the launcher's text. When you apply a theme, its collection replaces the one already on the TV. Use filenames containing only letters, digits, underscores, dots and hyphens, with no two dots in a row; the theme scripts refuse any other name before they change anything on the TV. Images can come from any source; check that you have permission to use them. The launcher considers up to 100 nonempty images, at most 10 MB each, skips unreadable files and keeps the last good background. An empty collection falls back to `wallpaper.jpg`, or the background colour if no image is available. `"wallpaper": false` disables both single-image and collection backgrounds. Selection is random without an immediate repeat when alternatives exist. The current image and its change time survive app/process restarts. Rotation runs only while Home is visible; on return from another app, an overdue background changes then. Images are decoded on a worker thread and bounded to the screen size. There is no download service, Internet permission or computer-side scheduler. The original system idle screensaver is left unchanged. On the TV, Launcher settings has a Wallpaper row: pick one picture from the collection (or the theme's `wallpaper.jpg`), or None. A pick shows that one picture and stops the rotation until you choose Theme default, press Follow theme, or push a theme again. **Your own walls with `weeb`.** Copy `themes/weeb` to a folder outside this checkout, so pictures you only have for your own use are never committed, put your pictures in its `wallpapers/` folder, then run `tools/theme.sh `. ## Reduced motion `"reduced_motion": true` keeps the look but calms the movement and draws far fewer frames: the highlight slides without bouncing and settles within 5 frames, every animation (tile zoom, dropdowns, the app library) lasts 90 ms or less, names, the top shelf, the glow and the highlight appear instead of fading, and there is no tile entrance. Use it on TVs whose GPU cannot keep up; a MediaTek MT5895 on Android 10 is the known case. The default, `false`, keeps the normal motion. ```json { "reduced_motion": true } ``` ## Time-of-day backdrop `"sky_intensity": 60` draws a dark gradient over the wallpaper and behind everything else, that follows the clock: night blues, a soft blue day, then a warm horizon that peaks at seven in the evening. The glow near the bottom edge travels from left to right with the sun. The colours stay dark so the white clock, the hero name and the dock band keep their contrast. It is drawn as one 480×270 bitmap and redrawn once a minute, never animated, so even a 1 GB TV has nothing to keep up with. `0` keeps the plain background; the default is off (the reference launcher looks exactly as before). ```json { "sky_intensity": 60 } ``` That evening peak is a fixed reference day, sunrise 07:30 and sunset 19:00 on the TV's clock. The launcher follows the real sun only when `latitude` and `longitude` are set in `launcher/app/src/main/res/values/config.xml`, which is a build setting a theme cannot carry: see [docs/sky.md](../docs/sky.md) for the two lines and what still falls back. ## Adding a theme Want a step-by-step prompt to hand to a coding agent with shell access to the TV? [`CREATE_A_THEME.md`](CREATE_A_THEME.md) walks this from the TV side, in the keys below. Open a PR with a new folder here containing: - `config.json`, the theme itself. - `screenshot.jpg`, **required**: Home on a real TV with the theme pushed, not a mock-up or an emulator. Take it with `tools/tv.py 'screencap -p /sdcard/s.png'` and `tools/tv.py --pull /sdcard/s.png s.png`, then save it as a JPEG at 1280×720 (16:9) under 300 KB, for example `sips -z 720 1280 -s format jpeg s.png --out screenshot.jpg` on macOS. Leave the focus on the dock so the glow and the hero name show. A PR without it fails CI (`tests/test_themes.py`). - `wallpaper.jpg` or `wallpapers/` if the theme uses them. Then add a row to the table above with the screenshot, the same way as the others, and bump the count in the Themes feature row of every front page that states it: `README.md` (the `N ship in themes/` sentence), `README.de.md`, `README.nl.md`, `README.uk.md`, `README.tr.md`, `README.pt-BR.md` (the last two in digits) and `README.he.md`, each in its own words. Those pages are the ones that state it today rather than the rule: `tests/test_readme_theme_count.py` counts the folders here that carry a `config.json` and fails on a page whose number is not that count, naming the page, so its list is the one a page that starts stating the count joins. Keep the dock list generic (the big streaming apps) unless the theme is for a specific TV, and say which TV you tried it on in the PR description.