Skip to content

android: keep the connection while phone as speaker is playing - #39

Merged
thtbee merged 4 commits into
thtbee:mainfrom
Abinanthan-CG:fix/android-speaker-keeps-connection
Oct 23, 2026
Merged

thtbee merged 4 commits into
thtbee:mainfrom
Abinanthan-CG:fix/android-speaker-keeps-connection

Conversation

@Abinanthan-CG

Copy link
Copy Markdown
Contributor

Follow-up to #22, the phone as speaker case thtbee mentioned in the review.

With "Run in the background" off, the core stopped about 5 s after leaving the app even while PC audio was playing on the phone, because it wasn't part of busy.

What changed

  • PcSpeakerPlayer has a playing flow that is true while audio frames arrived in the last 3 s. playFrame only does a timestamp write and flips the flow when it was false, no timer or allocation per frame
  • one watchdog thread (only exists while playing) sets it back to false after 3 s with no frames, stop() resets it right away
  • Core.busy takes playing as a fifth input, so the existing 5 s delay + 2 s debounce wait for the audio to end
  • small ActivityWindow helper with an injectable clock, with unit tests

Tested on Oppo, Android 11, setting off

  • phone as speaker playing, left the app, still connected and playing after 15 s
  • stopped the audio, disconnected a few seconds later
  • no audio, left the app, disconnects after about 5 s like before
  • setting on: no change

Gradle tests, lint and header check pass.

Signed-off-by: abinanthan-cg <dabinanthan@gmail.com>
@Abinanthan-CG
Abinanthan-CG requested a review from thtbee as a code owner October 21, 2026 01:54
alexfoxtm pushed a commit to alexfoxtm/nectarlink that referenced this pull request Oct 21, 2026
Include recent valid PC speaker frames in the existing busy state, so
Run in the background OFF waits for this supported activity to finish.

Adapted from upstream PR thtbee#39 commit f890fcc.
Preserves the existing PCM validation, lifecycle preference and route gates.

Signed-off-by: abinanthan-cg <dabinanthan@gmail.com>
@jochenkirstaetter

Copy link
Copy Markdown
Contributor

Hi @Abinanthan-CG

PR is working nicely. I patched onto my Linux stacked branches and created smoke test apps, and it's a welcomed change.

Cheers

@thtbee thtbee left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for picking this up, and for the tests on ActivityWindow.

One thing before merging. The PC stops sending frames about 500 ms after the sound goes quiet (pc_audio_stream.rs, the silence tail), so the 3 s window only covers audio that's actually playing. With "Run in the background" off, pausing a song for more than about 8 s (3 s window plus the 5 s stop delay) stops the core, and pressing play on the PC no longer reaches the phone. That's the case people will hit most: pause, read something, play again.

The phone already has a steadier signal. While "Phone as speaker" is on at the PC, its deck.state lists an output with the id nectarlink:phone_speaker (desktop/app/src/deck.rs, sample_live_state), and that lands in each device's deckState.outputDevices in CoreState. Could busy count that too, for any connected PC? Something like:

state.map { s -> s.devices.any { d -> d.deckState?.outputDevices?.any { it.id == "nectarlink:phone_speaker" } == true } }
    .distinctUntilChanged()

Then the frame window can stay as a backup for the gap before the first deck.state arrives. A test for the new flow (relay on keeps it busy with no frames, relay off lets it go) would be good too.

The watchdog thread looks right to me, including the restart in finally when frames arrive while it's exiting.

The PC stops sending audio about 500 ms into silence, so a paused song
let the frame window run out and, with "Run in the background" off,
the core stopped and Play on the PC no longer reached the phone. Busy
now also counts a connected PC whose deck state lists the phone as an
output; the frame window covers the moment before the first deck state
arrives.
@thtbee

thtbee commented Oct 23, 2026

Copy link
Copy Markdown
Owner

Thanks again for this, Abinanthan. To get it into the next release I added the change from my review myself (a696a48): busy now also counts a connected PC whose deck state lists the phone as an output (CoreState.pcSpeakerRelayOn), so a paused song no longer lets the core stop, and your frame window still covers the moment before the first deck state arrives. There's a unit test for relay on, off, no deck state yet, and an offline PC. I also merged main into the branch. I'll merge once CI is green; your commits stay yours.

@thtbee thtbee left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks Abinanthan. Approving with the relay check added; CI is green.

@thtbee
thtbee merged commit f37e6b9 into thtbee:main Oct 23, 2026
9 checks passed
@Abinanthan-CG

Copy link
Copy Markdown
Contributor Author

thanks @thtbee for merging

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.

3 participants