Skip to content

feat(playback): add external playback command stream - #194

Open
AndreasLymalmMTM wants to merge 9 commits into
Notalib:mainfrom
mtmse:feat/external-playback-stream
Open

feat(playback): add external playback command stream#194
AndreasLymalmMTM wants to merge 9 commits into
Notalib:mainfrom
mtmse:feat/external-playback-stream

Conversation

@AndreasLymalmMTM

Copy link
Copy Markdown
Contributor

Description

This adds a new stream onExternalPlaybackCommand, that emits playback commands received from headphones, lock screen, etc., such as play, pause, and seeking. It works on iOS and Android.

What Problem does this Solve?

The existing stream onTimebasedPlayerStateChanged gives the current playback state (pause, play, etc.). However, these are emitted when something internal happens as well, e.g. pause is emitted when chapter/file is changed automatically.

Sometimes we would rather want to know when the user actively has performed an action (e.g. play, pause on headphone) and act upon that, and ignore any internal state changes.

See related issue: #40

Example

There is now text in the example project that shows what is received from the stream, displayed for 2 seconds. In the video, Apple Airpods Pro were used to pause, play, seek forward and backward. (Notice how "State" very quickly emits pause and loading when seeking, which comes from internal state changes.)

flutter_readium-external-playback-command.mp4

After goForward(), locators.last may differ from savedLocator while still having the same href. Therefore, we must ensure a new locator is emitted after calling goToLocator().
@ddfreiling

Copy link
Copy Markdown
Member

Hey @AndreasLymalmMTM , sorry for letting this one marinate a bit, we had some overlapping holidays. Could you update it to latest main, then we'll get it merged.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Adds a new cross-platform event stream to the Flutter Readium plugin that surfaces user/system media-control intent (headphones, lock screen, Control Center, Android media session) as structured commands, distinct from the existing timebased playback state stream.

Changes:

  • Introduces ReadiumExternalPlaybackCommand + ExternalPlaybackCommandAction in the platform interface and exposes onExternalPlaybackCommand on FlutterReadium.
  • Implements native emission on iOS (MPRemoteCommandCenter) and Android (Media3 forwarding player + custom notification commands), with Web exposing the stream for compatibility.
  • Updates example UI + docs + changelogs, and adds unit tests for parsing/stream wiring.

Reviewed changes

Copilot reviewed 24 out of 24 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
flutter_readium/test/flutter_readium_test.dart Adds mock stream + unit test for the new Dart-facing API.
flutter_readium/lib/src/flutter_readium_web.dart Exposes the stream on Web for API compatibility (no emission yet).
flutter_readium/lib/flutter_readium.dart Adds public onExternalPlaybackCommand getter + docs.
flutter_readium/ios/.../navigator/NowPlayingInfoUpdater.swift Emits commands from iOS system media controls.
flutter_readium/ios/.../model/ReadiumExternalPlaybackCommand.swift Adds iOS model + mapping to event payload.
flutter_readium/ios/.../FlutterReadiumPlugin.swift Registers/disposes event channel + provides emitter method.
flutter_readium/example/lib/widgets/timebased.state.widget.dart Wires example UI to display last external command.
flutter_readium/example/lib/widgets/external_playback_command_status.widget.dart New widget showing commands for a short duration.
flutter_readium/example/lib/state/player_controls_bloc.dart Exposes the new stream via bloc getter.
flutter_readium/CHANGELOG.md Documents new stream in plugin changelog.
flutter_readium/android/.../ReadiumReader.kt Adds Android event channel plumbing + emitter entrypoint.
flutter_readium/android/.../PluginMediaService.kt Emits commands based on Media3 control operations.
flutter_readium/android/.../events/ReadiumExternalPlaybackCommandEventChannel.kt New Android event channel + command payload model.
flutter_readium_platform_interface/test/models_test.dart Tests enum parsing + JSON round-trip + unknown fallback.
flutter_readium_platform_interface/test/flutter_readium_platform_interface_test.dart Tests MethodChannel stream wiring & subscription behavior.
flutter_readium_platform_interface/lib/src/index.dart Exports the new model.
flutter_readium_platform_interface/lib/src/external_playback_command.dart Adds shared command model + enum parsing/serialization.
flutter_readium_platform_interface/lib/method_channel_flutter_readium.dart Adds event channel + decoding for the new stream.
flutter_readium_platform_interface/lib/flutter_readium_platform_interface.dart Adds abstract onExternalPlaybackCommand contract.
flutter_readium_platform_interface/CHANGELOG.md Documents new shared API in interface changelog.
docs/guides/audiobook-playback.md Adds usage guidance for the new stream.
docs/getting-started/concepts.md Updates streams overview to include the new stream.
docs/api-reference/streams-events.md Adds API reference section for onExternalPlaybackCommand.
docs/api-reference/flutter-readium.md Adds stream to the API reference table.
Suppressed comments (1)

flutter_readium/ios/flutter_readium/Sources/flutter_readium/navigator/NowPlayingInfoUpdater.swift:238

  • nextTrackCommand currently emits .next but then calls navigator.seekForward(). This makes the emitted action inconsistent with the behavior being performed (track skip vs seek-forward). If this handler is meant to perform a seek, emit .seekForward instead.
          emit(.next)

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread docs/guides/audiobook-playback.md
Comment thread docs/api-reference/streams-events.md Outdated
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