new: add iOS audio player

This commit is contained in:
2026-06-15 18:47:10 +02:00
parent c3347ca3ff
commit b95d81010e
8 changed files with 99 additions and 15 deletions
+3 -3
View File
@@ -11,7 +11,7 @@
/* End PBXFileReference section */ /* End PBXFileReference section */
/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */ /* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */
95ABF2C8EE96D87C60CB4B62 /* PBXFileSystemSynchronizedBuildFileExceptionSet */ = { 95ABF2C8EE96D87C60CB4B62 /* Exceptions for "iosApp" folder in "iosApp" target */ = {
isa = PBXFileSystemSynchronizedBuildFileExceptionSet; isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
membershipExceptions = ( membershipExceptions = (
Info.plist, Info.plist,
@@ -24,7 +24,7 @@
89B7F8019E767C8CDD8E9E81 /* iosApp */ = { 89B7F8019E767C8CDD8E9E81 /* iosApp */ = {
isa = PBXFileSystemSynchronizedRootGroup; isa = PBXFileSystemSynchronizedRootGroup;
exceptions = ( exceptions = (
95ABF2C8EE96D87C60CB4B62 /* PBXFileSystemSynchronizedBuildFileExceptionSet */, 95ABF2C8EE96D87C60CB4B62 /* Exceptions for "iosApp" folder in "iosApp" target */,
); );
path = iosApp; path = iosApp;
sourceTree = "<group>"; sourceTree = "<group>";
@@ -269,7 +269,7 @@
CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_ASSET_PATHS = "\"iosApp/Preview Content\""; DEVELOPMENT_ASSET_PATHS = "\"iosApp/Preview Content\"";
DEVELOPMENT_TEAM = "${TEAM_ID}"; DEVELOPMENT_TEAM = 492RZGQ5SU;
ENABLE_PREVIEWS = YES; ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = iosApp/Info.plist; INFOPLIST_FILE = iosApp/Info.plist;
+4
View File
@@ -4,5 +4,9 @@
<dict> <dict>
<key>CADisableMinimumFrameDurationOnPhone</key> <key>CADisableMinimumFrameDurationOnPhone</key>
<true/> <true/>
<key>UIBackgroundModes</key>
<array>
<string>audio</string>
</array>
</dict> </dict>
</plist> </plist>
@@ -1,10 +1,13 @@
package fr.ajaury.gwenedeg.di package fr.ajaury.gwenedeg.di
import fr.ajaury.gwenedeg.player.AndroidAudioPlayer import fr.ajaury.gwenedeg.player.AndroidAudioPlayer
import fr.ajaury.gwenedeg.player.AndroidAudioSessionManager
import fr.ajaury.gwenedeg.player.AudioPlayer import fr.ajaury.gwenedeg.player.AudioPlayer
import fr.ajaury.gwenedeg.player.AudioSessionManager
import org.koin.core.module.Module import org.koin.core.module.Module
import org.koin.dsl.module import org.koin.dsl.module
actual val platformModule: Module = module { actual val platformModule: Module = module {
single<AudioPlayer> { AndroidAudioPlayer(context = get()) } single<AudioPlayer> { AndroidAudioPlayer(context = get()) }
single<AudioSessionManager> { AndroidAudioSessionManager() }
} }
@@ -0,0 +1,9 @@
package fr.ajaury.gwenedeg.player
class AndroidAudioSessionManager : AudioSessionManager {
override fun activate() {
}
override fun deactivate() {
}
}
@@ -0,0 +1,10 @@
package fr.ajaury.gwenedeg.player
/**
* An Audio Session will allow to request Audio focus and declare our audio source to the system
*/
interface AudioSessionManager {
fun activate()
fun deactivate()
}
@@ -1,6 +1,13 @@
package fr.ajaury.gwenedeg.di package fr.ajaury.gwenedeg.di
import fr.ajaury.gwenedeg.player.AudioPlayer
import fr.ajaury.gwenedeg.player.AudioSessionManager
import fr.ajaury.gwenedeg.player.IosAudioPlayer
import fr.ajaury.gwenedeg.player.IosAudioSessionManager
import org.koin.core.module.Module import org.koin.core.module.Module
import org.koin.dsl.module
actual val platformModule: Module actual val platformModule: Module = module {
get() = TODO("Not yet implemented") single<AudioPlayer> { IosAudioPlayer() }
single<AudioSessionManager> { IosAudioSessionManager() }
}
@@ -1,23 +1,39 @@
package fr.ajaury.gwenedeg.player package fr.ajaury.gwenedeg.player
import gwenedeg.shared.generated.resources.Res
import kotlinx.cinterop.ExperimentalForeignApi
import platform.AVFoundation.AVPlayer
import platform.AVFoundation.pause
import platform.AVFoundation.play
import platform.AVFoundation.replaceCurrentItemWithPlayerItem
import platform.Foundation.NSURL
class IosAudioPlayer : AudioPlayer { class IosAudioPlayer : AudioPlayer {
override fun play(file: String) { private var player: AVPlayer? = null
TODO("Not yet implemented")
@OptIn(ExperimentalForeignApi::class)
override fun load(filePath: String) {
val uri = Res.getUri(filePath)
val nsUrl = NSURL.URLWithString(uri) ?: error("Invalid audio URI: $uri")
player = AVPlayer(nsUrl)
}
override fun play() {
player?.play()
} }
override fun pause() { override fun pause() {
TODO("Not yet implemented") player?.pause()
} }
override fun stop() { override fun stop() {
TODO("Not yet implemented") player?.pause()
player?.replaceCurrentItemWithPlayerItem(null)
} }
override fun release() { override fun release() {
TODO("Not yet implemented") player?.pause()
} player = null
override fun load(file: String) {
TODO("Not yet implemented")
} }
} }
@@ -0,0 +1,35 @@
package fr.ajaury.gwenedeg.player
import kotlinx.cinterop.ExperimentalForeignApi
import platform.AVFAudio.AVAudioSession
import platform.AVFAudio.AVAudioSessionCategoryOptionDuckOthers
import platform.AVFAudio.AVAudioSessionCategoryPlayback
import platform.AVFAudio.AVAudioSessionModeSpokenAudio
import platform.AVFAudio.setActive
class IosAudioSessionManager : AudioSessionManager {
private val session = AVAudioSession.sharedInstance()
@OptIn(ExperimentalForeignApi::class)
override fun activate() {
session.setCategory(
category = AVAudioSessionCategoryPlayback,
mode = AVAudioSessionModeSpokenAudio,
options = AVAudioSessionCategoryOptionDuckOthers,
error = null,
)
session.setActive(
active = true,
error = null,
)
}
@OptIn(ExperimentalForeignApi::class)
override fun deactivate() {
session.setActive(
false,
error = null,
)
}
}