Skip to content

feat(android): show different OpenGL live content on Home and Lock - #89

Merged
codenameakshay merged 9 commits into
mainfrom
akshay/per-screen-live-wallpapers
Nov 5, 2026
Merged

codenameakshay merged 9 commits into
mainfrom
akshay/per-screen-live-wallpapers

Conversation

@codenameakshay

@codenameakshay codenameakshay commented Nov 5, 2026 •

Copy link
Copy Markdown
Owner

Why

Android 16 lets one live wallpaper service show different content on the Home and Lock screens. Apps cannot set one screen themselves: setWallpaperComponentWithDescription needs the system-only SET_WALLPAPER_COMPONENT, and the stock AOSP live picker never calls Engine.onApplyWallpaper. So each OpenGL engine now keeps its own content per screen.

What changed

  • Each running engine reads its screen (getWallpaperFlags) and that screen's wallpaper id. A new id means the user just set that screen, so it takes the staged content. A known id means a restart, so it keeps its own. Records per screen also keep their textures safe from cleanup.
  • A picker that calls onApplyWallpaper gets a WallpaperDescription with the shader, frame rate, texture folder and tag; that screen's engine then draws it.
  • Dart API: WallpaperCapabilities.supportsPerScreenLiveWallpaper, OpenGlLiveWallpaperRequest.contentTag, WallpaperOperationResult.effectiveTarget, AsyncWallpaper.getLiveWallpaperScreens(). Version 3.7.0.

Scope

Blast Radius

Only the OpenGL wallpaper service and its store change behaviour, and only on API 36. Two engines can hold textures at once, so memory can double while both screens use this wallpaper; each draws only while visible. The schedule branch also touches pigeons/messages.dart, the CHANGELOG and the version; whichever PR merges second rebases and regenerates.

Verification

  • API 36 Pixel 9 emulator, stock picker: Cool shader on Lock, then Warm on Home. getLiveWallpaperScreens() reported Home: warm, Lock: cool, and both screens showed it at once (below).
  • ./gradlew :async_wallpaper:testDebugUnitTest: 217 passed. flutter test: 71 passed; example flutter test: 10 passed. flutter analyze clean, Pigeon bindings current, example debug APK builds.
  • Codex (gpt-6.1-sol) review: 5 fixes with failing-first tests (shared Lock content after a split, cleanup race, fallback reporting, empty snapshot on old Android, tag length), plus a follow-up that keeps described textures until the engine records them.
Home (Warm) Lock (Cool)
home lock

🤖 Generated with Claude Code

codenameakshay and others added 8 commits November 5, 2026 07:38
Android 16 lets one live wallpaper service draw different content on
each screen, but an app cannot set one screen itself: the description
setter needs SET_WALLPAPER_COMPONENT, and the stock AOSP live picker
never calls Engine.onApplyWallpaper.

Each running engine now reads its screen from getWallpaperFlags() and
that screen's wallpaper id. A new id means the user just set the
screen, so the engine takes the staged content; a known id means a
restart, so it keeps its own. Engines record their content per screen,
which also protects their textures from cleanup. A WallpaperDescription
this plugin wrote, for pickers that do call onApplyWallpaper, wins.

Add WallpaperCapabilities.supportsPerScreenLiveWallpaper,
OpenGlLiveWallpaperRequest.contentTag,
WallpaperOperationResult.effectiveTarget and
AsyncWallpaper.getLiveWallpaperScreens(). Android 15 and older keep the
3.6.0 behaviour and report effectiveTarget both.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A picker that calls onApplyWallpaper gets a description before the new
engine starts. If the process died in between and the app staged other
content, cleanup could delete the described textures. Record the
chosen screens with a placeholder id at apply time. Android never
issues wallpaper id 0, so the record cannot pass for a restart.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The engine callbacks only run on the API level that added them, and the
screens query reads getWallpaperInfo(int) only on API 36. Say so with
@RequiresApi and an SDK check so NewApi lint passes at minSdk 24.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@codenameakshay
codenameakshay merged commit b6d43d3 into main Nov 5, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant