From 9c406eaf60ef883c588d17b577a7baef76913abe Mon Sep 17 00:00:00 2001 From: dcvz Date: Wed, 9 Aug 2023 00:18:39 +0200 Subject: [PATCH] Add updated example --- .../.gitignore | 0 kotlin-audio-example/build.gradle.kts | 80 ++++++ .../proguard-rules.pro | 0 .../ExampleInstrumentedTest.kt | 6 +- .../src/main/AndroidManifest.xml | 13 +- .../kotlin_audio_example/MainActivity.kt | 232 ++++++++++++++++++ .../kotlin_audio_example/ext/LongExt.kt | 14 ++ .../ui/component/PlayerControls.kt | 99 ++++++++ .../ui/component/TrackDisplay.kt | 127 ++++++++++ .../kotlin_audio_example/ui/theme/Color.kt | 11 + .../kotlin_audio_example/ui/theme/Theme.kt | 70 ++++++ .../kotlin_audio_example/ui/theme/Type.kt | 34 +++ .../drawable-v24/ic_launcher_foreground.xml | 0 .../res/drawable/ic_launcher_background.xml | 0 .../res/mipmap-anydpi-v26/ic_launcher.xml | 1 + .../mipmap-anydpi-v26/ic_launcher_round.xml | 1 + .../src/main/res/mipmap-hdpi/ic_launcher.webp | Bin .../res/mipmap-hdpi/ic_launcher_round.webp | Bin .../src/main/res/mipmap-mdpi/ic_launcher.webp | Bin .../res/mipmap-mdpi/ic_launcher_round.webp | Bin .../main/res/mipmap-xhdpi/ic_launcher.webp | Bin .../res/mipmap-xhdpi/ic_launcher_round.webp | Bin .../main/res/mipmap-xxhdpi/ic_launcher.webp | Bin .../res/mipmap-xxhdpi/ic_launcher_round.webp | Bin .../main/res/mipmap-xxxhdpi/ic_launcher.webp | Bin .../res/mipmap-xxxhdpi/ic_launcher_round.webp | Bin .../src/main/res/values/colors.xml | 0 .../src/main/res/values/strings.xml | 3 + .../src/main/res/values/themes.xml | 5 + .../kotlin_audio_example}/ExampleUnitTest.kt | 2 +- kotlin-audio-sample/build.gradle | 50 ---- .../kotlin_audio_sample/FirstFragment.kt | 176 ------------- .../kotlin_audio_sample/MainActivity.kt | 35 --- .../kotlin_audio_sample/MainApplication.kt | 12 - .../src/main/res/layout/activity_main.xml | 25 -- .../src/main/res/layout/content_main.xml | 19 -- .../src/main/res/layout/fragment_first.xml | 103 -------- .../src/main/res/layout/fragment_second.xml | 27 -- .../src/main/res/navigation/nav_graph.xml | 14 -- .../src/main/res/raw/kalimba.mp3 | Bin 8414449 -> 0 bytes .../src/main/res/values-night/themes.xml | 16 -- .../src/main/res/values/strings.xml | 11 - .../src/main/res/values/themes.xml | 25 -- .../kotlinaudio/players/BaseAudioPlayer.kt | 4 + settings.gradle | 2 +- 45 files changed, 692 insertions(+), 525 deletions(-) rename {kotlin-audio-sample => kotlin-audio-example}/.gitignore (100%) create mode 100644 kotlin-audio-example/build.gradle.kts rename {kotlin-audio-sample => kotlin-audio-example}/proguard-rules.pro (100%) rename {kotlin-audio-sample/src/androidTest/java/com/doublesymmetry/kotlin_audio_sample => kotlin-audio-example/src/androidTest/java/com/example/kotlin_audio_example}/ExampleInstrumentedTest.kt (75%) rename {kotlin-audio-sample => kotlin-audio-example}/src/main/AndroidManifest.xml (71%) create mode 100644 kotlin-audio-example/src/main/java/com/example/kotlin_audio_example/MainActivity.kt create mode 100644 kotlin-audio-example/src/main/java/com/example/kotlin_audio_example/ext/LongExt.kt create mode 100644 kotlin-audio-example/src/main/java/com/example/kotlin_audio_example/ui/component/PlayerControls.kt create mode 100644 kotlin-audio-example/src/main/java/com/example/kotlin_audio_example/ui/component/TrackDisplay.kt create mode 100644 kotlin-audio-example/src/main/java/com/example/kotlin_audio_example/ui/theme/Color.kt create mode 100644 kotlin-audio-example/src/main/java/com/example/kotlin_audio_example/ui/theme/Theme.kt create mode 100644 kotlin-audio-example/src/main/java/com/example/kotlin_audio_example/ui/theme/Type.kt rename {kotlin-audio-sample => kotlin-audio-example}/src/main/res/drawable-v24/ic_launcher_foreground.xml (100%) rename {kotlin-audio-sample => kotlin-audio-example}/src/main/res/drawable/ic_launcher_background.xml (100%) rename {kotlin-audio-sample => kotlin-audio-example}/src/main/res/mipmap-anydpi-v26/ic_launcher.xml (79%) rename {kotlin-audio-sample => kotlin-audio-example}/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml (79%) rename {kotlin-audio-sample => kotlin-audio-example}/src/main/res/mipmap-hdpi/ic_launcher.webp (100%) rename {kotlin-audio-sample => kotlin-audio-example}/src/main/res/mipmap-hdpi/ic_launcher_round.webp (100%) rename {kotlin-audio-sample => kotlin-audio-example}/src/main/res/mipmap-mdpi/ic_launcher.webp (100%) rename {kotlin-audio-sample => kotlin-audio-example}/src/main/res/mipmap-mdpi/ic_launcher_round.webp (100%) rename {kotlin-audio-sample => kotlin-audio-example}/src/main/res/mipmap-xhdpi/ic_launcher.webp (100%) rename {kotlin-audio-sample => kotlin-audio-example}/src/main/res/mipmap-xhdpi/ic_launcher_round.webp (100%) rename {kotlin-audio-sample => kotlin-audio-example}/src/main/res/mipmap-xxhdpi/ic_launcher.webp (100%) rename {kotlin-audio-sample => kotlin-audio-example}/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp (100%) rename {kotlin-audio-sample => kotlin-audio-example}/src/main/res/mipmap-xxxhdpi/ic_launcher.webp (100%) rename {kotlin-audio-sample => kotlin-audio-example}/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp (100%) rename {kotlin-audio-sample => kotlin-audio-example}/src/main/res/values/colors.xml (100%) create mode 100644 kotlin-audio-example/src/main/res/values/strings.xml create mode 100644 kotlin-audio-example/src/main/res/values/themes.xml rename {kotlin-audio-sample/src/test/java/com/doublesymmetry/kotlin_audio_sample => kotlin-audio-example/src/test/java/com/example/kotlin_audio_example}/ExampleUnitTest.kt (87%) delete mode 100644 kotlin-audio-sample/build.gradle delete mode 100644 kotlin-audio-sample/src/main/java/com/doublesymmetry/kotlin_audio_sample/FirstFragment.kt delete mode 100644 kotlin-audio-sample/src/main/java/com/doublesymmetry/kotlin_audio_sample/MainActivity.kt delete mode 100644 kotlin-audio-sample/src/main/java/com/doublesymmetry/kotlin_audio_sample/MainApplication.kt delete mode 100644 kotlin-audio-sample/src/main/res/layout/activity_main.xml delete mode 100644 kotlin-audio-sample/src/main/res/layout/content_main.xml delete mode 100644 kotlin-audio-sample/src/main/res/layout/fragment_first.xml delete mode 100644 kotlin-audio-sample/src/main/res/layout/fragment_second.xml delete mode 100644 kotlin-audio-sample/src/main/res/navigation/nav_graph.xml delete mode 100644 kotlin-audio-sample/src/main/res/raw/kalimba.mp3 delete mode 100644 kotlin-audio-sample/src/main/res/values-night/themes.xml delete mode 100644 kotlin-audio-sample/src/main/res/values/strings.xml delete mode 100644 kotlin-audio-sample/src/main/res/values/themes.xml diff --git a/kotlin-audio-sample/.gitignore b/kotlin-audio-example/.gitignore similarity index 100% rename from kotlin-audio-sample/.gitignore rename to kotlin-audio-example/.gitignore diff --git a/kotlin-audio-example/build.gradle.kts b/kotlin-audio-example/build.gradle.kts new file mode 100644 index 00000000..a1870e27 --- /dev/null +++ b/kotlin-audio-example/build.gradle.kts @@ -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") +} \ No newline at end of file diff --git a/kotlin-audio-sample/proguard-rules.pro b/kotlin-audio-example/proguard-rules.pro similarity index 100% rename from kotlin-audio-sample/proguard-rules.pro rename to kotlin-audio-example/proguard-rules.pro diff --git a/kotlin-audio-sample/src/androidTest/java/com/doublesymmetry/kotlin_audio_sample/ExampleInstrumentedTest.kt b/kotlin-audio-example/src/androidTest/java/com/example/kotlin_audio_example/ExampleInstrumentedTest.kt similarity index 75% rename from kotlin-audio-sample/src/androidTest/java/com/doublesymmetry/kotlin_audio_sample/ExampleInstrumentedTest.kt rename to kotlin-audio-example/src/androidTest/java/com/example/kotlin_audio_example/ExampleInstrumentedTest.kt index c87baf71..fa18273c 100644 --- a/kotlin-audio-sample/src/androidTest/java/com/doublesymmetry/kotlin_audio_sample/ExampleInstrumentedTest.kt +++ b/kotlin-audio-example/src/androidTest/java/com/example/kotlin_audio_example/ExampleInstrumentedTest.kt @@ -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 @@ -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) } } \ No newline at end of file diff --git a/kotlin-audio-sample/src/main/AndroidManifest.xml b/kotlin-audio-example/src/main/AndroidManifest.xml similarity index 71% rename from kotlin-audio-sample/src/main/AndroidManifest.xml rename to kotlin-audio-example/src/main/AndroidManifest.xml index 00ea177b..eedcfde7 100644 --- a/kotlin-audio-sample/src/main/AndroidManifest.xml +++ b/kotlin-audio-example/src/main/AndroidManifest.xml @@ -1,11 +1,9 @@ - + - + + android:label="@string/app_name" + android:theme="@style/Theme.KotlinAudio"> + diff --git a/kotlin-audio-example/src/main/java/com/example/kotlin_audio_example/MainActivity.kt b/kotlin-audio-example/src/main/java/com/example/kotlin_audio_example/MainActivity.kt new file mode 100644 index 00000000..6d0b872d --- /dev/null +++ b/kotlin-audio-example/src/main/java/com/example/kotlin_audio_example/MainActivity.kt @@ -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 + ) + } +} diff --git a/kotlin-audio-example/src/main/java/com/example/kotlin_audio_example/ext/LongExt.kt b/kotlin-audio-example/src/main/java/com/example/kotlin_audio_example/ext/LongExt.kt new file mode 100644 index 00000000..c972fa85 --- /dev/null +++ b/kotlin-audio-example/src/main/java/com/example/kotlin_audio_example/ext/LongExt.kt @@ -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) + } +} \ No newline at end of file diff --git a/kotlin-audio-example/src/main/java/com/example/kotlin_audio_example/ui/component/PlayerControls.kt b/kotlin-audio-example/src/main/java/com/example/kotlin_audio_example/ui/component/PlayerControls.kt new file mode 100644 index 00000000..2afb5a46 --- /dev/null +++ b/kotlin-audio-example/src/main/java/com/example/kotlin_audio_example/ui/component/PlayerControls.kt @@ -0,0 +1,99 @@ +package com.example.kotlin_audio_example.ui.component + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.rounded.FastForward +import androidx.compose.material.icons.rounded.FastRewind +import androidx.compose.material.icons.rounded.PauseCircle +import androidx.compose.material.icons.rounded.PlayCircle +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import com.example.kotlin_audio_example.ui.theme.KotlinAudioTheme + +@Composable +fun PlayerControls( + modifier: Modifier = Modifier, + onPrevious: () -> Unit = {}, + onNext: () -> Unit = {}, + isPaused: Boolean, + onPlayPause: () -> Unit = {}, +) { + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.Center, + modifier = modifier + ) { + IconButton( + onClick = onPrevious, + modifier = Modifier + .height(48.dp) + .width(48.dp) + ) { + Icon( + Icons.Rounded.FastRewind, + contentDescription = "Previous", + modifier = Modifier + .height(48.dp) + .width(48.dp) + ) + } + Spacer(modifier = Modifier.width(20.dp)) + IconButton( + onClick = onPlayPause, + modifier = Modifier + .height(68.dp) + .width(68.dp) + ) { + Icon( + if (isPaused) { + Icons.Rounded.PlayCircle + } else { + Icons.Rounded.PauseCircle + }, + contentDescription = "Play", + modifier = Modifier + .height(68.dp) + .width(68.dp) + .clip(RoundedCornerShape(50)) + ) + } + Spacer(modifier = Modifier.width(20.dp)) + IconButton( + onClick = onNext, + modifier = Modifier + .height(48.dp) + .width(48.dp) + ) { + Icon( + Icons.Rounded.FastForward, + contentDescription = "Next", + modifier = Modifier + .height(48.dp) + .width(48.dp) + ) + } + } +} + +@Preview(showBackground = true) +@Composable +fun PlayerControlsPreview() { + KotlinAudioTheme { + Column { + PlayerControls(isPaused = true) + PlayerControls(isPaused = false) + } + } +} diff --git a/kotlin-audio-example/src/main/java/com/example/kotlin_audio_example/ui/component/TrackDisplay.kt b/kotlin-audio-example/src/main/java/com/example/kotlin_audio_example/ui/component/TrackDisplay.kt new file mode 100644 index 00000000..0e667b99 --- /dev/null +++ b/kotlin-audio-example/src/main/java/com/example/kotlin_audio_example/ui/component/TrackDisplay.kt @@ -0,0 +1,127 @@ +package com.example.kotlin_audio_example.ui.component + +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Slider +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import coil.compose.AsyncImage +import com.example.kotlin_audio_example.R +import com.example.kotlin_audio_example.ext.millisecondsToString +import com.example.kotlin_audio_example.ui.theme.KotlinAudioTheme + +@Composable +fun TrackDisplay( + title: String, + artist: String, + artwork: String, + position: Long, + duration: Long, + isLive: Boolean, +) { + Column(horizontalAlignment = Alignment.CenterHorizontally) { + if (artwork.isEmpty()) + Image( + painter = painterResource(id = R.drawable.ic_launcher_background), + contentDescription = "Album Cover", + modifier = Modifier + .fillMaxWidth() + .height(240.dp) + .padding(top = 48.dp) + ) + else + AsyncImage( + model = artwork, + contentDescription = "Album Cover", + modifier = Modifier + .fillMaxWidth() + .height(240.dp) + .padding(top = 48.dp) + ) + Text( + text = title, + style = MaterialTheme.typography.titleLarge, + modifier = Modifier.padding(top = 16.dp) + ) + Text( + text = artist, + style = MaterialTheme.typography.bodyMedium, + ) + if (isLive) + Text( + text = "Live", + style = MaterialTheme.typography.bodyLarge, + modifier = Modifier.padding(top = 16.dp, bottom = 16.dp) + ) + else + Column { + Slider( + value = if (duration == 0L) { + 0f + } else { + position.toFloat() / duration.toFloat() + }, + onValueChange = { /*TODO*/ }, + modifier = Modifier + .fillMaxWidth() + .padding(top = 16.dp, start = 8.dp, end = 8.dp) + ) + Row( + modifier = Modifier + .fillMaxWidth() + .padding(top = 0.dp, start = 16.dp, end = 16.dp, bottom = 16.dp), + ) { + Text( + text = position.millisecondsToString(), + style = MaterialTheme.typography.bodyMedium, + ) + Spacer(modifier = Modifier.weight(1f)) + Text( + text = duration.millisecondsToString(), + style = MaterialTheme.typography.bodyMedium, + ) + } + } + } +} + +@Preview(showBackground = true) +@Composable +fun TrackDisplayPreview() { + KotlinAudioTheme { + TrackDisplay( + title = "Title", + artist = "Artist", + artwork = "", + position = 1000, + duration = 6000, + isLive = false, + ) + } +} + +@Preview(showBackground = true) +@Composable +fun TrackDisplayLivePreview() { + KotlinAudioTheme { + TrackDisplay( + title = "Title", + artist = "Artist", + artwork = "", + position = 1000, + duration = 6000, + isLive = true, + ) + } +} diff --git a/kotlin-audio-example/src/main/java/com/example/kotlin_audio_example/ui/theme/Color.kt b/kotlin-audio-example/src/main/java/com/example/kotlin_audio_example/ui/theme/Color.kt new file mode 100644 index 00000000..1aaf9ee1 --- /dev/null +++ b/kotlin-audio-example/src/main/java/com/example/kotlin_audio_example/ui/theme/Color.kt @@ -0,0 +1,11 @@ +package com.example.kotlin_audio_example.ui.theme + +import androidx.compose.ui.graphics.Color + +val Purple80 = Color(0xFFD0BCFF) +val PurpleGrey80 = Color(0xFFCCC2DC) +val Pink80 = Color(0xFFEFB8C8) + +val Purple40 = Color(0xFF6650a4) +val PurpleGrey40 = Color(0xFF625b71) +val Pink40 = Color(0xFF7D5260) \ No newline at end of file diff --git a/kotlin-audio-example/src/main/java/com/example/kotlin_audio_example/ui/theme/Theme.kt b/kotlin-audio-example/src/main/java/com/example/kotlin_audio_example/ui/theme/Theme.kt new file mode 100644 index 00000000..3b77c9d8 --- /dev/null +++ b/kotlin-audio-example/src/main/java/com/example/kotlin_audio_example/ui/theme/Theme.kt @@ -0,0 +1,70 @@ +package com.example.kotlin_audio_example.ui.theme + +import android.app.Activity +import android.os.Build +import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.darkColorScheme +import androidx.compose.material3.dynamicDarkColorScheme +import androidx.compose.material3.dynamicLightColorScheme +import androidx.compose.material3.lightColorScheme +import androidx.compose.runtime.Composable +import androidx.compose.runtime.SideEffect +import androidx.compose.ui.graphics.toArgb +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalView +import androidx.core.view.WindowCompat + +private val DarkColorScheme = darkColorScheme( + primary = Purple80, + secondary = PurpleGrey80, + tertiary = Pink80 +) + +private val LightColorScheme = lightColorScheme( + primary = Purple40, + secondary = PurpleGrey40, + tertiary = Pink40 + + /* Other default colors to override + background = Color(0xFFFFFBFE), + surface = Color(0xFFFFFBFE), + onPrimary = Color.White, + onSecondary = Color.White, + onTertiary = Color.White, + onBackground = Color(0xFF1C1B1F), + onSurface = Color(0xFF1C1B1F), + */ +) + +@Composable +fun KotlinAudioTheme( + darkTheme: Boolean = isSystemInDarkTheme(), + // Dynamic color is available on Android 12+ + dynamicColor: Boolean = true, + content: @Composable () -> Unit +) { + val colorScheme = when { + dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> { + val context = LocalContext.current + if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) + } + + darkTheme -> DarkColorScheme + else -> LightColorScheme + } + val view = LocalView.current + if (!view.isInEditMode) { + SideEffect { + val window = (view.context as Activity).window + window.statusBarColor = colorScheme.primary.toArgb() + WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = darkTheme + } + } + + MaterialTheme( + colorScheme = colorScheme, + typography = Typography, + content = content + ) +} \ No newline at end of file diff --git a/kotlin-audio-example/src/main/java/com/example/kotlin_audio_example/ui/theme/Type.kt b/kotlin-audio-example/src/main/java/com/example/kotlin_audio_example/ui/theme/Type.kt new file mode 100644 index 00000000..9a5ed42b --- /dev/null +++ b/kotlin-audio-example/src/main/java/com/example/kotlin_audio_example/ui/theme/Type.kt @@ -0,0 +1,34 @@ +package com.example.kotlin_audio_example.ui.theme + +import androidx.compose.material3.Typography +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.sp + +// Set of Material typography styles to start with +val Typography = Typography( + bodyLarge = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.Normal, + fontSize = 16.sp, + lineHeight = 24.sp, + letterSpacing = 0.5.sp + ) + /* Other default text styles to override + titleLarge = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.Normal, + fontSize = 22.sp, + lineHeight = 28.sp, + letterSpacing = 0.sp + ), + labelSmall = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.Medium, + fontSize = 11.sp, + lineHeight = 16.sp, + letterSpacing = 0.5.sp + ) + */ +) \ No newline at end of file diff --git a/kotlin-audio-sample/src/main/res/drawable-v24/ic_launcher_foreground.xml b/kotlin-audio-example/src/main/res/drawable-v24/ic_launcher_foreground.xml similarity index 100% rename from kotlin-audio-sample/src/main/res/drawable-v24/ic_launcher_foreground.xml rename to kotlin-audio-example/src/main/res/drawable-v24/ic_launcher_foreground.xml diff --git a/kotlin-audio-sample/src/main/res/drawable/ic_launcher_background.xml b/kotlin-audio-example/src/main/res/drawable/ic_launcher_background.xml similarity index 100% rename from kotlin-audio-sample/src/main/res/drawable/ic_launcher_background.xml rename to kotlin-audio-example/src/main/res/drawable/ic_launcher_background.xml diff --git a/kotlin-audio-sample/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/kotlin-audio-example/src/main/res/mipmap-anydpi-v26/ic_launcher.xml similarity index 79% rename from kotlin-audio-sample/src/main/res/mipmap-anydpi-v26/ic_launcher.xml rename to kotlin-audio-example/src/main/res/mipmap-anydpi-v26/ic_launcher.xml index eca70cfe..6f3b755b 100644 --- a/kotlin-audio-sample/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +++ b/kotlin-audio-example/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -2,4 +2,5 @@ + \ No newline at end of file diff --git a/kotlin-audio-sample/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/kotlin-audio-example/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml similarity index 79% rename from kotlin-audio-sample/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml rename to kotlin-audio-example/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml index eca70cfe..6f3b755b 100644 --- a/kotlin-audio-sample/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +++ b/kotlin-audio-example/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -2,4 +2,5 @@ + \ No newline at end of file diff --git a/kotlin-audio-sample/src/main/res/mipmap-hdpi/ic_launcher.webp b/kotlin-audio-example/src/main/res/mipmap-hdpi/ic_launcher.webp similarity index 100% rename from kotlin-audio-sample/src/main/res/mipmap-hdpi/ic_launcher.webp rename to kotlin-audio-example/src/main/res/mipmap-hdpi/ic_launcher.webp diff --git a/kotlin-audio-sample/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/kotlin-audio-example/src/main/res/mipmap-hdpi/ic_launcher_round.webp similarity index 100% rename from kotlin-audio-sample/src/main/res/mipmap-hdpi/ic_launcher_round.webp rename to kotlin-audio-example/src/main/res/mipmap-hdpi/ic_launcher_round.webp diff --git a/kotlin-audio-sample/src/main/res/mipmap-mdpi/ic_launcher.webp b/kotlin-audio-example/src/main/res/mipmap-mdpi/ic_launcher.webp similarity index 100% rename from kotlin-audio-sample/src/main/res/mipmap-mdpi/ic_launcher.webp rename to kotlin-audio-example/src/main/res/mipmap-mdpi/ic_launcher.webp diff --git a/kotlin-audio-sample/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/kotlin-audio-example/src/main/res/mipmap-mdpi/ic_launcher_round.webp similarity index 100% rename from kotlin-audio-sample/src/main/res/mipmap-mdpi/ic_launcher_round.webp rename to kotlin-audio-example/src/main/res/mipmap-mdpi/ic_launcher_round.webp diff --git a/kotlin-audio-sample/src/main/res/mipmap-xhdpi/ic_launcher.webp b/kotlin-audio-example/src/main/res/mipmap-xhdpi/ic_launcher.webp similarity index 100% rename from kotlin-audio-sample/src/main/res/mipmap-xhdpi/ic_launcher.webp rename to kotlin-audio-example/src/main/res/mipmap-xhdpi/ic_launcher.webp diff --git a/kotlin-audio-sample/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/kotlin-audio-example/src/main/res/mipmap-xhdpi/ic_launcher_round.webp similarity index 100% rename from kotlin-audio-sample/src/main/res/mipmap-xhdpi/ic_launcher_round.webp rename to kotlin-audio-example/src/main/res/mipmap-xhdpi/ic_launcher_round.webp diff --git a/kotlin-audio-sample/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/kotlin-audio-example/src/main/res/mipmap-xxhdpi/ic_launcher.webp similarity index 100% rename from kotlin-audio-sample/src/main/res/mipmap-xxhdpi/ic_launcher.webp rename to kotlin-audio-example/src/main/res/mipmap-xxhdpi/ic_launcher.webp diff --git a/kotlin-audio-sample/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/kotlin-audio-example/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp similarity index 100% rename from kotlin-audio-sample/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp rename to kotlin-audio-example/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp diff --git a/kotlin-audio-sample/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/kotlin-audio-example/src/main/res/mipmap-xxxhdpi/ic_launcher.webp similarity index 100% rename from kotlin-audio-sample/src/main/res/mipmap-xxxhdpi/ic_launcher.webp rename to kotlin-audio-example/src/main/res/mipmap-xxxhdpi/ic_launcher.webp diff --git a/kotlin-audio-sample/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/kotlin-audio-example/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp similarity index 100% rename from kotlin-audio-sample/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp rename to kotlin-audio-example/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp diff --git a/kotlin-audio-sample/src/main/res/values/colors.xml b/kotlin-audio-example/src/main/res/values/colors.xml similarity index 100% rename from kotlin-audio-sample/src/main/res/values/colors.xml rename to kotlin-audio-example/src/main/res/values/colors.xml diff --git a/kotlin-audio-example/src/main/res/values/strings.xml b/kotlin-audio-example/src/main/res/values/strings.xml new file mode 100644 index 00000000..cebb443c --- /dev/null +++ b/kotlin-audio-example/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + Kotlin Audio Example + \ No newline at end of file diff --git a/kotlin-audio-example/src/main/res/values/themes.xml b/kotlin-audio-example/src/main/res/values/themes.xml new file mode 100644 index 00000000..e3188a72 --- /dev/null +++ b/kotlin-audio-example/src/main/res/values/themes.xml @@ -0,0 +1,5 @@ + + + + - \ No newline at end of file diff --git a/kotlin-audio-sample/src/main/res/values/strings.xml b/kotlin-audio-sample/src/main/res/values/strings.xml deleted file mode 100644 index 531276f8..00000000 --- a/kotlin-audio-sample/src/main/res/values/strings.xml +++ /dev/null @@ -1,11 +0,0 @@ - - MainActivity - - KotlinAudio - - Kotlin Audio Demo - Next - Previous - Rewind - Forward - \ No newline at end of file diff --git a/kotlin-audio-sample/src/main/res/values/themes.xml b/kotlin-audio-sample/src/main/res/values/themes.xml deleted file mode 100644 index e64c6b3a..00000000 --- a/kotlin-audio-sample/src/main/res/values/themes.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - -