refactor: extract audio player logic into a dedicated multiplatform module

This commit is contained in:
2026-06-16 17:26:48 +02:00
parent 45a8f7ac7f
commit 1de2d43b3b
22 changed files with 96 additions and 33 deletions
+55
View File
@@ -0,0 +1,55 @@
import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
plugins {
alias(libs.plugins.kotlinMultiplatform)
alias(libs.plugins.androidMultiplatformLibrary)
}
kotlin {
iosArm64()
iosSimulatorArm64()
jvm()
js {
browser()
}
@OptIn(ExperimentalWasmDsl::class)
wasmJs {
browser()
}
androidLibrary {
namespace = "fr.ajaury.gwenedeg.core.audioplayer"
compileSdk =
libs.versions.androidCompileSdk
.get()
.toInt()
minSdk =
libs.versions.androidMinSdk
.get()
.toInt()
compilerOptions {
jvmTarget = JvmTarget.JVM_11
}
}
sourceSets {
androidMain.dependencies {
// Player audio
implementation(libs.media3.exoplayer)
implementation(libs.media3.common)
}
commonMain.dependencies {
// DI
implementation(project.dependencies.platform(libs.koin.bom))
implementation(libs.koin.core)
}
commonTest.dependencies {
implementation(libs.kotlin.test)
}
}
}
@@ -3,15 +3,14 @@ package fr.ajaury.gwenedeg.player
import android.content.Context import android.content.Context
import androidx.media3.common.MediaItem import androidx.media3.common.MediaItem
import androidx.media3.exoplayer.ExoPlayer import androidx.media3.exoplayer.ExoPlayer
import gwenedeg.shared.generated.resources.Res
class AndroidAudioPlayer( class AndroidAudioPlayer(
context: Context, context: Context,
) : AudioPlayer { ) : AudioPlayer {
private val player = ExoPlayer.Builder(context).build() private val player = ExoPlayer.Builder(context).build()
override fun load(filePath: String) { override fun load(uri: String) {
val mediaItem = MediaItem.fromUri(Res.getUri(filePath)) val mediaItem = MediaItem.fromUri(uri)
player.setMediaItem(mediaItem) player.setMediaItem(mediaItem)
player.prepare() player.prepare()
} }
@@ -1,4 +1,4 @@
package fr.ajaury.gwenedeg.di package fr.ajaury.gwenedeg.player.di
import fr.ajaury.gwenedeg.player.AndroidAudioPlayer import fr.ajaury.gwenedeg.player.AndroidAudioPlayer
import fr.ajaury.gwenedeg.player.AndroidAudioSessionManager import fr.ajaury.gwenedeg.player.AndroidAudioSessionManager
@@ -7,7 +7,7 @@ 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 audioPlayerModule: Module = module {
single<AudioPlayer> { AndroidAudioPlayer(context = get()) } single<AudioPlayer> { AndroidAudioPlayer(context = get()) }
single<AudioSessionManager> { AndroidAudioSessionManager() } single<AudioSessionManager> { AndroidAudioSessionManager() }
} }
@@ -9,5 +9,5 @@ interface AudioPlayer {
fun release() fun release()
fun load(filePath: String) fun load(uri: String)
} }
@@ -0,0 +1,5 @@
package fr.ajaury.gwenedeg.player.di
import org.koin.core.module.Module
expect val audioPlayerModule: Module
@@ -1,6 +1,5 @@
package fr.ajaury.gwenedeg.player package fr.ajaury.gwenedeg.player
import gwenedeg.shared.generated.resources.Res
import kotlinx.cinterop.ExperimentalForeignApi import kotlinx.cinterop.ExperimentalForeignApi
import platform.AVFoundation.AVPlayer import platform.AVFoundation.AVPlayer
import platform.AVFoundation.pause import platform.AVFoundation.pause
@@ -12,8 +11,7 @@ class IosAudioPlayer : AudioPlayer {
private var player: AVPlayer? = null private var player: AVPlayer? = null
@OptIn(ExperimentalForeignApi::class) @OptIn(ExperimentalForeignApi::class)
override fun load(filePath: String) { override fun load(uri: String) {
val uri = Res.getUri(filePath)
val nsUrl = NSURL.URLWithString(uri) ?: error("Invalid audio URI: $uri") val nsUrl = NSURL.URLWithString(uri) ?: error("Invalid audio URI: $uri")
player = AVPlayer(nsUrl) player = AVPlayer(nsUrl)
@@ -1,4 +1,4 @@
package fr.ajaury.gwenedeg.di package fr.ajaury.gwenedeg.player.di
import fr.ajaury.gwenedeg.player.AudioPlayer import fr.ajaury.gwenedeg.player.AudioPlayer
import fr.ajaury.gwenedeg.player.AudioSessionManager import fr.ajaury.gwenedeg.player.AudioSessionManager
@@ -7,7 +7,7 @@ import fr.ajaury.gwenedeg.player.IosAudioSessionManager
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 audioPlayerModule: Module = module {
single<AudioPlayer> { IosAudioPlayer() } single<AudioPlayer> { IosAudioPlayer() }
single<AudioSessionManager> { IosAudioSessionManager() } single<AudioSessionManager> { IosAudioSessionManager() }
} }
@@ -1,7 +1,7 @@
package fr.ajaury.gwenedeg.player package fr.ajaury.gwenedeg.player
class JsAudioPlayer : AudioPlayer { class JsAudioPlayer : AudioPlayer {
override fun play(file: String) { override fun play() {
TODO("Not yet implemented") TODO("Not yet implemented")
} }
@@ -17,7 +17,7 @@ class JsAudioPlayer : AudioPlayer {
TODO("Not yet implemented") TODO("Not yet implemented")
} }
override fun load(filePath: String) { override fun load(uri: String) {
TODO("Not yet implemented") TODO("Not yet implemented")
} }
} }
@@ -1,6 +1,6 @@
package fr.ajaury.gwenedeg.di package fr.ajaury.gwenedeg.player.di
import org.koin.core.module.Module import org.koin.core.module.Module
actual val platformModule: Module actual val audioPlayerModule: Module
get() = TODO("Not yet implemented") get() = TODO("Not yet implemented")
@@ -1,7 +1,7 @@
package fr.ajaury.gwenedeg.player package fr.ajaury.gwenedeg.player
class JvmAudioPlayer : AudioPlayer { class JvmAudioPlayer : AudioPlayer {
override fun play(file: String) { override fun play() {
TODO("Not yet implemented") TODO("Not yet implemented")
} }
@@ -17,7 +17,7 @@ class JvmAudioPlayer : AudioPlayer {
TODO("Not yet implemented") TODO("Not yet implemented")
} }
override fun load(filePath: String) { override fun load(uri: String) {
TODO("Not yet implemented") TODO("Not yet implemented")
} }
} }
@@ -0,0 +1,6 @@
package fr.ajaury.gwenedeg.player.di
import org.koin.core.module.Module
actual val audioPlayerModule: Module
get() = TODO("Not yet implemented")
@@ -1,7 +1,7 @@
package fr.ajaury.gwenedeg.player package fr.ajaury.gwenedeg.player
class WasmAudioPlayer : AudioPlayer { class WasmAudioPlayer : AudioPlayer {
override fun play(file: String) { override fun play() {
TODO("Not yet implemented") TODO("Not yet implemented")
} }
@@ -17,7 +17,7 @@ class WasmAudioPlayer : AudioPlayer {
TODO("Not yet implemented") TODO("Not yet implemented")
} }
override fun load(filePath: String) { override fun load(uri: String) {
TODO("Not yet implemented") TODO("Not yet implemented")
} }
} }
@@ -0,0 +1,6 @@
package fr.ajaury.gwenedeg.player.di
import org.koin.core.module.Module
actual val audioPlayerModule: Module
get() = TODO("Not yet implemented")
+1
View File
@@ -36,3 +36,4 @@ include(":androidApp")
include(":desktopApp") include(":desktopApp")
include(":webApp") include(":webApp")
include(":shared") include(":shared")
include(":core:audioplayer")
+3 -4
View File
@@ -56,12 +56,11 @@ kotlin {
sourceSets { sourceSets {
androidMain.dependencies { androidMain.dependencies {
implementation(libs.compose.uiToolingPreview) implementation(libs.compose.uiToolingPreview)
// Player audio
implementation(libs.media3.exoplayer)
implementation(libs.media3.common)
} }
commonMain.dependencies { commonMain.dependencies {
// Audio player module
implementation(projects.core.audioplayer)
implementation(libs.compose.runtime) implementation(libs.compose.runtime)
implementation(libs.compose.foundation) implementation(libs.compose.foundation)
implementation(libs.compose.material3) implementation(libs.compose.material3)
@@ -7,9 +7,9 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.navigation3.runtime.NavEntry import androidx.navigation3.runtime.NavEntry
import androidx.navigation3.ui.NavDisplay import androidx.navigation3.ui.NavDisplay
import androidx.savedstate.compose.serialization.serializers.SnapshotStateListSerializer import androidx.savedstate.compose.serialization.serializers.SnapshotStateListSerializer
import fr.ajaury.gwenedeg.di.platformModule
import fr.ajaury.gwenedeg.di.sharedModule import fr.ajaury.gwenedeg.di.sharedModule
import fr.ajaury.gwenedeg.navigation.Route import fr.ajaury.gwenedeg.navigation.Route
import fr.ajaury.gwenedeg.player.di.audioPlayerModule
import fr.ajaury.gwenedeg.player.ui.PlayerScreen import fr.ajaury.gwenedeg.player.ui.PlayerScreen
import fr.ajaury.gwenedeg.records.ui.RecordsScreen import fr.ajaury.gwenedeg.records.ui.RecordsScreen
import fr.ajaury.gwenedeg.theme.GwenedegTheme import fr.ajaury.gwenedeg.theme.GwenedegTheme
@@ -23,7 +23,7 @@ fun App() {
configuration = koinConfiguration { configuration = koinConfiguration {
modules( modules(
sharedModule, sharedModule,
platformModule, audioPlayerModule,
) )
}, },
) { ) {
@@ -4,7 +4,6 @@ import fr.ajaury.gwenedeg.player.ui.viewmodel.PlayerViewModel
import fr.ajaury.gwenedeg.records.data.InMemoryRecordRepository import fr.ajaury.gwenedeg.records.data.InMemoryRecordRepository
import fr.ajaury.gwenedeg.records.domain.RecordRepository import fr.ajaury.gwenedeg.records.domain.RecordRepository
import fr.ajaury.gwenedeg.records.ui.viewmodel.RecordsViewModel import fr.ajaury.gwenedeg.records.ui.viewmodel.RecordsViewModel
import org.koin.core.module.Module
import org.koin.core.module.dsl.bind import org.koin.core.module.dsl.bind
import org.koin.core.module.dsl.factoryOf import org.koin.core.module.dsl.factoryOf
import org.koin.core.module.dsl.viewModelOf import org.koin.core.module.dsl.viewModelOf
@@ -15,5 +14,3 @@ val sharedModule = module {
viewModelOf(::RecordsViewModel) viewModelOf(::RecordsViewModel)
viewModelOf(::PlayerViewModel) viewModelOf(::PlayerViewModel)
} }
expect val platformModule: Module
@@ -3,6 +3,7 @@ package fr.ajaury.gwenedeg.player.ui.viewmodel
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import fr.ajaury.gwenedeg.player.AudioPlayer import fr.ajaury.gwenedeg.player.AudioPlayer
import fr.ajaury.gwenedeg.player.AudioSessionManager import fr.ajaury.gwenedeg.player.AudioSessionManager
import gwenedeg.shared.generated.resources.Res
class PlayerViewModel( class PlayerViewModel(
private val audioPlayer: AudioPlayer, private val audioPlayer: AudioPlayer,
@@ -13,7 +14,7 @@ class PlayerViewModel(
} }
fun play(filePath: String) { fun play(filePath: String) {
audioPlayer.load(filePath = filePath) audioPlayer.load(uri = Res.getUri(filePath))
audioPlayer.play() audioPlayer.play()
} }
@@ -1,4 +0,0 @@
package fr.ajaury.gwenedeg.di
actual val platformModule: org.koin.core.module.Module
get() = TODO("Not yet implemented")