The always-on ambient screen your desk was missing.
Website · Download APK · Contributing · How it works
Plug any Android TV device into a spare monitor and turn that dusty second screen into the calmest, most useful surface in your setup.
You already have a laptop for the thing you're focused on. You already have a phone for the world reaching in. You're missing the third screen — the one that just sits there and tells you the time, what meeting is next, and what's playing, without ever asking for a click.
This is that screen.
- Features
- Screenshots
- Quick start
- Configuration
- How it works
- Releasing
- Contributing
- License
- Acknowledgements
A huge, thin, beautifully kerned time display — with seconds that quietly fade away after 90 seconds of stillness so the resting face is just 7:00 PM. When idle, the whole dashboard melts into a true-black screensaver and the clock drifts a few pixels every minute so your panel never burns in.
The dashboard shows what's happening now and what's next. Walk past the screen and you instantly know: "keep coding" or "stand up, meeting in five." Tap into the Calendar page for the full timeline — personal + work feeds merged from your Google / Outlook iCal URLs, colour-tagged.
The Music page is a full-blown Spotify Connect remote: album art, transport buttons, your live queue, your recently played tracks — driven by the actual MediaSession on your TV plus the Spotify Web API. Skip from across the room with the Android TV remote. Switch playback to your headphones, kitchen speaker, or living-room TV from one focus ring.
Flip the switch in Settings and the dock starts advertising itself on your network as a wireless display target for AirPlay (iPhone, iPad, Mac), Google Cast (Chrome, Android), and Miracast (Windows, Android). The dashboard stays calm at rest; the moment a sender connects, it crossfades out and your phone or laptop takes the screen edge-to-edge. A small "Casting from {device} via AirPlay" pill rests in the corner, drifting a few pixels every minute (same burn-in protection as the clock). Each protocol is independently toggleable.
The dock ships with a native WireGuard tunnel. Drop a .conf from your laptop over the LAN, tap Connect on the new Connect page, and every byte the TV sends goes through the tunnel — Netflix, Plex, the launcher, everything. A discreet country pill confirms the tunnel is up. The system "Always-on / kill-switch" hook is one tap away so non-VPN traffic gets blocked at the kernel.
Every screen carries a softly blurred wash of the current track's album art — Gaussian-quality (pyramid downsample + 3-pass box blur, plus GPU RenderEffect on API 31+). When you stop touching the remote, the wash fades to black so the clock owns the room.
- Stay awake while visible — no screen-off mid-meeting
- Sleep timer so the dock gracefully exits when you go to bed
- Burn-in protection for OLED panels — clock drifts in ambient mode
- Android TV native — uses the remote, D-pad, and media keys exactly as you'd expect
- No cloud, no telemetry, no account required — Spotify is optional and PKCE-based
- Grab the latest APK for your platform from the Releases page (
app-firetv-release-*.apkorapp-googletv-release-*.apk). - Enable ADB on your TV (Fire OS: Settings → My Fire TV → Developer Options. Google TV: Settings → System → About → tap Android TV OS build 7×, then Developer options → USB debugging).
- From your laptop:
adb connect <tv-ip>:5555 adb install -r app-firetv-release-vX.Y.Z.apk # or app-googletv-release-vX.Y.Z.apk
- Launch Dock from the Apps row.
APK builds are produced manually per release (see Releasing for the why) — not every release has APKs attached. If yours doesn't, build from source below or run
gh workflow run package.yml -f tag=<latest>on your fork.
source scripts/dev-env.sh
cp local.properties.example local.properties # set sdk.dir, spotify.clientId, unsplash.accessKey
./gradlew assembleFiretvDebug
adb install -r app/build/outputs/apk/firetv/debug/app-firetv-debug.apkThe firetv build flavor is the default and targets minSdk 25 (older Amazon Fire OS hardware still ships there). For newer Google TV / Android TV / Chromecast devices, use ./gradlew assembleGoogletvDebug — same code, minSdk 29, separate applicationId so both can coexist on one device.
spotify.clientId comes from the Spotify Developer Dashboard. Redirect URI: com.ambient.tvclock://spotify-callback. Add your Spotify account under User Management (Development Mode).
unsplash.accessKey comes from Unsplash Developers — create an app, copy the Access Key (not Secret Key). Drives the configurable background image feature. Free Demo tier is 50 req/hr, which the dock stays well under because one search returns a page of 30 URLs that get cycled locally.
Google Calendar → your calendar → Integrate calendar → Secret address in iCal format. On the TV: Settings → Personal calendar URL — paste the link. Or, from your Mac:
./scripts/set-calendar-urls.sh 192.168.1.4:5555 "https://calendar.google.com/calendar/ical/.../basic.ics"Work Outlook ICS goes under Work calendar URL.
Grant the notification listener so the dock can read MediaSession metadata from the Spotify TV app:
LISTENER=com.ambient.tvclock.firetv/com.ambient.tvclock.MediaNotificationListener
adb shell settings put secure enabled_notification_listeners $LISTENER
adb shell cmd notification allow_listener $LISTENEROr just run ./scripts/install-firetv.sh — it does all three steps in one shot.
For Premium users who want queue + remote transport:
- Add
spotify.clientIdtolocal.propertiesand rebuild - Settings → Connect Spotify — sign in on the TV (WebView)
- Play Spotify on the same account; this TV must be the active Connect device for queue data
- Focus a transport button or track row and press OK
- Focus Playing on … · OK to switch to move playback to another device
Settings → Phone mirroring (beta) → Enable mirroring receiver. Set a device name if you want a custom one in the AirPlay picker. Each protocol (AirPlay / Cast / Miracast) is independently toggleable. The dock starts a small foreground service that advertises on the LAN; flip it off and everything goes back to sleep.
The dock spins up a short-lived LAN endpoint that accepts a .conf upload from your laptop's browser:
Settings → VPN → Receive config from laptop (LAN)
To activate, head to the Connect page and press OK on the VPN card. The kill-switch lives one tap away under Settings → VPN → Always-on / kill-switch (system).
| Input | Where | What it does |
|---|---|---|
| D-pad Left / Right | Anywhere | Switch between Connect, Home, Calendar, Music |
| D-pad Up / Down | Calendar | Scroll the day |
| D-pad / OK | Music, Connect | Focus + activate transport, tracks, devices, AirPlay, VPN |
| Media keys | Music | Play/pause, skip, previous (works with the Android TV remote media buttons) |
| Menu | Anywhere | Settings |
After your configured idle window (default 90s) the dashboard fades into ambient mode — clock plus a single horizontal music × calendar strip below it. Any keypress brings it back.
flowchart TD
MA[MainActivity<br/>pager + drift + ambient watchdog] --> DP[DashboardPagerAdapter]
DP --> C[Connect<br/>StatusScreenBinder]
DP --> H[Home<br/>HomeScreenBinder]
DP --> CA[Calendar<br/>CalendarScreenBinder]
DP --> M[Music<br/>MusicScreenBinder]
DP --> BG[BlurredBackgroundBinder<br/>AlbumArtBlur]
H --> CP[CalendarPoller<br/>IcalParser]
CA --> CP
H --> NP[NowPlayingPoller<br/>MediaSession]
M --> NP
M --> SC[SpotifyApiClient<br/>OAuth PKCE]
C --> RS[ReceiverService<br/>AirPlay / Cast / Miracast]
C --> WG[WireGuardTunnel<br/>VPN service]
RS --> SO[StreamingOverlay<br/>SurfaceView crossfade]
MainActivity.kt— dashboard pager, drift, ambient watchdog, input routing, streaming-overlay crossfadeHome/Calendar/Music/StatusScreenBinder.kt— per-page view bindersBlurredBackgroundBinder.kt+AlbumArtBlur.kt— full-bleed artwork wash (pyramid downsample + 3-pass box blur ≈ Gaussian, GPURenderEffectpass on API 31+)CalendarPoller.kt/IcalParser.kt— iCal feed polling, every 15 minSpotifyApiClient.kt/SpotifyAuthActivity.kt— OAuth PKCE + queue / recently-playedNowPlayingPoller.kt— MediaSession bridgereceiver/ReceiverService.kt— foreground service hosting AirPlay / Google Cast / Miracast, plus the RTSP + MediaCodec pipeline for the AirPlay video streamreceiver/ui/StreamingOverlay.kt— full-bleed SurfaceView the dashboard crossfades into when a sender connectsvpn/— WireGuard tunnel manager + the LAN config-import endpoint
Built on plain Android views (no Compose, no React Native) so it stays buttery on older Android TV hardware.
This project uses semantic-release driven by Conventional Commits.
- Every PR title is a Conventional Commit (
feat:,fix:,perf:, …) - Releases are cut locally with the
/releaseClaude Code skill ornpx semantic-release --no-ci— no CI cost - Building and attaching APKs is a separate
workflow_dispatchstep:gh workflow run package.yml -f tag=vX.Y.Z
See CONTRIBUTING.md for the full release flow and commit conventions.
Pull requests welcome. Read CONTRIBUTING.md before opening one — there's a small but firm set of conventions around commit messages, scope, and "what won't be merged" (cloud deps, telemetry, interruptive features). For bugs and feature ideas, use the issue templates.
Apache License 2.0. The patent grant matters for the WireGuard, AirPlay, and codec code paths — please don't strip the LICENSE if you fork.
- WireGuard for Android —
com.wireguard.android:tunnel - dd-plist — Apple plist parsing for AirPlay session attributes
- OkHttp and Bouncy Castle — networking + crypto
- Timber — logging
- Spotify Web API — queue / device control
- The screen-saver photo frames of the world — for being almost-but-not-quite what I wanted
If you've got a spare monitor and an Android TV stick in a drawer somewhere, give this five minutes.
It's the kind of small infrastructure you don't realise was missing from your desk until it's there.




