Keep the reading playing when the poem is scrolled, and let it be scrubbed #4

Closed
anas wants to merge 0 commits from fix/recitation-keeps-playing into main
Owner

The bug

The reading stopped the moment you scrolled down the poem.

RecitationPlayer was an item of the LazyColumn, inside the same header item as the breadcrumbs. Scrolling it off screen disposed it, which fired

DisposableEffect(player) { onDispose { player.release() } }

and cut the audio off mid-line.

The fix

The player is now pinned above the scrolling text, between the top bar and the list. It stays in composition for as long as the poem is open, so the reading continues — and it stays in reach while you read further down, which is exactly when a player is wanted.

Seek bar

Added, as asked:

  • position, a draggable handle, and both times (1:23 … 4:56);
  • shown only once the length is known — a bar that cannot be dragged anywhere is furniture;
  • the handle follows the audio on a 250ms poll while playing, and stops fighting the finger while being dragged (scrubbing holds the pending value until the drag ends);
  • MediaPlayer.seekTo already does this, so no media library was needed — the ponytail: note is updated rather than invalidated.

The bar and its two times are laid out left to right inside the otherwise right-to-left page, because time runs that way whatever the script.

Verified

Pinning confirmed on the tablet (2560×1600): the player bar stays at the top while the couplets scroll behind it. Builds clean, 63 tests pass.

Not verified: actual playback continuing across a scroll. I could not get the emulator to start streaming the MP3 (AudioPlaybackConfiguration … state:idle), so the audio path itself is unproven here — the disposal cause is certain from the code, but a real device is the honest check.

## The bug The reading stopped the moment you scrolled down the poem. `RecitationPlayer` was an item of the `LazyColumn`, inside the same header item as the breadcrumbs. Scrolling it off screen disposed it, which fired ```kotlin DisposableEffect(player) { onDispose { player.release() } } ``` and cut the audio off mid-line. ## The fix The player is now **pinned above the scrolling text**, between the top bar and the list. It stays in composition for as long as the poem is open, so the reading continues — and it stays in reach while you read further down, which is exactly when a player is wanted. ## Seek bar Added, as asked: - position, a draggable handle, and both times (`1:23` … `4:56`); - shown **only once the length is known** — a bar that cannot be dragged anywhere is furniture; - the handle follows the audio on a 250ms poll while playing, and stops fighting the finger while being dragged (`scrubbing` holds the pending value until the drag ends); - `MediaPlayer.seekTo` already does this, so no media library was needed — the `ponytail:` note is updated rather than invalidated. The bar and its two times are laid out **left to right** inside the otherwise right-to-left page, because time runs that way whatever the script. ## Verified Pinning confirmed on the tablet (2560×1600): the player bar stays at the top while the couplets scroll behind it. Builds clean, 63 tests pass. **Not verified:** actual playback continuing across a scroll. I could not get the emulator to start streaming the MP3 (`AudioPlaybackConfiguration … state:idle`), so the audio path itself is unproven here — the disposal cause is certain from the code, but a real device is the honest check.
anas added 1 commit 2026-10-07 16:25:46 +00:00
The player was an item of the LazyColumn, so scrolling it off screen disposed
it, and the DisposableEffect that releases the MediaPlayer cut the reading off
mid-line. It is now pinned above the scrolling text: it stays in composition for
as long as the poem is open, and stays in reach while you read further down,
which is when a player is wanted.

A seek bar comes with it — position, a draggable handle, and the two times —
shown only once the length is known, because a bar that cannot be dragged
anywhere is furniture. The handle follows the audio on a 250ms poll while it
plays and stops fighting the finger while it is being dragged. MediaPlayer
already seeks; this needed no media library.

The bar and its times run left to right inside the otherwise right-to-left page,
because time does, whatever the script.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
anas closed this pull request 2026-10-07 16:53:19 +00:00

Pull request closed

Sign in to join this conversation.
No reviewers
No Label
No Milestone
No project
No Assignees
1 Participants
Notifications
Due Date
The due date is invalid or out of range. Please use the format 'yyyy-mm-dd'.

No due date set.

Dependencies

No dependencies set.

Reference: anas/ganjoorandroid#4
No description provided.