JetBrains / compose-multiplatform

Compose Multiplatform, a modern UI framework for Kotlin that makes building performant and beautiful user interfaces easy and enjoyable.
https://jetbrains.com/lp/compose-multiplatform
Apache License 2.0
15.24k stars 1.11k forks source link

"No such file or directory" when using `Res.getUri()` #4877

Closed Sanghyun0505 closed 6 days ago

Sanghyun0505 commented 1 month ago

Describe the bug I was developing multiplatform audio player using MediaPlayer for Android, AVAudioPlayer for iOS. I used new resource api Res.getUri() to get URI of the mp3 file.

actual class AudioPlayer actual constructor(uri: String) {
    private val mediaPlayer = MediaPlayer().apply {
        setDataSource(MainActivity.appContext, uri.toUri())
        prepare()
    }

    actual fun play() = mediaPlayer.start()
}
val audioPlayer = AudioPlayer(Res.getUri("files/Sample.mp3"))

iOS works fine. By the way, there is an issue with Android:

Failed to open file 'jar:file:/data/app/~~iN2KoRgBjEr0KuRV7TmyrT==/com.example.audioplayer-1F2AEdsqRz5PTffEv0pF3t==/base.apk!/files/Sample.mp3'. (No such file or directory)

Affected platforms

Versions

terrakok commented 1 month ago

It is the expected behavior, because the resource on the android side is located inside a jar archive. The Uri has the correct path but your player doesn't support files in jar archives. You need to implement a data reading from jars.

terrakok commented 1 month ago

You could try something like: https://stackoverflow.com/questions/15055747/play-media-from-from-zip-file-without-unzipping

(I haven't check it.)

egorikftp commented 1 month ago

I see in the code logic that for fonts we additionally check if file inside "assets"

image

is it possible to add option to store files on Android in assets folder? it will fix the issue otherwise it make sharing files logic useless 🙁

terrakok commented 1 month ago

@egorikftp First of all, the font check is dirty hack I'd like to remove but it is impossible to create a font by a byte array on android.

sharing files

Files are not resources. Compose resources are part of UI.

There is no such thing as "assets" on the JVM platform. What you ask is a platform dependent solution. If you want to add file to android assets you may use the android assets folder.

Woren commented 4 weeks ago

For anyone interested I've created issue in Media3/Exoplayer repository. I have almost same use case. More details there.

terrakok commented 3 weeks ago

is it possible to add option to store files on Android in assets folder?

BTW, there is a technical limitation for that: to read android assets users have to provide an android context. it will make the library API more complicated

hichamboushaba commented 3 weeks ago

BTW, there is a technical limitation for that: to read android assets users have to provide an android context. it will make the library API more complicated

@terrakok I think there is still a valid use case for offering support for assets on the resources library, in addition to the reasons above (which are caused by the fact that usage of Jar resources is not common on Android), there have been some issues with the performance of Jar resources in Android (Per my testing, the performance of Jar resources has improved in recent Android versions, but they are still slower by 15% than assets or raw files).

To solve this issue without impacting the API, I think there are some approaches:

  1. Require passing Context for base functions, but make the common functions Composables, and make use of LocalContext.current in the Android implementations.
  2. Introduce a notion of PlatformContext in the library, and require the client apps to pass it in the APIs

What do you think about these ideas?

hichamboushaba commented 3 weeks ago

If someone is looking for some workarounds for the MediaPlayer issue, the issue created by @Woren has some possible solutions, and I shared one additional workaround there too.