Skip to content

fix(playback): put rewind and forward on either side of play in system controls - #1232

Merged
ashwkun merged 4 commits into
masterfrom
media-button-slots
Nov 2, 2026
Merged

ashwkun merged 4 commits into
masterfrom
media-button-slots

Conversation

@ashwkun

@ashwkun ashwkun commented Nov 2, 2026 •

Copy link
Copy Markdown
Member

Summary

  • System playback controls (lock screen, notification, Samsung's Now Bar, Android Auto) now always show rewind, play/pause, forward. On Samsung's Now Bar they showed previous, play/pause, rewind whenever nothing was queued next.

Closes #1231

Motivation

  • The session's custom layout gave Media3 rewind, forward and Mark played without slots. Media3 1.10 converts such a layout by its implicit rules: previous is almost always available, so the back slot goes to previous; when no next item exists, the first custom button (rewind) is moved into the forward slot. Samsung's three-button Now Bar shows exactly that.

What changed

  • buildSeekButtons sets SLOT_BACK on rewind and SLOT_FORWARD on forward; Mark played gets SLOT_OVERFLOW.
  • A Next episode button (COMMAND_SKIP_NEXT, SLOT_OVERFLOW) comes last, after Mark played, while the player has a next item; system players that put one extra button on each side of the row (HyperOS) then show it on the right. It runs the session player's seekToNext (the service's handleSkipNext), so the skip setting (just skip / mark played and skip) applies. The service republishes the buttons when the timeline, current item, repeat or shuffle changes whether a next item exists, because skipping with nothing queued stops playback.
  • One list, playbackButtonPreferences, is set as media button preferences in all three places that set buttons: the session builder, updateSeekCommandButtons after a skip-length change, and AutoBrowseLibraryCallback.onConnect (previously setCustomLayout).
  • With a custom button in the back and forward slots, Media3 leaves ACTION_SKIP_TO_PREVIOUS / ACTION_SKIP_TO_NEXT out of the platform playback state and lists rewind and forward first among its custom actions (MediaSessionLegacyStub in 1.10.1), so system players place them around play/pause.
  • core/playback/README.md and release-notes/upcoming.md updated.

Behavior & compatibility

  • Before: previous / play / rewind on Samsung's Now Bar when nothing was queued next; previous and next in the notification's outer slots when both existed. After: rewind / play / forward everywhere, plus Mark played and, while another episode is queued, Next episode. On HyperOS that reads Mark played, rewind, play, forward, Next.
  • Headset, Bluetooth and car next/previous keys are unchanged: AutoBrowseLibraryCallback.onMediaButtonEvent turns them into seek forward/back.
  • Custom skip lengths don't affect placement.

Impact (required)

  • user-impact-critical
  • user-impact-high
  • user-impact-medium
  • user-impact-low
  • no-user-impact

Listener impact

What changes in the user’s life:

  • The lock screen, notification and Samsung's Now Bar show rewind, play and forward in the usual order, so the right-hand button skips forward instead of rewinding.

  • backend-change

Release copy (verbatim)

CHANGELOG.md (required developer copy)

Fixed

  • Media session buttons use media button preferences with explicit slots (rewind SLOT_BACK, forward SLOT_FORWARD, Next episode and Mark played SLOT_OVERFLOW) via playbackButtonPreferences, so system players show rewind, play, forward instead of previous, play, rewind when nothing is queued next; Next episode is offered only while a next item exists.

Listener notes (required for user-impact changes)

  • Listener notes already cover this change

release-notes/upcoming.md gains one line under Fixed.

Test plan

  • :core:playback:testDebugUnitTest (591 tests, including the new PlaybackButtonSlotsTest: order and slots, Next only with a next item, custom skip lengths; and AutoBrowseLibraryCallbackTest for Next on connect and its skip), detekt, :core:playback:lintDebug
  • Phone check (Android 16, one episode playing, nothing queued): the system session offers no previous/next, and its custom actions are rewind, forward, Mark complete in that order
  • HyperOS notification (Android 16): Mark played, rewind, play, forward, Next episode
  • Samsung check (Galaxy S25 Ultra, Android 16, One UI 8.5, Remote Test Lab)
  • Required checks are green before merge, or a maintainer explicitly authorized a check bypass for this PR

Notes (optional)

  • Robolectric doesn't connect a platform MediaController to a session, so the platform playback-state conversion isn't covered by JVM tests; it's Media3's documented slot behavior and needs the device checks above.
  • No window or layout change, so tablet, foldable and resize states are unaffected.

@ashwkun ashwkun added the user-impact-medium Noticeable but not headline — include in README when space allows label Nov 2, 2026
@coderabbitai

coderabbitai Bot commented Nov 2, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Recent review info
Run configuration
Commits

Reviewing files that changed from the base of the PR and between 77106cb and 52f2633.

Files selected for processing: 13
  • core/catalog/src/main/res/drawable/rounded_skip_next_24.xml
  • core/catalog/src/main/res/values/strings.xml
  • core/playback/README.md
  • core/playback/src/main/java/cx/aswin/boxlore/core/playback/service/BoxLorePlaybackService.kt
  • core/playback/src/main/java/cx/aswin/boxlore/core/playback/service/PlaybackServicePlayerFactory.kt
  • core/playback/src/main/java/cx/aswin/boxlore/core/playback/service/auto/AutoBrowseContract.kt
  • core/playback/src/main/java/cx/aswin/boxlore/core/playback/service/auto/AutoBrowseLibraryCallback.kt
  • core/playback/src/main/java/cx/aswin/boxlore/core/playback/service/auto/AutoBrowseLibraryHost.kt
  • core/playback/src/test/java/cx/aswin/boxlore/core/playback/service/BoxLorePlaybackServiceFallbackTest.kt
  • core/playback/src/test/java/cx/aswin/boxlore/core/playback/service/PlaybackButtonSlotsTest.kt
  • core/playback/src/test/java/cx/aswin/boxlore/core/playback/service/PlaybackServicePlayerFactoryTest.kt
  • core/playback/src/test/java/cx/aswin/boxlore/core/playback/service/auto/AutoBrowseLibraryCallbackTest.kt
  • release-notes/upcoming.md

Included review availability: This review used your included allowance. Your plan provides up to 2 included reviews per hour; 0 remain after this review.

Summary

Summary by CodeRabbit

  • New Features
    • Added a Next episode button to lock-screen, notification, Samsung Now Bar, and Android Auto playback controls when another episode is queued. Selecting it skips to the next episode.
  • Bug Fixes
    • Rewind and forward controls now stay in their expected positions across supported system-player and Android Auto sessions, including when custom skip lengths are configured.

Playback sessions now publish media button preferences with explicit back, forward, and overflow slots. The Next episode button appears only when another item is available. Playback updates and Android Auto connections publish the preferences, and Android Auto handles the skip-next command.

Changes: Playback controls

Layer / File(s) Summary
Define button preferences
core/playback/src/main/java/cx/aswin/boxlore/core/playback/service/PlaybackServicePlayerFactory.kt, core/playback/src/test/java/cx/aswin/boxlore/core/playback/service/PlaybackButtonSlotsTest.kt, core/catalog/src/main/res/drawable/rounded_skip_next_24.xml, core/catalog/src/main/res/values/strings.xml, core/playback/src/test/java/cx/aswin/boxlore/core/playback/service/PlaybackServicePlayerFactoryTest.kt
The factory assigns rewind, forward, and Mark Complete to the back, forward, and overflow slots. It creates a skip-next button for the overflow slot and provides ordered preferences with Next episode as an optional button. Tests check slot assignments, optional button behavior, and custom seek lengths.
Publish preferences during playback
core/playback/src/main/java/cx/aswin/boxlore/core/playback/service/BoxLorePlaybackService.kt, core/playback/src/test/java/cx/aswin/boxlore/core/playback/service/BoxLorePlaybackServiceFallbackTest.kt, core/playback/README.md, release-notes/upcoming.md
The playback service publishes preferences after session setup and updates them when relevant player events change next-item availability. The README describes the preferences and testing notes. The release note describes control order and when Next episode appears.
Add skip-next to Android Auto
core/playback/src/main/java/cx/aswin/boxlore/core/playback/service/auto/AutoBrowseContract.kt, core/playback/src/main/java/cx/aswin/boxlore/core/playback/service/auto/AutoBrowseLibraryHost.kt, core/playback/src/main/java/cx/aswin/boxlore/core/playback/service/auto/AutoBrowseLibraryCallback.kt, core/playback/src/test/java/cx/aswin/boxlore/core/playback/service/auto/AutoBrowseLibraryCallbackTest.kt
The Android Auto callback exposes the skip-next command and includes its button only when a next item exists. The command advances the player to the next item. Tests check button availability and command handling.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Change: Bug fix · Severity of issue fixed: Low

Merge Risk: Low · up to 52f26

Hardware keys seek by the configured interval rather than navigating episodes as documented. Queue-driven button updates also need regression coverage; these are bounded follow-ups.

Important

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

Failed Failed checks: 2 errors · 2 warnings

Check name Status Explanation Resolution
Title check Error The title uses the required Conventional Commits format and accurately describes the playback-control change, but it is 79 characters and exceeds the approximate 72-character limit. Shorten the title to about 72 characters or fewer while keeping the imperative mood, for example: "fix(playback): place rewind and forward around play".
Unresolved Review Threads Error Four review findings remain unresolved. Two posted CodeRabbit threads are marked discussion=unresolved: the source-size finding in BoxLorePlaybackService.kt and the regression-test finding in `Playb… Fix all four findings and mark each review thread resolved, or explicitly dismiss each finding with a short rationale.
Out of Scope Changes check Warning The current diff adds a Next episode control that the PR does not describe. The PR description states the result is "rewind / play / forward everywhere, with Mark played in the overflow." The code add… Remove the Next episode path: the skipNext button, the COMMAND_SKIP_NEXT command and handler, the nextEpisode parameter and its call sites, the skip-next drawable and string, and the related README and release-note lines. Keep rewind …
Docstring Coverage Warning Docstring coverage is 16.67% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documenta… Write docstrings for the functions missing them to satisfy the coverage threshold.
Passed Passed checks: 5 passed
Check name Status Explanation
Linked Issues check Passed [#1231] requires system controls in the order skip back, play/pause, skip forward. The current diff meets this in code. buildSeekButtons sets SLOT_BACK on rewind and SLOT_FORWARD on forward. Mar…
Architecture Compliance Passed PASS. The authoritative diff changes only playback service/session code, playback tests/docs, catalog resources, and release notes. It adds no feature-to-feature import or dependency, no PostHog, Hilt…
Module Readme Updated Passed The PR changes production Kotlin under core/playback/src/main/. It also modifies the matching module README, core/playback/README.md, in the same PR. No other changed production Kotlin module lack…
Jvm Tests For Changed Logic Passed The PR adds hermetic Robolectric/JVM tests under core/playback/src/test. PlaybackButtonSlotsTest covers slot assignment, queued and empty queue behavior, and custom skip lengths. `AutoBrowseLibrar…
Description check Passed The pull request description directly explains the playback-control changes, their motivation, affected system controls, implementation details, compatibility behavior, testing, and release notes.
Full details: Linked Issues check

Explanation

[#1231] requires system controls in the order skip back, play/pause, skip forward. The current diff meets this in code. buildSeekButtons sets SLOT_BACK on rewind and SLOT_FORWARD on forward. Mark played uses SLOT_OVERFLOW. playbackButtonPreferences feeds setMediaButtonPreferences in buildMediaLibrarySession and in AutoBrowseLibraryCallback.onConnect. The optional Next episode button uses overflow and does not take the back or forward slot, so it does not change the requested order. The PR states that PlaybackButtonSlotsTest covers slots with custom skip lengths; that test file was not re-read. Samsung Now Bar behavior is unverified on a device, and JVM tests do not cover the platform playback-state conversion. The code evidence supports compliance, so the verdict is PASS.

Full details: Architecture Compliance

Explanation

PASS. The authoritative diff changes only playback service/session code, playback tests/docs, catalog resources, and release notes. It adds no feature-to-feature import or dependency, no PostHog, Hilt, Koin, Dagger, or MockK usage, no second PlaybackRepository or AppContainer graph, and no identity or storage contract. No build or dependency files changed. The catalog additions are resources consumed by core:playback, which matches ARCHITECTURE.md's allowed playback → catalog direction.

Full details: Module Readme Updated

Explanation

The PR changes production Kotlin under core/playback/src/main/. It also modifies the matching module README, core/playback/README.md, in the same PR. No other changed production Kotlin module lacks its matching README.

Full details: Jvm Tests For Changed Logic

Explanation

The PR adds hermetic Robolectric/JVM tests under core/playback/src/test. PlaybackButtonSlotsTest covers slot assignment, queued and empty queue behavior, and custom skip lengths. AutoBrowseLibraryCallbackTest covers queued/empty Next episode visibility and the skip command. The fallback test exercises the SecurityException path. These tests cover the changed playback button logic.

Full details: Unresolved Review Threads

Explanation

Four review findings remain unresolved. Two posted CodeRabbit threads are marked discussion=unresolved: the source-size finding in BoxLorePlaybackService.kt and the regression-test finding in PlaybackButtonSlotsTest.kt; both have Severity: Major. Two newly generated findings are also outstanding: the README hardware-key description is Severity: Minor, and the service-side button-update regression is Severity: Major. No finding is marked resolved or explicitly dismissed.

Resolution

Fix all four findings and mark each review thread resolved, or explicitly dismiss each finding with a short rationale.

Full details: Out of Scope Changes check

Explanation

The current diff adds a Next episode control that the PR does not describe. The PR description states the result is "rewind / play / forward everywhere, with Mark played in the overflow." The code adds: a skipNext CommandButton with SLOT_OVERFLOW and COMMAND_SKIP_NEXT (PlaybackServicePlayerFactory.kt); a nextEpisode parameter in playbackButtonPreferences; a COMMAND_SKIP_NEXT session command and handler in AutoBrowseLibraryCallback.kt; the rounded_skip_next_24.xml drawable; and the auto_skip_next string. [#1231] asks only for skip back, play/pause, and skip forward. The Next episode button adds a new user-visible control that the issue does not request. The release notes text in the README and release-notes/upcoming.md mentions it, but the PR changelog copy does not. The prior out-of-scope PASS does not cover this addition.

Resolution

Remove the Next episode path: the skipNext button, the COMMAND_SKIP_NEXT command and handler, the nextEpisode parameter and its call sites, the skip-next drawable and string, and the related README and release-note lines. Keep rewind in SLOT_BACK, forward in SLOT_FORWARD, and Mark played in SLOT_OVERFLOW, as the PR description states. If the Next episode button is intended, move it to a separate PR. Then update the PR description, changelog copy, and release notes to describe it and to justify it against [#1231].

Full details: Docstring Coverage

Explanation

Docstring coverage is 16.67% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documentation-only edits and deletions within surviving functions are included; unchanged diff context and fully deleted functions are excluded. Analyzed 6 functions across 9 files. (4 skipped: 4 unsupported.)

Resolution

Write docstrings for the functions missing them to satisfy the coverage threshold.

Finishing Touches
Generate docstrings
  • Commit to this branch
  • Create a new PR
  • update changelog

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share

Comment thread core/playback/README.md Resolved
@sonarqubecloud

sonarqubecloud Bot commented Nov 2, 2026

Copy link
Copy Markdown

@ashwkun
ashwkun enabled auto-merge (squash) November 2, 2026 09:05
@ashwkun
ashwkun merged commit cf9f2af into master Nov 2, 2026
16 of 17 checks passed
@ashwkun
ashwkun deleted the media-button-slots branch November 2, 2026 09:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

user-impact-medium Noticeable but not headline — include in README when space allows

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: System media player controls

1 participant