Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 24 additions & 24 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,14 @@ plugins {
id("dagger.hilt.android.plugin")
}

var composeVersion: String = "1.5.4"

android {
namespace = "com.rcudev.simplemediaplayer"
compileSdk = 34
compileSdk = 35

defaultConfig {
applicationId = "com.rcudev.simplemediaplayer"
minSdk = 26
targetSdk = 34
targetSdk = 35
versionCode = 1
versionName = "1.0"

Expand All @@ -41,7 +39,7 @@ android {
compose = true
}
composeOptions {
kotlinCompilerExtensionVersion = "1.4.4"
kotlinCompilerExtensionVersion = "1.5.13"
}
packagingOptions {
resources.excludes.add("META-INF/{AL2.0,LGPL2.1}")
Expand All @@ -51,33 +49,35 @@ android {
dependencies {
implementation(project(":player-service"))

implementation("androidx.core:core-ktx:1.12.0")
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.6.2")
implementation("androidx.core:core-ktx:1.13.1")
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.8.6")

// Compose
implementation("androidx.activity:activity-compose:1.8.1")
implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.6.2")
implementation("androidx.lifecycle:lifecycle-runtime-compose:2.6.2")
implementation("androidx.compose.ui:ui:1.5.4")
implementation("androidx.compose.ui:ui-tooling-preview:$composeVersion")
implementation("androidx.compose.material3:material3:1.1.2")
implementation("androidx.navigation:navigation-compose:2.7.5")
implementation("androidx.hilt:hilt-navigation-compose:1.1.0")
implementation(platform("androidx.compose:compose-bom:2024.09.02"))
implementation("androidx.compose.ui:ui")
implementation("androidx.compose.ui:ui-tooling-preview")
implementation("androidx.compose.material3:material3")

implementation("androidx.activity:activity-compose:1.9.2")
implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.8.6")
implementation("androidx.lifecycle:lifecycle-runtime-compose:2.8.6")
implementation("androidx.navigation:navigation-compose:2.8.1")
implementation("androidx.hilt:hilt-navigation-compose:1.2.0")

// Hilt
implementation("com.google.dagger:hilt-android:2.46.1")
kapt("com.google.dagger:hilt-compiler:2.46.1")
implementation("com.google.dagger:hilt-android:2.52")
kapt("com.google.dagger:hilt-compiler:2.52")

// Media3
implementation("androidx.media3:media3-session:1.2.0")
implementation("androidx.media3:media3-session:1.4.1")

// Coil
implementation("io.coil-kt:coil-compose:2.4.0")
implementation("io.coil-kt:coil-compose:2.7.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("androidx.compose.ui:ui-test-junit4:$composeVersion")
debugImplementation("androidx.compose.ui:ui-tooling:$composeVersion")
debugImplementation("androidx.compose.ui:ui-test-manifest:$composeVersion")
androidTestImplementation("androidx.test.ext:junit:1.2.1")
androidTestImplementation("androidx.test.espresso:espresso-core:3.6.1")
androidTestImplementation("androidx.compose.ui:ui-test-junit4")
debugImplementation("androidx.compose.ui:ui-tooling")
debugImplementation("androidx.compose.ui:ui-test-manifest")
}
5 changes: 0 additions & 5 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@
xmlns:tools="http://schemas.android.com/tools">

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

<application
android:name=".SimpleMediaPlayerApp"
Expand Down Expand Up @@ -33,9 +31,6 @@
android:value="" />
</activity>

<service
android:name="com.rcudev.player_service.service.SimpleMediaService"
android:foregroundServiceType="mediaPlayback" />
</application>

</manifest>
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package com.rcudev.simplemediaplayer.common.ui

sealed class Destination(val route: String) {
object Main: Destination("main")
object Secondary: Destination("secondary")
data object Main: Destination("main")
data object Secondary: Destination("secondary")
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
package com.rcudev.simplemediaplayer.common.ui

import android.content.ComponentName
import android.content.Intent
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.viewModels
import androidx.media3.session.MediaController
import androidx.media3.session.SessionToken
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import com.google.common.util.concurrent.MoreExecutors
import com.rcudev.player_service.service.SimpleMediaService
import com.rcudev.simplemediaplayer.common.ui.theme.SimpleMediaPlayerTheme
import com.rcudev.simplemediaplayer.main.SimpleMediaScreen
Expand All @@ -18,7 +22,6 @@ import dagger.hilt.android.AndroidEntryPoint
class SimpleMediaActivity : ComponentActivity() {

private val viewModel: SimpleMediaViewModel by viewModels()
private var isServiceRunning = false

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Expand All @@ -32,7 +35,6 @@ class SimpleMediaActivity : ComponentActivity() {
SimpleMediaScreen(
vm = viewModel,
navController = navController,
startService = ::startService
)
}
composable(Destination.Secondary.route) {
Expand All @@ -42,18 +44,4 @@ class SimpleMediaActivity : ComponentActivity() {
}
}
}

override fun onDestroy() {
super.onDestroy()
stopService(Intent(this, SimpleMediaService::class.java))
isServiceRunning = false
}

private fun startService() {
if (!isServiceRunning) {
val intent = Intent(this, SimpleMediaService::class.java)
startForegroundService(intent)
isServiceRunning = true
}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package com.rcudev.simplemediaplayer.common.ui

import android.net.Uri
import androidx.compose.runtime.mutableFloatStateOf
import androidx.compose.runtime.mutableLongStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
Expand All @@ -26,22 +28,37 @@ class SimpleMediaViewModel @Inject constructor(
savedStateHandle: SavedStateHandle
) : ViewModel() {

var duration by savedStateHandle.saveable { mutableStateOf(0L) }
var progress by savedStateHandle.saveable { mutableStateOf(0f) }
var duration by savedStateHandle.saveable { mutableLongStateOf(0L) }
var progress by savedStateHandle.saveable { mutableFloatStateOf(0f) }
var progressString by savedStateHandle.saveable { mutableStateOf("00:00") }
var isPlaying by savedStateHandle.saveable { mutableStateOf(false) }

private val _uiState = MutableStateFlow<UIState>(UIState.Initial)
val uiState = _uiState.asStateFlow()

init {
viewModelScope.launch {
loadData()
simpleMediaServiceHandler.connect(
callBack = { connected, playing ->
when {
connected && playing -> {
println("VM.init - simpleMediaServiceHandler is connected and is playing")
}
connected && !playing -> {
println("VM.init - simpleMediaServiceHandler is connected. Load data")
loadData()
}
else -> {
println("VM.init - simpleMediaServiceHandler failed to connect")
}
}
}
)

viewModelScope.launch {
simpleMediaServiceHandler.simpleMediaState.collect { mediaState ->
when (mediaState) {
is SimpleMediaState.Buffering -> calculateProgressValues(mediaState.progress)
SimpleMediaState.Initial -> _uiState.value = UIState.Initial
is SimpleMediaState.Buffering -> calculateProgressValues(mediaState.progress)
is SimpleMediaState.Playing -> isPlaying = mediaState.isPlaying
is SimpleMediaState.Progress -> calculateProgressValues(mediaState.progress)
is SimpleMediaState.Ready -> {
Expand All @@ -54,9 +71,8 @@ class SimpleMediaViewModel @Inject constructor(
}

override fun onCleared() {
viewModelScope.launch {
simpleMediaServiceHandler.onPlayerEvent(PlayerEvent.Stop)
}
println("VM.onCleared")
simpleMediaServiceHandler.release()
}

fun onUIEvent(uiEvent: UIEvent) = viewModelScope.launch {
Expand Down Expand Up @@ -95,7 +111,7 @@ class SimpleMediaViewModel @Inject constructor(
.setFolderType(MediaMetadata.FOLDER_TYPE_ALBUMS)
.setArtworkUri(Uri.parse("https://i.pinimg.com/736x/4b/02/1f/4b021f002b90ab163ef41aaaaa17c7a4.jpg"))
.setAlbumTitle("SoundHelix")
.setDisplayTitle("Song 1")
.setTitle("Song 1")
.build()
).build()

Expand All @@ -117,17 +133,16 @@ class SimpleMediaViewModel @Inject constructor(
simpleMediaServiceHandler.addMediaItem(mediaItem)
//simpleMediaServiceHandler.addMediaItemList(mediaItemList)
}

}

sealed class UIEvent {
object PlayPause : UIEvent()
object Backward : UIEvent()
object Forward : UIEvent()
data object PlayPause : UIEvent()
data object Backward : UIEvent()
data object Forward : UIEvent()
data class UpdateProgress(val newProgress: Float) : UIEvent()
}

sealed class UIState {
object Initial : UIState()
object Ready : UIState()
}
data object Initial : UIState()
data object Ready : UIState()
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@ import androidx.compose.foundation.layout.*
import androidx.compose.material3.Slider
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableFloatStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.rcudev.simplemediaplayer.common.ui.UIEvent
Expand All @@ -17,21 +20,21 @@ internal fun PlayerBar(
progressString: String,
onUiEvent: (UIEvent) -> Unit
) {
val newProgressValue = remember { mutableStateOf(0f) }
val useNewProgressValue = remember { mutableStateOf(false) }
var newProgressValue by remember { mutableFloatStateOf(0f) }
var useNewProgressValue by remember { mutableStateOf(false) }

Column(
modifier = Modifier.fillMaxWidth()
) {
Slider(
value = if (useNewProgressValue.value) newProgressValue.value else progress,
value = if (useNewProgressValue) newProgressValue else progress,
onValueChange = { newValue ->
useNewProgressValue.value = true
newProgressValue.value = newValue
useNewProgressValue = true
newProgressValue = newValue
onUiEvent(UIEvent.UpdateProgress(newProgress = newValue))
},
onValueChangeFinished = {
useNewProgressValue.value = false
useNewProgressValue = false
},
modifier = Modifier
.padding(horizontal = 8.dp)
Expand All @@ -46,4 +49,4 @@ internal fun PlayerBar(
Text(text = durationString)
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ import com.rcudev.simplemediaplayer.common.ui.components.SimpleMediaPlayerUI
internal fun SimpleMediaScreen(
vm: SimpleMediaViewModel,
navController: NavController,
startService: () -> Unit,
) {
val state = vm.uiState.collectAsStateWithLifecycle()

Expand All @@ -38,10 +37,6 @@ internal fun SimpleMediaScreen(
.align(Alignment.Center)
)
is UIState.Ready -> {
LaunchedEffect(true) { // This is only call first time
startService()
}

ReadyContent(vm = vm, navController = navController)
}
}
Expand Down
8 changes: 4 additions & 4 deletions build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
buildscript {
dependencies {
classpath("com.google.dagger:hilt-android-gradle-plugin:2.46.1")
classpath("com.google.dagger:hilt-android-gradle-plugin:2.52")
}
}

plugins {
id("com.android.application") version "8.1.1" apply false
id("com.android.library") version "8.1.1" apply false
id("org.jetbrains.kotlin.android") version "1.8.10" apply false
id("com.android.application") version "8.6.1" apply false
id("com.android.library") version "8.6.1" apply false
id("org.jetbrains.kotlin.android") version "2.0.20" apply false
}
2 changes: 1 addition & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#Fri Mar 24 10:06:16 CET 2023
distributionBase=GRADLE_USER_HOME
distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
distributionPath=wrapper/dists
zipStorePath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
17 changes: 10 additions & 7 deletions player-service/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -33,23 +33,26 @@ android {

dependencies {

implementation("androidx.core:core-ktx:1.12.0")
implementation("androidx.core:core-ktx:1.13.1")
implementation("androidx.appcompat:appcompat:1.6.1")
implementation("androidx.legacy:legacy-support-v4:1.0.0") // Needed MediaSessionCompat.Token
implementation("com.google.android.material:material:1.10.0")
implementation("com.google.android.material:material:1.12.0")

// Hilt
implementation("com.google.dagger:hilt-android:2.46.1")
kapt("com.google.dagger:hilt-compiler:2.46.1")
implementation("com.google.dagger:hilt-android:2.51.1")
kapt("com.google.dagger:hilt-compiler:2.51.1")

// Media3
implementation("androidx.media3:media3-exoplayer:1.2.0")
implementation("androidx.media3:media3-ui:1.2.0")
implementation("androidx.media3:media3-session:1.2.0")
implementation("androidx.media3:media3-exoplayer:1.3.1")
implementation("androidx.media3:media3-ui:1.3.1")
implementation("androidx.media3:media3-session:1.3.1")

// Glide
implementation("com.github.bumptech.glide:glide:4.15.1")


implementation("org.jetbrains.kotlinx:kotlinx-coroutines-guava:1.8.1")

testImplementation("junit:junit:4.13.2")
androidTestImplementation("androidx.test.ext:junit:1.1.5")
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
Expand Down
Loading