Skip to content

Add rewind on pause - #1

Closed
skorokithakis wants to merge 1 commit into
mainfrom
feature/rewind-on-pause
Closed

Add rewind on pause#1
skorokithakis wants to merge 1 commit into
mainfrom
feature/rewind-on-pause

Conversation

@skorokithakis

Copy link
Copy Markdown
Owner

Moves the position back when a book is continued, proportionally to how long that book was paused:

rewind = amount * min(pause, 300s) / 300s

So a momentary pause moves almost nothing, and a pause of five minutes or more moves the full amount.

The setting

"Rewind on pause" in Settings -> Playback -> Seek settings: a toggle and an amount (1-60s, presets 5/10/15/30/60), on by default with 30s. It is part of the settings backup, and the amount is clamped to 1-60 both on read and on import, so an edited backup cannot produce a strange rewind.

This puts the dormant RewindOnPauseTime domain class to use and removes the SeekTimeOption enum, which nothing else referenced.

Per book, not global

The last active time is stored per book (a bookId -> epoch millis map, capped at the 100 newest books). Pausing book A and starting book B must not reset A's clock: the rewind models how much of that book the listener forgot. A book with no stored time gets the full rewind, because "no record" means the listener probably forgot more, not less.

One choke point

The rewind is applied by a thin ForwardingPlayer around the ExoPlayer that the MediaLibrarySession is built on. The seek therefore happens before the audio starts, so there is no audible jump, and every source behaves the same: the app, the notification, media buttons, Android Auto, the widget, the sleep timer, a book switch, and a resume after the process was killed.

Two details in there:

  • It fires only on a real playWhenReady false -> true change, so a repeated play command during a session cannot rewind.
  • The automatic resume after a short audio focus loss (a notification, a navigation prompt, a call, with the "pause" policy) never issues a play command, because ExoPlayer only suppresses playback. That path is handled from the playback suppression reason instead. A buffering stall also stops playback and is deliberately excluded, because nothing was missed. On this one path the audio has already restarted when the seek happens, so a very short blip is possible; avoiding it would need a stop/seek/start dance that risks losing the audio focus.

Notes for review

  • Chapter lengths come from the book, not from the player timeline: the timeline reports C.TIME_UNSET for chapters that ExoPlayer has not prepared yet, which would silently disable the rewind on the first play after opening the app.
  • The book that the player has loaded is tracked in the handler rather than read from the playing item preference. On a book switch the preference already points at the next book before the previous one stops, so reading it in the listener would record the stop against the wrong book.

Tests

Unit tests for the proportional maths and the seek target (including crossing chapter starts and clamping at the start of the book), the preference storage and its cap, the handler (book switch, suppression resume, the playWhenReady gate), the settings backup, and the view model. ./gradlew lintKotlin test passes.

Moves the position back when a book is continued, proportionally to how
long that book was paused: amount * min(pause, 300s) / 300s. A momentary
pause moves almost nothing, a pause of five minutes or more moves the
full amount.

The setting lives in Settings -> Playback -> Seek settings (on by
default, 30s, 1-60s) and is part of the settings backup. It reuses the
dormant RewindOnPauseTime domain class and drops the unused
SeekTimeOption enum.

The last active time is stored per book, so pausing book A and listening
to book B does not reset A. A book with no stored time gets the full
rewind. The map keeps the 100 newest books.

The rewind is applied in one place, a forwarding player around the
ExoPlayer that the media session is built on, so the seek happens before
the audio starts and every source behaves the same: the app, the
notification, media buttons, Android Auto, the widget, and the sleep
timer. It fires only on a real playWhenReady false -> true change, so a
repeated play command cannot rewind. The automatic resume after a short
audio focus loss has no play command, so it is covered separately by the
playback suppression reason; a buffering stall is excluded there.
@skorokithakis

Copy link
Copy Markdown
Owner Author

Superseded by GrakovNe#492, which targets upstream.

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