Skip to content

Commit

Permalink
Merge pull request #7 from kkostov/feat/improvements
Browse files Browse the repository at this point in the history
Experimental WASM support, Android improvements
  • Loading branch information
kkostov authored Dec 14, 2024
2 parents e512471 + 84443e0 commit 603b50e
Show file tree
Hide file tree
Showing 8 changed files with 3,035 additions and 27 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/gradle.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ jobs:
steps:
- uses: actions/checkout@v3
- name: Validate Gradle Wrapper
uses: gradle/wrapper-validation-action@v1
uses: gradle/actions/wrapper-validation@v3
- uses: actions/cache@v3
with:
path: |
Expand Down
13 changes: 12 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ Gadulka is available from Maven Central at the following coordinates:
implementation("eu.iamkonstantin.kotlin:gadulka:x.x.x")
```

### Example

Instantiate the player and call play!

```kotlin
Expand All @@ -40,6 +42,7 @@ player.stop()
player.release()
```

### Jetpack Compose
Example using Jetpack Compose:

```kotlin
Expand All @@ -66,7 +69,15 @@ fun AudioPlayer(player: GadulkaPlayer = GadulkaPlayer()) {
}
```

For Android, the player needs a reference to the android context. For example, using Koin (or another DI library):
### Android

For Android, the player needs a reference to the android context.

```kotlin
GadulkaPlayer(LocalContext.current)
```

Or using Koin (or another DI library):

```kotlin
factory<GadulkaPlayer> {
Expand Down
12 changes: 12 additions & 0 deletions gadulka/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import com.vanniktech.maven.publish.SonatypeHost
import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
import org.jetbrains.kotlin.gradle.targets.js.dsl.ExperimentalWasmDsl

plugins {
alias(libs.plugins.kotlinMultiplatform)
Expand All @@ -24,6 +25,11 @@ kotlin {
iosX64()
iosArm64()
iosSimulatorArm64()
@OptIn(ExperimentalWasmDsl::class)
wasmJs {
browser()
binaries.executable()
}

sourceSets {
val commonMain by getting {
Expand Down Expand Up @@ -55,6 +61,12 @@ kotlin {
}
}

val wasmJsMain by getting {
dependencies {
implementation(libs.kotlinx.browser)
}
}

androidMain.dependencies {
implementation(libs.androix.media3.exploplayer)
}
Expand Down
38 changes: 15 additions & 23 deletions gadulka/src/androidMain/kotlin/GadulkaAudioPlayer.android.kt
Original file line number Diff line number Diff line change
@@ -1,34 +1,13 @@
package eu.iamkonstantin.kotlin.gadulka

import android.content.ContentResolver
import android.media.MediaPlayer

import android.content.Context
import android.net.Uri
import androidx.media3.common.MediaItem
import androidx.media3.exoplayer.ExoPlayer

//
//actual class AudioPlayer(private val context: Context) {
//
// private val mediaPlayer = ExoPlayer.Builder(context).build()
// private val mediaItems = soundResList.map {
// MediaItem.fromUri(Res.getUri(it))
// }
//
// init {
// mediaPlayer.prepare()
// }
//
// @OptIn(ExperimentalResourceApi::class)
// actual fun playSound(id: Int) {
// mediaPlayer.setMediaItem(mediaItems[id])
// mediaPlayer.play()
// }
//
// actual fun release() {
// mediaPlayer.release()
// }
//}
//
actual class GadulkaPlayer(private val context: Context) {
private val mediaPlayer = ExoPlayer.Builder(context).build()

Expand All @@ -43,7 +22,20 @@ actual class GadulkaPlayer(private val context: Context) {
mediaPlayer.play()
}


/**
* Android-specific implementation of the [play] method which uses a ContentResolver to calculate the Uri of a raw file resource bundled with the app.
*/
fun play(rawResourceId: Int) {
val uri = Uri.Builder()
.scheme(ContentResolver.SCHEME_ANDROID_RESOURCE)
.appendPath("$rawResourceId")
.build().toString()
play(uri)
}

actual fun release() {
mediaPlayer.pause()
mediaPlayer.release()
}

Expand Down
43 changes: 42 additions & 1 deletion gadulka/src/commonMain/kotlin/GadulkaAudioPlayer.kt
Original file line number Diff line number Diff line change
@@ -1,9 +1,50 @@
package eu.iamkonstantin.kotlin.gadulka

/**
* A minimalistic audio player
*
*
* Example:
*
* ```kotlin
val player = GadulkaPlayer()
player.play(url = "...")
player.stop()
player.release()
```
*/
expect class GadulkaPlayer {
/**
* Start playback of the audio resource at the provided [url].
*
* ### Resource URI
*
* Can be a remote HTTP(s) url, or a `files` URI obtained via `Res.getUri("files/sample.mp3")`.
*
* Check the [JetBrains docs on how to store raw](https://www.jetbrains.com/help/kotlin-multiplatform-dev/compose-multiplatform-resources-usage.html#raw-files) files as part of multiplatform project resources.
*
* On Android, you can resolve the resource URI using something like:
*
* ```kotlin
* val uri = Uri.Builder()
.scheme(ContentResolver.SCHEME_ANDROID_RESOURCE)
.appendPath("${R.raw.name_of_your_resource}")
.build().toString()
* ```
*
*/
fun play(url: String)

/**
* Stop playback.
*
* Note: If the player is currently not playing, this action has no effect.
*/
fun stop()


/**
* Pause and attempts to perform cleanup in order to dispose of any player resources.
*
*/
fun release()
}
47 changes: 47 additions & 0 deletions gadulka/src/wasmJsMain/kotlin/GadulkaAudioPlayer.wasmJs.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package eu.iamkonstantin.kotlin.gadulka

import kotlinx.browser.document
import kotlinx.browser.window
import kotlinx.dom.appendElement
import kotlinx.dom.appendText
import org.w3c.dom.Element
import org.w3c.dom.HTMLAudioElement
import org.w3c.dom.HTMLInputElement


actual class GadulkaPlayer(val htmlId: String) {
actual fun play(url: String) {
release()
document.body?.appendElement("audio") {
this as HTMLAudioElement
this.id = htmlId
this.src = url
}

val playerEl = getPlayerElement()
playerEl?.play()
}

/**
* Stop playback.
*
* Note: the player element remains in the DOM.
*/
actual fun stop() {
val playerEl = getPlayerElement()
playerEl?.pause()
}

/**
* Stops playback and removes the player element from the DOM.
*/
actual fun release() {
val playerEl = getPlayerElement()
playerEl?.pause()
}

private fun getPlayerElement(): HTMLAudioElement? {
return document.getElementById(htmlId) as? HTMLAudioElement
}

}
3 changes: 2 additions & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@ kotlin = "2.0.21"
android-minSdk = "24"
android-compileSdk = "34"
media3_version = "1.4.1"
kotlinx-browser = "0.3"

[libraries]
kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" }
androix-media3-exploplayer = { group = "androidx.media3", name = "media3-exoplayer", version.ref = "media3_version" }

kotlinx-browser = { module = "org.jetbrains.kotlinx:kotlinx-browser-wasm-js", version.ref = "kotlinx-browser" }

[plugins]
androidLibrary = { id = "com.android.library", version.ref = "agp" }
Expand Down
Loading

0 comments on commit 603b50e

Please sign in to comment.