feat(media-control): add module with Linux MPRIS, macOS Now Playing, Windows SMTC#203
Merged
kdroidFilter merged 10 commits intomainfrom Apr 16, 2026
Merged
feat(media-control): add module with Linux MPRIS, macOS Now Playing, Windows SMTC#203kdroidFilter merged 10 commits intomainfrom
kdroidFilter merged 10 commits intomainfrom
Conversation
New module exposing OS-level media controls (play/pause, next/previous, seek, metadata, volume) via the MPRIS2 D-Bus specification. - Kotlin API: MediaControlService, MediaMetadata, MediaPlaybackState, MediaControlEvent (sealed) - Native: C/JNI bridge over GLib/GIO (GDBus), runs a dedicated GMainLoop thread hosting /org/mpris/MediaPlayer2 with both the root and Player interfaces - Events deserialized via kotlinx.serialization (1.11.0 added to the version catalog) - Callback dispatched on the Swing EDT by the library itself - PropertiesChanged auto-emitted on metadata/status/volume changes; Seeked signal emitted on position updates - GraalVM reachability metadata shipped in the JAR - CI: build + verify + upload in build-natives.yaml; download + EXPECTED in pre-merge, publish-maven, publish-plugin, release-graalvm, test-graalvm, test-packaging - Example app: Linux-only MediaControlScreen demo - Docs: docs/runtime/media-control.md + mkdocs nav entry
Implement Windows System Media Transport Controls (SMTC) backend for media controls using WinRT/WRL. Includes: - C++/WRL JNI bridge (nucleus_media_control_windows.dll) with proper COM STA apartment initialization for event marshaling - WindowsBackend integration in MediaControlService - Windows DLL build steps in Gradle (buildNativeWindows task) - Native build script with x64 + ARM64 support - CI workflow steps for build-natives, pre-merge, publish-maven - GraalVM reachability metadata for NativeWindowsBridge - Updated example and docs to reference Windows SMTC support Events supported: Play, Pause, Next, Previous, Stop, SetPosition, SeekBy. AUMID is auto-derived from displayName to ensure stable app identity. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- launcher-windows: use FindClass to resolve ThumbBarClickListener interface instead of GetObjectClass(lambda), fixing NoSuchMethodError under GraalVM native-image - global-hotkey: add ProGuard keep rules for onHotKey static JNI callbacks (Windows/macOS/Linux) - launcher-windows: add ProGuard keep rules for ThumbBarClickListener implementors ProGuard shrinks JNI-only symbols because native code references (FindClass/GetMethodID) are invisible to the obfuscator. Kotlin lambdas implementing fun interfaces produce synthetic $$Lambda classes not registered as JNI-accessible under GraalVM. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ol, scheduler Native code resolves bridges via FindClass(BRIDGE_CLASS) and invokes static callbacks via GetStaticMethodID. ProGuard can't see these references and was stripping the classes, causing JVM access violations when toasts activated.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
New
media-controlruntime module exposing OS-level media controls (play/pause, next/previous, seek, metadata, artwork) through a single cross-platform Kotlin API, backed by three native backends.Cross-platform API
MediaControlService(singleton) withconfigure(),setMetadata(),setPlaybackState(),setVolume(),attach(callback),detach(),isAvailable().MediaMetadata,MediaPlaybackState,MediaPlaybackStatus.MediaControlEvent: Play / Pause / Toggle / Next / Previous / Stop /SeekBy(offsetMs)/SetPosition(positionMs)/SetVolume(volume)/OpenUri(uri)/ Raise / Quit.kotlinx-serialization-json.Linux — MPRIS D-Bus
GDBus). Dedicated thread with its ownGMainContext+GMainLoophosting/org/mpris/MediaPlayer2root + Player interfaces.PropertiesChangedauto-emitted on metadata/status/volume updates;Seekedsignal on position changes.playerctl, media keys.macOS — MPNowPlayingInfoCenter + MPRemoteCommandCenter
MediaPlayer.framework. GCD blocks on the main queue.nowPlayingInfo(title / artist / album / duration / artwork). Artwork URLs fetched asynchronously.Windows — SystemMediaTransportControls (SMTC)
Windows.Media.SystemMediaTransportControls.RoInitialize(RO_INIT_SINGLETHREADED)) pumping Windows messages so WinRT events can marshal back.NucleusApp.aumid("com.app.<packageName>"injected by the Nucleus plugin — identical to the AUMID the plugin stamps on the NSIS/MSI Start Menu shortcut). For APPX/MSIX packages (ExecutableRuntime.isAppX()) the explicit AUMID is skipped so Windows uses the package manifest identity.configure()also patches the Start Menu shortcut{displayName}.lnkto carry the same AUMID inSystem.AppUserModel.ID— a no-op when the plugin has already written it, which it does.ButtonPressed+PlaybackPositionChangeRequested.FastForward/Rewindbuttons map toSeekBy(±10s).Build & packaging
linux-x64,linux-aarch64,darwin-x64,darwin-aarch64,win32-x64,win32-aarch64.buildNativeLinux,buildNativeMacos,buildNativeWindowswired intoprocessResources.reachability-metadata.jsonusing the modern schema (reflection[].type + jniAccessible: true) — resources picked up by the existingnucleus/.*glob ingraalvm-runtime.build-natives.yamlfor all three OS jobs; download +EXPECTEDentries added topre-merge,publish-maven,publish-plugin,release-graalvm,test-graalvm,test-packaging.Sample & docs
example/MediaControlScreen.kt: transport controls, position/volume sliders, live event log; auto-detects backend and shows the platform-appropriate label.docs/runtime/media-control.md: backend internals (GLib main-loop, GCD, STA window pump), event tables, system integration surfaces per OS, ProGuard rules, AUMID/shortcut notes for NSIS vs. APPX.mkdocs.ymlnav updated,README.md+docs/runtime/index.mdindex entries,llms.txt/llms-full.txtregenerated.Test plan
./gradlew :media-control:compileKotlinpasses on all three OSes./gradlew :media-control:ktlintCheck :media-control:detektpasses./gradlew preMergepassesbuild.sh), macOS (build.sh), Windows (build.bat) — x64 + aarch64 eachplayerctl --player=<dbusName> play-pause/next/position 30trigger the matching events;Seekedre-emitted on position changes; GNOME/KDE media applets show metadata and respond to transport./gradlew :example:runshows the track in Control Center and the menu-bar Now Playing widget; F8 / headset Play-Pause triggersToggleNucleusApp.aumidExecutableRuntime.isAppX()returns true → package identity used for the media overlaytest-graalvm.yaml)