Fix Kotlin Native iOS media session APIs

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
sibylla canton 2026-11-03 08:37:56 +01:00
parent 5edafa1201
commit 30baf0cdcf

View File

@ -23,9 +23,9 @@ import kotlinx.cinterop.ExperimentalForeignApi
import platform.AVFAudio.AVAudioSession import platform.AVFAudio.AVAudioSession
import platform.AVFAudio.AVAudioSessionCategoryPlayback import platform.AVFAudio.AVAudioSessionCategoryPlayback
import platform.AVFAudio.AVAudioSessionModeDefault import platform.AVFAudio.AVAudioSessionModeDefault
import platform.Foundation.NSDictionary import platform.AVFAudio.setActive
import platform.Foundation.NSMutableDictionary
import platform.Foundation.NSNotificationCenter import platform.Foundation.NSNotificationCenter
import platform.Foundation.NSNumber
import platform.Foundation.NSOperationQueue import platform.Foundation.NSOperationQueue
import platform.MediaPlayer.MPChangePlaybackPositionCommandEvent import platform.MediaPlayer.MPChangePlaybackPositionCommandEvent
import platform.MediaPlayer.MPMediaItemPropertyAlbumTitle import platform.MediaPlayer.MPMediaItemPropertyAlbumTitle
@ -99,20 +99,21 @@ internal class IosMediaSession {
return return
} }
val info = NSMutableDictionary() val info = buildMap<Any?, Any?> {
info.setObject(item.title, forKey = MPMediaItemPropertyTitle) put(MPMediaItemPropertyTitle, item.title)
info.setObject(item.artist, forKey = MPMediaItemPropertyArtist) put(MPMediaItemPropertyArtist, item.artist)
item.album.takeIf { it.isNotBlank() }?.let { item.album.takeIf { it.isNotBlank() }?.let {
info.setObject(it, forKey = MPMediaItemPropertyAlbumTitle) put(MPMediaItemPropertyAlbumTitle, it)
} }
if (durationSeconds.isFinite() && durationSeconds > 0) { if (durationSeconds.isFinite() && durationSeconds > 0) {
info.setObject(durationSeconds, forKey = MPMediaItemPropertyPlaybackDuration) put(MPMediaItemPropertyPlaybackDuration, durationSeconds)
} }
if (positionSeconds.isFinite() && positionSeconds >= 0) { if (positionSeconds.isFinite() && positionSeconds >= 0) {
info.setObject(positionSeconds, forKey = MPNowPlayingInfoPropertyElapsedPlaybackTime) put(MPNowPlayingInfoPropertyElapsedPlaybackTime, positionSeconds)
} }
info.setObject(if (isPlaying) 1.0 else 0.0, forKey = MPNowPlayingInfoPropertyPlaybackRate) put(MPNowPlayingInfoPropertyPlaybackRate, if (isPlaying) 1.0 else 0.0)
nowPlayingInfoCenter.nowPlayingInfo = info as NSDictionary }
nowPlayingInfoCenter.nowPlayingInfo = info
} }
fun clear() { fun clear() {
@ -130,38 +131,42 @@ internal class IosMediaSession {
} }
private fun registerAudioSessionNotifications() { private fun registerAudioSessionNotifications() {
notificationTokens += notificationCenter.addObserverForName( notificationCenter.addObserverForName(
name = platform.AVFAudio.AVAudioSessionInterruptionNotification, name = platform.AVFAudio.AVAudioSessionInterruptionNotification,
`object` = audioSession, `object` = audioSession,
queue = NSOperationQueue.mainQueue()) { notification -> queue = NSOperationQueue.mainQueue()) { notification ->
val type = notification?.userInfo?.get( val type = notification?.userInfo?.get(
platform.AVFAudio.AVAudioSessionInterruptionTypeKey) as? Number platform.AVFAudio.AVAudioSessionInterruptionTypeKey) as? NSNumber
if (type?.intValue == platform.AVFAudio.AVAudioSessionInterruptionTypeBegan) { if (type?.unsignedLongValue == platform.AVFAudio.AVAudioSessionInterruptionTypeBegan) {
onPause?.invoke() onPause?.invoke()
} else if (type?.intValue == platform.AVFAudio.AVAudioSessionInterruptionTypeEnded) { } else if (type?.unsignedLongValue == platform.AVFAudio.AVAudioSessionInterruptionTypeEnded) {
activate() activate()
val options = notification?.userInfo?.get( val options = notification?.userInfo?.get(
platform.AVFAudio.AVAudioSessionInterruptionOptionKey) as? Number platform.AVFAudio.AVAudioSessionInterruptionOptionKey) as? NSNumber
val shouldResume = (options?.intValue ?: 0) and val shouldResume = options?.unsignedLongValue
platform.AVFAudio.AVAudioSessionInterruptionOptionShouldResume.toInt() != 0 ?.and(platform.AVFAudio.AVAudioSessionInterruptionOptionShouldResume)
?.let { it != 0UL } == true
if (shouldResume) onPlay?.invoke() if (shouldResume) onPlay?.invoke()
} }
} }?.let { notificationTokens += it }
} }
private fun registerRemoteCommands() { private fun registerRemoteCommands() {
remoteCommandTokens += remoteCommandCenter.playCommand to fun register(command: MPRemoteCommand, handler: (platform.MediaPlayer.MPRemoteCommandEvent?) -> platform.MediaPlayer.MPRemoteCommandHandlerStatus) {
remoteCommandCenter.playCommand.addTargetWithHandler { command.addTargetWithHandler(handler)?.let {
remoteCommandTokens += command to it
}
}
register(remoteCommandCenter.playCommand) {
onPlay?.invoke() onPlay?.invoke()
MPRemoteCommandHandlerStatusSuccess MPRemoteCommandHandlerStatusSuccess
} }
remoteCommandTokens += remoteCommandCenter.pauseCommand to register(remoteCommandCenter.pauseCommand) {
remoteCommandCenter.pauseCommand.addTargetWithHandler {
onPause?.invoke() onPause?.invoke()
MPRemoteCommandHandlerStatusSuccess MPRemoteCommandHandlerStatusSuccess
} }
remoteCommandTokens += remoteCommandCenter.changePlaybackPositionCommand to register(remoteCommandCenter.changePlaybackPositionCommand) { event ->
remoteCommandCenter.changePlaybackPositionCommand.addTargetWithHandler { event ->
val position = (event as? MPChangePlaybackPositionCommandEvent)?.positionTime val position = (event as? MPChangePlaybackPositionCommandEvent)?.positionTime
if (position != null && position.isFinite() && position >= 0) { if (position != null && position.isFinite() && position >= 0) {
onSeek?.invoke(position) onSeek?.invoke(position)
@ -170,13 +175,11 @@ internal class IosMediaSession {
MPRemoteCommandHandlerStatusCommandFailed MPRemoteCommandHandlerStatusCommandFailed
} }
} }
remoteCommandTokens += remoteCommandCenter.nextTrackCommand to register(remoteCommandCenter.nextTrackCommand) {
remoteCommandCenter.nextTrackCommand.addTargetWithHandler {
onNext?.invoke() onNext?.invoke()
MPRemoteCommandHandlerStatusSuccess MPRemoteCommandHandlerStatusSuccess
} }
remoteCommandTokens += remoteCommandCenter.previousTrackCommand to register(remoteCommandCenter.previousTrackCommand) {
remoteCommandCenter.previousTrackCommand.addTargetWithHandler {
onPrevious?.invoke() onPrevious?.invoke()
MPRemoteCommandHandlerStatusSuccess MPRemoteCommandHandlerStatusSuccess
} }