Skip to content

Commit

Permalink
Add updated example
Browse files Browse the repository at this point in the history
  • Loading branch information
dcvz committed Aug 8, 2023
1 parent 4caf58f commit 9c406ea
Show file tree
Hide file tree
Showing 45 changed files with 692 additions and 525 deletions.
File renamed without changes.
80 changes: 80 additions & 0 deletions kotlin-audio-example/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
plugins {
id("com.android.application")
id("org.jetbrains.kotlin.android")
}

android {
namespace = "com.example.kotlin_audio_example"
compileSdk = 33

defaultConfig {
applicationId = "com.example.kotlin_audio_example"
minSdk = 21
targetSdk = 33
versionCode = 1
versionName = "1.0"

testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables {
useSupportLibrary = true
}
}

buildTypes {
release {
isMinifyEnabled = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = "1.8"
}
buildFeatures {
compose = true
}
composeOptions {
kotlinCompilerExtensionVersion = "1.3.1"
}
packagingOptions {
resources {
excludes += "/META-INF/{AL2.0,LGPL2.1}"
}
}
}

dependencies {
constraints {
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.8.0") {
because("kotlin-stdlib-jdk7 is now a part of kotlin-stdlib")
}
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.8.0") {
because("kotlin-stdlib-jdk8 is now a part of kotlin-stdlib")
}
}

implementation(project(":kotlin-audio"))
implementation("androidx.core:core-ktx:1.9.0")
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.6.1")
implementation("androidx.activity:activity-compose:1.7.0")
implementation(platform("androidx.compose:compose-bom:2023.03.00"))
implementation("androidx.compose.ui:ui")
implementation("androidx.compose.ui:ui-graphics")
implementation("androidx.compose.ui:ui-tooling-preview")
implementation("androidx.compose.material3:material3")
implementation("androidx.compose.material:material-icons-extended")
implementation("io.coil-kt:coil-compose:2.4.0")
testImplementation("junit:junit:4.13.2")
androidTestImplementation("androidx.test.ext:junit:1.1.5")
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
androidTestImplementation(platform("androidx.compose:compose-bom:2023.03.00"))
androidTestImplementation("androidx.compose.ui:ui-test-junit4")
debugImplementation("androidx.compose.ui:ui-tooling")
debugImplementation("androidx.compose.ui:ui-test-manifest")
}
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.doublesymmetry.kotlin_audio_sample
package com.example.kotlin_audio_example

import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4
Expand All @@ -17,8 +17,8 @@ import org.junit.Assert.*
class ExampleInstrumentedTest {
@Test
fun useAppContext() {
// Context of the kotlin-audio under test.
// Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals(BuildConfig.APPLICATION_ID, appContext.packageName)
assertEquals("com.example.kotlin_audio_example", appContext.packageName)
}
}
Original file line number Diff line number Diff line change
@@ -1,24 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.doublesymmetry.kotlin_audio_sample">
<manifest xmlns:android="http://schemas.android.com/apk/res/android">

<uses-permission android:name="android.permission.INTERNET" />

<application
android:name=".MainApplication"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.KotlinAudio">
<activity
android:name="com.doublesymmetry.kotlin_audio_sample.MainActivity"
android:name=".MainActivity"
android:exported="true"
android:label="@string/title_activity_main"
android:theme="@style/Theme.KotlinAudio.NoActionBar">
android:label="@string/app_name"
android:theme="@style/Theme.KotlinAudio">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,232 @@
package com.example.kotlin_audio_example

import android.content.res.Configuration.UI_MODE_NIGHT_YES
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.doublesymmetry.kotlinaudio.models.AudioPlayerState
import com.doublesymmetry.kotlinaudio.models.DefaultAudioItem
import com.doublesymmetry.kotlinaudio.models.MediaType
import com.doublesymmetry.kotlinaudio.models.RepeatMode
import com.doublesymmetry.kotlinaudio.models.PlayerConfig
import com.doublesymmetry.kotlinaudio.players.QueuedAudioPlayer
import com.example.kotlin_audio_example.ui.component.PlayerControls
import com.example.kotlin_audio_example.ui.component.TrackDisplay
import com.example.kotlin_audio_example.ui.theme.KotlinAudioTheme
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlin.time.Duration.Companion.seconds

class MainActivity : ComponentActivity() {
private lateinit var player: QueuedAudioPlayer

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

player = QueuedAudioPlayer(
this, playerConfig = PlayerConfig(
interceptPlayerActionsTriggeredExternally = true,
handleAudioBecomingNoisy = true,
handleAudioFocus = true
)
)
player.add(tracks)
player.playerOptions.repeatMode = RepeatMode.ALL
player.play()

setContent {
val state = player.event.stateChange.collectAsState(initial = AudioPlayerState.IDLE)
var title by remember { mutableStateOf("") }
var artist by remember { mutableStateOf("") }
var artwork by remember { mutableStateOf("") }
var position by remember { mutableStateOf(0L) }
var duration by remember { mutableStateOf(0L) }
var isLive by remember { mutableStateOf(false) }

Inner(
title = title,
artist = artist,
artwork = artwork,
position = position,
duration = duration,
isLive = isLive,
onPrevious = { player.previous() },
onNext = { player.next() },
isPaused = state.value != AudioPlayerState.PLAYING,
onPlayPause = {
if (player.playerState == AudioPlayerState.PLAYING) {
player.pause()
} else {
player.play()
}
}
)

LaunchedEffect(key1 = player, key2 = player.event.audioItemTransition) {
player.event.audioItemTransition
.onEach {
title = player.currentItem?.title ?: ""
artist = player.currentItem?.artist ?: ""
artwork = player.currentItem?.artwork ?: ""
duration = player.currentItem?.duration ?: 0
isLive = player.isCurrentMediaItemLive
}
.launchIn(this)
}

if (player.playerState == AudioPlayerState.PLAYING) {
LaunchedEffect(Unit) {
while(true) {
position = player.position
duration = player.duration
isLive = player.isCurrentMediaItemLive

delay(1.seconds / 30)
}
}
}
}
}

companion object {
val tracks = listOf(
DefaultAudioItem(
"https://rntp.dev/example/Longing.mp3",
MediaType.DEFAULT,
title = "Longing",
artwork = "https://rntp.dev/example/Longing.jpeg",
artist = "David Chavez",
duration = 143 * 1000,
),
DefaultAudioItem(
"https://rntp.dev/example/Soul%20Searching.mp3",
MediaType.DEFAULT,
title = "Soul Searching (Demo)",
artwork = "https://rntp.dev/example/Soul%20Searching.jpeg",
artist = "David Chavez",
duration = 77 * 1000,
),
DefaultAudioItem(
"https://rntp.dev/example/Lullaby%20(Demo).mp3",
MediaType.DEFAULT,
title = "Lullaby (Demo)",
artwork = "https://rntp.dev/example/Lullaby%20(Demo).jpeg",
artist = "David Chavez",
duration = 71 * 1000,
),
DefaultAudioItem(
"https://rntp.dev/example/Rhythm%20City%20(Demo).mp3",
MediaType.DEFAULT,
title = "Rhythm City (Demo)",
artwork = "https://rntp.dev/example/Rhythm%20City%20(Demo).jpeg",
artist = "David Chavez",
duration = 106 * 1000,
),
DefaultAudioItem(
"https://rntp.dev/example/hls/whip/playlist.m3u8",
MediaType.HLS,
title = "Whip",
artwork = "https://rntp.dev/example/hls/whip/whip.jpeg",
),
DefaultAudioItem(
"https://ais-sa5.cdnstream1.com/b75154_128mp3",
MediaType.DEFAULT,
title = "Smooth Jazz 24/7",
artwork = "https://rntp.dev/example/smooth-jazz-24-7.jpeg",
artist = "New York, NY",
),
)
}
}

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun Inner(
title: String,
artist: String,
artwork: String,
position: Long,
duration: Long,
isLive: Boolean,
onPrevious: () -> Unit = {},
onNext: () -> Unit = {},
isPaused: Boolean,
onPlayPause: () -> Unit = {},
) {
KotlinAudioTheme {
// A surface container using the 'background' color from the theme
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
Column(
modifier = Modifier
.fillMaxSize()
) {
TopAppBar(
title = {
Text(
text = "Kotlin Audio Example",
color = MaterialTheme.colorScheme.onPrimary
)
},
colors = TopAppBarDefaults.smallTopAppBarColors(containerColor = MaterialTheme.colorScheme.primary)
)
TrackDisplay(
title = title,
artist = artist,
artwork = artwork,
position = position,
duration = duration,
isLive = isLive,
)
Spacer(modifier = Modifier.weight(1f))
PlayerControls(
onPrevious = onPrevious,
onNext = onNext,
isPaused = isPaused,
onPlayPause = onPlayPause,
modifier = Modifier
.fillMaxWidth()
.padding(bottom = 60.dp))
}
}
}
}
@Preview(showBackground = true, uiMode = UI_MODE_NIGHT_YES)
@Composable
fun ContentPreview() {
KotlinAudioTheme {
Inner(
title = "Title",
artist = "Artist",
artwork = "",
position = 1000,
duration = 6000,
isLive = false,
isPaused = true
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.example.kotlin_audio_example.ext

fun Long.millisecondsToString(): String {
val seconds = this / 1000
val hours = seconds / 3600
val minutes = (seconds % 3600) / 60
val remainingSeconds = seconds % 60

return if (hours > 0) {
String.format("%02d:%02d:%02d", hours, minutes, remainingSeconds)
} else {
String.format("%02d:%02d", minutes, remainingSeconds)
}
}
Loading

0 comments on commit 9c406ea

Please sign in to comment.