Radio 1.2.0: detached-audio playback fixes (BT controls, background streaming) + wakelock/UI polish - #2
Open
fenleon wants to merge 20 commits into
Open
Radio 1.2.0: detached-audio playback fixes (BT controls, background streaming) + wakelock/UI polish#2fenleon wants to merge 20 commits into
fenleon wants to merge 20 commits into
Conversation
…playing The background service acquired a PARTIAL_WAKE_LOCK + high-perf wifi lock in onCreate() and held them until onDestroy(), so a paused or stopped player kept the device awake indefinitely (observed on the LP3: 4h1m wakelock for 24m24s of actual audio, ~123 mAh wasted). Locks are now tied to the player's real state via a Player.Listener: acquired only while playing or buffering, released on pause/stop/end, and the service stops itself when the session detaches (player released). Buffering still holds the lock, so the background-stream suspension fix from d78107a is not regressed.
…p bars, track title marquee - Search and direct-URL entry share one field: a URL plays immediately (host-derived name), anything else searches the Radio Browser directory; the separate AddStationUrlScreen is gone and the bottom bar drops the redundant ADD slot (search · library · bluetooth). - Bluetooth: bottom-right glyph shows connection state — BLUETOOTH_CONNECTED (underline variant, new drawable) when a BT A2DP device is connected, BLUETOOTH otherwise; tap still opens BT settings. State comes from the new permission-free LightBluetooth SDK helper (AudioDeviceCallback + AudioManager.getDevices, no BLUETOOTH permission, started by LightMediaService so the tool never needs Context). - Top bars: back arrows removed; centered titles act as the back affordance on sub-screens (native LightOS grammar), Home is title-only. - Titles clamp at 2 lines; long station names and the current-track line switch to a pause-then-scroll marquee instead of clipping. - Current track (song/artist) from player mediaMetadata (media3 onMediaMetadataChanged), shown when it differs from the station name; LightText gains an onTextLayout passthrough for overflow detection.
The fork previously shipped with the emulator SDK server as its default; on a real LP3 that package does not exist, so all server calls (BT settings, media-session registration) silently failed. com.lightos is LightOS's own SDK server on the device (verified hosting LightSdkService) and implements the standard methods; the emulator flip is documented in the toml comment.
…restored, SDK keyboard, volume panel - Home: top bar removed entirely (tool button minimizes); the 'Radio' title is gone from the Home screen. - Bluetooth: pressing it now opens the system Bluetooth settings directly via the SDK's LightMediaService (the platform server's OpenBluetoothSettings bridge isn't implemented on the LP3 — previously a silent no-op). - Library and Find stations: back buttons restored in the top bars (the tap-the-title affordance wasn't discoverable); the Find stations top bar also drops its redundant search icon (the keyboard submits). - Find stations: the text input now uses the embedded LightSDK keyboard (light-keyboard, same as the old URL screen) instead of the system soft keyboard; search/URL detection and results unchanged. - Volume controls (from audiobooks): new permission-free LightVolume SDK helper (VOLUME_CHANGED_ACTION receiver + AudioManager reads, started by LightMediaService); a shared RadioBaseViewModel owns the panel state and shows it instantly on rocker presses before letting the key reach LightOS; the canonical VolumePanelOverlay (tools/volume-panel, synced via tools/sync-volume-panel) is hosted on every screen's root.
…arch keyboard
- Home: the 'Radio' top-bar title is back (title only, no back button).
- Library (and Find stations results): the scrollbar is flush to the screen
edge — the 24dp side padding moved from the scroll view to its content.
- Find stations keyboard:
- hides after submitting a search (tap the input row to bring it back,
results then use the full height);
- sits flush at the bottom (the bottom-bar search button is gone — the
keyboard's return submits);
- loads in caps mode (setCapsMode(true)) and drops the emoji row
(KeyboardOptions with an empty emoji list) — matching the native
podcast keyboard;
- light-keyboard bumped v0.0.16 -> v0.0.18 (the version the real SDK
uses; source-compatible).
…, no premature 'no results'
- Library: the active tab underline now hugs the label (was a wide 60% bar);
the delete X is flush right (row padding moved onto the name column).
- Find stations:
- 'No results found' only shows after a search has actually completed
(new hasSearched flag) — no more false message while typing;
- mic button removed from the keyboard (displayVoice = false);
- search icon restored in the bottom bar, below the keyboard;
- podcast-style two-view flow: a 'Find Stations' typing view (input text
bumped to heading size) submits to a separate 'Search Results' page
(back arrow, no query shown); back returns to editing the search;
- title capitalised to 'Find Stations' (results page is 'Search Results').
…put; fix volume panel not showing - Search keyboard: no enter key (displayReturn = false — submit via the bottom-bar search icon); input text dropped from heading to subheading (38sp -> 30sp); the bottom bar gained an optional topMarginUnits param and the search screen uses 0f so the keyboard sits tight against the bar (icon size unchanged). - Volume panel fix (not showing on the LP3): the SDK's LightActivity routes KEYCODE_VOLUME_UP/DOWN to super.onKeyDown, so the key-based trigger in RadioBaseViewModel never fired. The panel is now driven by LightVolume's VOLUME_CHANGED_ACTION receiver — every real volume change (rocker or BT AVRCP) surfaces in the panel; onKeyDown stays as a fallback.
…rd podcast-style; fix panel flash - Volume panel now tracks the ringer stream too: while audio plays the rocker adjusts media (Media panel); with nothing playing it adjusts the ringer (Ringer/Silent/Vibrate panel) — the panel shows whether or not music is playing, as long as the tool is foreground (LightVolume.State extended with ringerLevel/max/mode). - Panel flash on return to Home fixed: only the visible screen's view model reacts to volume changes (screenVisible guard in RadioBaseViewModel, wired via super.onScreenShow in the three screen VMs + onScreenHide/onAppPause). - Rename keyboard now matches the podcast-style keyboard (no mic, enter, or emoji keys — KeyboardOptions with empty emojis, displayReturn/Voice false); submit via SAVE.
…l flash - LightActivity sets volume control stream to STREAM_MUSIC — while the radio tool is foreground the rocker always adjusts media volume, playing or not (Android would route it to the ringer when nothing plays). - Startup flash fixed: LightVolume.State is now 'seeded' only after the first real read; the panel trigger ignores transitions from an unseeded state (the view-model collector can start before LightMediaService's observe() seeds the volumes — previously the default 0/0 -> real read looked like a change and flashed the panel on app load).
Copied from the podcast app's search screen (captured 2026-08-19, 1080x1240): - Input text: subheading size at ~y300 (was right under the top bar) — a 4.5-unit spacer above it, label removed (the podcast has none). - Side padding 2 grid units (~80px), underline 2dp (~6px, full width) — measured from the podcast's 81px padding / 6px line. - The bottom-bar search button is gone: the search icon now floats centered ~80px below the keyboard (5.75-unit area, icon 2 units), like the podcast. - The keyboard keeps caps-start / no emoji / no mic / no enter.
…e passes - The 'Search stations or enter a URL' hint is back, sitting just above the input (the podcast-style input position is kept). - The bottom area now follows the passes add-code editor: the search icon sits in a bottom bar flush at the screen bottom with the keyboard resting directly on it (topMarginUnits = 0) — replacing the floating podcast icon.
…H bar) Root-caused from LP3 captures: the library's keyboard wrapper renders LP3_KEYBOARD_HEIGHT_DP + max(additionalBottomHeight, 36dp). The real SDK (passes) passes additionalBottomHeight = 5 units and nests the bar inside the keyboard's zone, which puts the keys ~55px lower than the fork's old layout (keys + separate flush bar). Ported: - LightEmbeddedLp3Keyboard now matches the real SDK: additionalBottomHeight, bottomBar, onOverlayDismissed, overlay params + 10dp top padding. - LightBottomBar's topMarginUnits renamed to topPadding: Dp (real SDK signature). - The search screen renders the keyboard with additionalBottomHeight = 5 units and a bottomBar containing a centered 'SEARCH' text button (passes' 'SAVE' style) — the keyboard now sits at the passes/podcast height with the action nested below the keys.
The submit path flipped to the results view before search()'s own length guard, so an empty query still landed on an empty Search Results page. onSubmit now only navigates when the query is a URL or >= 2 chars.
The auto-scroll LaunchedEffect (present in the original URL screen) was dropped during the search rewrite; long inputs scrolled the cursor off-screen.
togglePlayback now only starts playback after a real station has been loaded (playStation / restored last-played); the default 'No Station Selected' state can't trigger a bogus connect.
- Release build: R8 + resource shrinking on (already configured); the SDK's unused CameraX + ML Kit QR stack is now excluded from the APK (83 MB debug -> 6.25 MB release; dex 4.5 MB, no native bloat). - versionCode 2 / versionName 1.1.0 (lighttool.toml). - README rewritten for the fork: features, architecture, build/install, screens, credits (was upstream light-sdk boilerplate). - Release verified: apksigner (LightSDK dev key), aapt badging, sha256 75dd70e85fd73e022dc2f61f1562cfd0d1b4639a34ea00c40c2cf567c037bec7, emulator smoke test (launch renders; R8 registry keep intact).
Home, Find Stations (keyboard up), Search Results, Library — captured on the real Light Phone 3 (2026-08-19) alongside the upstream captures.
Move playback into the SDK's LightAudioService (detached-audio capability), so the stream survives the tool losing foreground and every tool instance shares one session. Fixes: - BT icon now opens Bluetooth settings (transparent bridge activity) - BT play/pause actions the current station (one shared session) - stop -> exit -> play resumes the same station (no stale sessions) - no double playback (one player, one session) - stream no longer stops ~10s after screen-off / tool inactive Also: LightActivity clears screen ViewModelStores on destroy so the detached handle never leaks across relaunches; audio-demo kept compiling against the new player API.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
20 commits of development + LP3-verified polish on top of
b2d8cab. Everything below is verified on a real Light Phone 3 and the lightos emulator.1.2.0 — detached-audio playback fixes (the big one)
Playback now runs on the SDK's detached-audio model (
capabilities = ["detached-audio"]): the player lives in an SDK-ownedLightAudioService(MediaSessionService), and every tool instance is a controller of one shared session. Fixes the five remaining bugs:BluetoothSettingsActivitybridge launched from the tool's foreground activity (the old staticLightMediaServicepath silently no-op'd).Also:
LightActivity.onDestroyclears screenViewModelStores so the detached handle never leaks across relaunches;LightBluetooth/LightVolumeobservation moved intoLightAudioService; SHOUTcast/HLS MIME hints + ICY metadata preserved in the player.Earlier: the wakelock leak
LightMediaServiceacquired aPARTIAL_WAKE_LOCK+ high-perf wifi lock inonCreate()and held them untilonDestroy()— a paused/stopped player kept the device awake indefinitely. On a real LP3 overnight audit this showed as 4h1m of wakelock for 24m24s of actual audio (~123 mAh wasted). Locks are now tied to the player's real state (held only while playing/buffering, released on pause/stop/end; the service stops itself when the player is released). The original background-suspension behavior is preserved — buffering still holds the lock.UI (feedback-driven, LP3-verified)
additionalBottomHeight+ nestedbottomBar).setVolumeControlStream). Panel is driven byVOLUME_CHANGED_ACTION(the SDK never forwards volume keys to the screen).BLUETOOTH_CONNECTEDunderline variant); the tap opens BT settings through the SDK's bridge activity.SDK (vendored copy in this fork)
LightAudioService,DetachedSessionState,PendingPlayerCommands,DetachedAudioCapability);newPlayer(playback = Detached); the Gradle plugin translatescapabilities = ["detached-audio"]into the FGS permissions, capability marker, and service declaration.LightBluetooth/LightVolume— permission-free audio-framework observers (the tool plugin bans Context; no BLUETOOTH permission needed).LightText.onTextLayoutpassthrough,LightBottomBar.topPadding(matches upstream's newer signature),LightEmbeddedLp3Keyboardported to the current upstream version (additionalBottomHeight/bottomBar/overlay),BLUETOOTH_CONNECTEDicon + drawable,setVolumeControlStreaminLightActivity.serverPackagedefault is nowcom.lightos(real device); emulator flip documented in the toml.Release