From c7309e0550b976d94315b30c48c39c10f3782582 Mon Sep 17 00:00:00 2001 From: Ericgacoki Date: Fri, 15 Nov 2024 18:21:50 +0300 Subject: [PATCH 1/2] update compose BOM --- app/build.gradle.kts | 20 ++++++++++---------- auth/build.gradle.kts | 16 ++++++++-------- core/build.gradle.kts | 3 +-- feature/album/build.gradle.kts | 4 ++-- feature/artist/build.gradle.kts | 4 ++-- feature/folder/build.gradle.kts | 4 ++-- feature/home/build.gradle.kts | 6 +++--- feature/player/build.gradle.kts | 16 ++++++++-------- network/build.gradle.kts | 4 ++-- uicomponent/build.gradle.kts | 12 +++++++----- 10 files changed, 45 insertions(+), 44 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index f1b9842..3e3112f 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -7,12 +7,12 @@ plugins { android { namespace = "com.android.swingmusic" - compileSdk = 34 + compileSdk = 35 defaultConfig { applicationId = "com.android.swingmusic" minSdk = 26 - targetSdk = 34 + targetSdk = 35 versionCode = 1 versionName = "1.0.0" @@ -67,11 +67,11 @@ dependencies { // Core // implementation("androidx.core:core-ktx:1.13.1") - implementation("androidx.activity:activity-ktx:1.9.1") - implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.8.4") + implementation("androidx.activity:activity-ktx:1.9.3") + implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.8.7") // Compose - implementation(platform("androidx.compose:compose-bom:2023.08.00")) + implementation(platform("androidx.compose:compose-bom:2024.10.01")) implementation("androidx.compose.ui:ui") implementation("androidx.compose.ui:ui-graphics") implementation("androidx.compose.ui:ui-tooling-preview") @@ -101,7 +101,7 @@ dependencies { implementation("com.jakewharton.timber:timber:5.0.1") // WorkManger - implementation("androidx.work:work-runtime-ktx:2.9.0") + implementation("androidx.work:work-runtime-ktx:2.10.0") implementation("androidx.hilt:hilt-work:1.2.0") // Navigation @@ -110,8 +110,8 @@ dependencies { ksp("io.github.raamcosta.compose-destinations:ksp:1.9.63") // Media3-Player - implementation("androidx.media3:media3-exoplayer:1.4.0") - implementation("androidx.media3:media3-session:1.4.0") + implementation("androidx.media3:media3-exoplayer:1.4.1") + implementation("androidx.media3:media3-session:1.4.1") // implementation("androidx.media3:media3-exoplayer-hls:1.3.1") // implementation("androidx.media3:media3-exoplayer-dash:1.3.1") @@ -121,8 +121,8 @@ dependencies { // Coroutines implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.0") implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.8.0") - implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.8.4") - implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.8.4") + implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.8.7") + implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.8.7") } kotlin { diff --git a/auth/build.gradle.kts b/auth/build.gradle.kts index 3f93329..fad0bb3 100644 --- a/auth/build.gradle.kts +++ b/auth/build.gradle.kts @@ -7,7 +7,7 @@ plugins { android { namespace = "com.android.swingmusic.auth" - compileSdk = 34 + compileSdk = 35 defaultConfig { minSdk = 26 @@ -51,11 +51,11 @@ dependencies { implementation(project(":feature:common")) // Core - implementation("androidx.core:core-ktx:1.13.1") - implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.8.3") + implementation("androidx.core:core-ktx:1.15.0") + implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.8.7") // Compose - implementation(platform("androidx.compose:compose-bom:2023.08.00")) + implementation(platform("androidx.compose:compose-bom:2024.10.01")) implementation("androidx.compose.ui:ui") implementation("androidx.compose.ui:ui-graphics") implementation("androidx.compose.ui:ui-tooling-preview") @@ -82,7 +82,7 @@ dependencies { implementation("androidx.datastore:datastore-preferences:1.1.1") // WorkManger - implementation("androidx.work:work-runtime-ktx:2.9.0") + implementation("androidx.work:work-runtime-ktx:2.10.0") implementation("androidx.hilt:hilt-work:1.2.0") // Timber @@ -94,9 +94,9 @@ dependencies { // Coroutines implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.1") - implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3") - implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.8.3") - implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.8.3") + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.8.0") + implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.8.7") + implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.8.7") } kotlin { diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 90c269e..10de9c3 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -29,8 +29,7 @@ android { } dependencies { - implementation("androidx.core:core-ktx:1.13.1") - // implementation("androidx.appcompat:appcompat:1.7.0") + implementation("androidx.core:core-ktx:1.15.0") testImplementation("junit:junit:4.13.2") androidTestImplementation("androidx.test.ext:junit:1.2.1") androidTestImplementation("androidx.test.espresso:espresso-core:3.6.1") diff --git a/feature/album/build.gradle.kts b/feature/album/build.gradle.kts index 8e4b5e0..da9ecf0 100644 --- a/feature/album/build.gradle.kts +++ b/feature/album/build.gradle.kts @@ -52,8 +52,8 @@ dependencies { implementation(project(":feature:common")) // Core - implementation("androidx.core:core-ktx:1.13.1") - implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.8.4") + implementation("androidx.core:core-ktx:1.15.0") + implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.8.7") // Jetpack Compose implementation(platform("androidx.compose:compose-bom:2024.10.01")) diff --git a/feature/artist/build.gradle.kts b/feature/artist/build.gradle.kts index 29c8323..718a847 100644 --- a/feature/artist/build.gradle.kts +++ b/feature/artist/build.gradle.kts @@ -52,8 +52,8 @@ dependencies { implementation(project(":feature:common")) // Core - implementation("androidx.core:core-ktx:1.13.1") - implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.8.3") + implementation("androidx.core:core-ktx:1.15.0") + implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.8.7") // Compose implementation(platform("androidx.compose:compose-bom:2024.10.01")) diff --git a/feature/folder/build.gradle.kts b/feature/folder/build.gradle.kts index 52247c4..317f2ce 100644 --- a/feature/folder/build.gradle.kts +++ b/feature/folder/build.gradle.kts @@ -52,8 +52,8 @@ dependencies { implementation(project(":feature:common")) // Core - implementation("androidx.core:core-ktx:1.13.1") - implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.8.4") + implementation("androidx.core:core-ktx:1.15.0") + implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.8.7") // Jetpack Compose implementation(platform("androidx.compose:compose-bom:2024.10.01")) diff --git a/feature/home/build.gradle.kts b/feature/home/build.gradle.kts index f90aa1e..f6e9266 100644 --- a/feature/home/build.gradle.kts +++ b/feature/home/build.gradle.kts @@ -48,11 +48,11 @@ dependencies { // Common implementation(project(":feature:common")) - implementation("androidx.core:core-ktx:1.13.1") - implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.8.3") + implementation("androidx.core:core-ktx:1.15.0") + implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.8.7") // Compose - implementation(platform("androidx.compose:compose-bom:2023.08.00")) + implementation(platform("androidx.compose:compose-bom:2024.10.01")) implementation("androidx.compose.ui:ui") implementation("androidx.compose.ui:ui-graphics") implementation("androidx.compose.ui:ui-tooling-preview") diff --git a/feature/player/build.gradle.kts b/feature/player/build.gradle.kts index 8b37ae8..5ca6db6 100644 --- a/feature/player/build.gradle.kts +++ b/feature/player/build.gradle.kts @@ -50,11 +50,11 @@ dependencies { implementation(project(":feature:common")) // Core - implementation("androidx.core:core-ktx:1.13.1") - implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.8.3") + implementation("androidx.core:core-ktx:1.15.0") + implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.8.7") // Compose - implementation(platform("androidx.compose:compose-bom:2023.08.00")) + implementation(platform("androidx.compose:compose-bom:2024.10.01")) implementation("androidx.compose.ui:ui") implementation("androidx.compose.ui:ui-graphics") implementation("androidx.compose.ui:ui-tooling-preview") @@ -79,20 +79,20 @@ dependencies { ksp("io.github.raamcosta.compose-destinations:ksp:1.9.63") // Media3 - implementation("androidx.media3:media3-exoplayer:1.4.0") - implementation("androidx.media3:media3-session:1.4.0") + implementation("androidx.media3:media3-exoplayer:1.4.1") + implementation("androidx.media3:media3-session:1.4.1") // Coil Image Loader implementation("io.coil-kt:coil-compose:2.6.0") // Pagination - implementation("androidx.paging:paging-compose:3.3.1") + implementation("androidx.paging:paging-compose:3.3.2") // Coroutines implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.0") implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.8.0") - implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.8.4") - implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.8.4") + implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.8.7") + implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.8.7") } kotlin { diff --git a/network/build.gradle.kts b/network/build.gradle.kts index 84fd958..44a96cc 100644 --- a/network/build.gradle.kts +++ b/network/build.gradle.kts @@ -38,7 +38,7 @@ dependencies { implementation(project(":database")) // Kotlin extensions - implementation("androidx.core:core-ktx:1.13.1") + implementation("androidx.core:core-ktx:1.15.0") // Retrofit implementation("com.squareup.retrofit2:retrofit:2.11.0") @@ -56,5 +56,5 @@ dependencies { implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.8.0") // Pagination - implementation ("androidx.paging:paging-compose:3.3.1") + implementation ("androidx.paging:paging-compose:3.3.2") } diff --git a/uicomponent/build.gradle.kts b/uicomponent/build.gradle.kts index 7f84b4f..2b852c4 100644 --- a/uicomponent/build.gradle.kts +++ b/uicomponent/build.gradle.kts @@ -43,9 +43,9 @@ dependencies { implementation(project(":core")) // Core - implementation("androidx.core:core-ktx:1.13.1") - implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.8.4") - implementation(platform("androidx.compose:compose-bom:2024.06.00")) + implementation("androidx.core:core-ktx:1.15.0") + implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.8.7") + implementation(platform("androidx.compose:compose-bom:2024.10.01")) implementation("androidx.compose.material3:material3") implementation("androidx.compose.ui:ui") implementation("androidx.compose.ui:ui-tooling-preview") @@ -54,12 +54,14 @@ dependencies { testImplementation("junit:junit:4.13.2") androidTestImplementation("androidx.test.ext:junit:1.2.1") androidTestImplementation("androidx.test.espresso:espresso-core:3.6.1") - androidTestImplementation(platform("androidx.compose:compose-bom:2024.06.00")) + androidTestImplementation(platform("androidx.compose:compose-bom:2024.10.01")) androidTestImplementation("androidx.compose.ui:ui-test-junit4") debugImplementation("androidx.compose.ui:ui-tooling") debugImplementation("androidx.compose.ui:ui-test-manifest") // Coil Image Loader implementation("io.coil-kt:coil-compose:2.6.0") - implementation("androidx.appcompat:appcompat:1.3.1") + + //App Compat for XML themes + implementation("androidx.appcompat:appcompat:1.7.0") } From c7f223b4b0e9a7eae1a1dec08b60a7a3c82bf274 Mon Sep 17 00:00:00 2001 From: Ericgacoki Date: Fri, 15 Nov 2024 18:25:03 +0300 Subject: [PATCH 2/2] swipe to switch playing media --- .../presentation/activity/MainActivity.kt | 34 +++++- .../presentation/navigator/BottomNavItem.kt | 10 +- .../presentation/navigator/CoreNavigator.kt | 22 +++- .../presentation/navigator/NavGraphs.kt | 9 +- .../presentation/screen/ArtistInfoScreen.kt | 19 +-- .../presentation/screen/ViewAllScreen.kt | 5 + .../presentation/navigator/CommonNavigator.kt | 2 + .../presentation/screen/FoldersAndTracks.kt | 67 +++++++---- .../viewmodel/FoldersViewModel.kt | 7 +- .../player/presentation/screen/NowPlaying.kt | 109 ++++++++++++++---- .../player/presentation/util/Extension.kt | 2 +- .../viewmodel/MediaControllerViewModel.kt | 5 + 12 files changed, 214 insertions(+), 77 deletions(-) diff --git a/app/src/main/java/com/android/swingmusic/presentation/activity/MainActivity.kt b/app/src/main/java/com/android/swingmusic/presentation/activity/MainActivity.kt index 4affb1b..d04170d 100644 --- a/app/src/main/java/com/android/swingmusic/presentation/activity/MainActivity.kt +++ b/app/src/main/java/com/android/swingmusic/presentation/activity/MainActivity.kt @@ -51,6 +51,7 @@ import androidx.navigation.compose.currentBackStackEntryAsState import androidx.navigation.compose.rememberNavController import com.android.swingmusic.album.presentation.screen.destinations.AllAlbumScreenDestination import com.android.swingmusic.artist.presentation.screen.destinations.ArtistsScreenDestination +import com.android.swingmusic.artist.presentation.screen.destinations.ViewAllScreenDestination import com.android.swingmusic.artist.presentation.viewmodel.ArtistInfoViewModel import com.android.swingmusic.auth.data.workmanager.scheduleTokenRefreshWork import com.android.swingmusic.auth.presentation.screen.destinations.LoginWithQrCodeDestination @@ -166,17 +167,40 @@ class MainActivity : ComponentActivity() { "auth/${LoginWithQrCodeDestination.route}", "auth/${LoginWithUsernameScreenDestination.route}", "player/${NowPlayingScreenDestination.route}", - "player/${QueueScreenDestination.route}" + "player/${QueueScreenDestination.route}", + + // Hide mini player in view all screen + "player/${ViewAllScreenDestination.route}", + "folder/${ViewAllScreenDestination.route}", + "album/${ViewAllScreenDestination.route}", + "artist/${ViewAllScreenDestination.route}" )) ) { MiniPlayer( mediaControllerViewModel = mediaControllerViewModel, onClickPlayerItem = { + /*if (route !in listOf( + // make mini player un-clickable in view all screen + "player/${ViewAllScreenDestination.route}", + "folder/${ViewAllScreenDestination.route}", + "album/${ViewAllScreenDestination.route}", + "artist/${ViewAllScreenDestination.route}" + ) + ) { + navController.navigate( + "player/${NowPlayingScreenDestination.route}", + fun NavOptionsBuilder.() { + launchSingleTop = true + restoreState = false + } + ) + }*/ + navController.navigate( "player/${NowPlayingScreenDestination.route}", fun NavOptionsBuilder.() { - launchSingleTop = false - restoreState = true + launchSingleTop = true + restoreState = false } ) } @@ -196,7 +220,7 @@ class MainActivity : ComponentActivity() { val currentSelectedItem by navController.currentScreenAsState( isUserLoggedIn ) - + if (showBottomNav) { NavigationBar( modifier = Modifier.fillMaxWidth(), @@ -219,7 +243,7 @@ class MainActivity : ComponentActivity() { label = { Text(text = item.title) }, onClick = { navController.navigate( - item.navGraph!!, + item.navGraph, fun NavOptionsBuilder.() { launchSingleTop = true restoreState = true diff --git a/app/src/main/java/com/android/swingmusic/presentation/navigator/BottomNavItem.kt b/app/src/main/java/com/android/swingmusic/presentation/navigator/BottomNavItem.kt index fbd9c79..c06f05d 100644 --- a/app/src/main/java/com/android/swingmusic/presentation/navigator/BottomNavItem.kt +++ b/app/src/main/java/com/android/swingmusic/presentation/navigator/BottomNavItem.kt @@ -7,13 +7,13 @@ import com.android.swingmusic.uicomponent.R as UiComponent sealed class BottomNavItem( var title: String, @DrawableRes var icon: Int, - var navGraph: NavGraphSpec? + var navGraph: NavGraphSpec ) { - data object Home : BottomNavItem( + /*data object Home : BottomNavItem( title = "Home", icon = UiComponent.drawable.ic_home, navGraph = null - ) + )*/ data object Folder : BottomNavItem( title = "Folders", @@ -27,11 +27,11 @@ sealed class BottomNavItem( navGraph = NavGraphs.album ) - data object Playlist : BottomNavItem( + /*data object Playlist : BottomNavItem( title = "Playlists", icon = UiComponent.drawable.play_list, navGraph = null - ) + )*/ data object Artist : BottomNavItem( title = "Artists", diff --git a/app/src/main/java/com/android/swingmusic/presentation/navigator/CoreNavigator.kt b/app/src/main/java/com/android/swingmusic/presentation/navigator/CoreNavigator.kt index c8fb151..9240897 100644 --- a/app/src/main/java/com/android/swingmusic/presentation/navigator/CoreNavigator.kt +++ b/app/src/main/java/com/android/swingmusic/presentation/navigator/CoreNavigator.kt @@ -9,6 +9,7 @@ import com.android.swingmusic.auth.presentation.screen.destinations.LoginWithQrC import com.android.swingmusic.auth.presentation.screen.destinations.LoginWithUsernameScreenDestination import com.android.swingmusic.auth.presentation.viewmodel.AuthViewModel import com.android.swingmusic.common.presentation.navigator.CommonNavigator +import com.android.swingmusic.folder.presentation.screen.destinations.FoldersAndTracksScreenDestination import com.android.swingmusic.player.presentation.screen.destinations.QueueScreenDestination import com.android.swingmusic.player.presentation.viewmodel.MediaControllerViewModel import com.ramcosta.composedestinations.dynamic.within @@ -140,7 +141,10 @@ class CoreNavigator( override fun gotoArtistInfo(artistHash: String) { val currentDestination = navController.currentDestination?.route - val targetDestination = ArtistInfoScreenDestination(artistHash) + val targetDestination = ArtistInfoScreenDestination( + artistHash = artistHash, + loadNewArtist = true + ) if (currentDestination != targetDestination.route) { navController.navigate( @@ -172,4 +176,20 @@ class CoreNavigator( ) } } + + override fun gotoSourceFolder(name: String, path: String) { + val currentDestination = navController.currentDestination?.route + val targetDestination = + FoldersAndTracksScreenDestination(gotoFolderName = name, gotoFolderPath = path) + + if (currentDestination != targetDestination.route) { + navController.navigate( + targetDestination within navGraph, + fun NavOptionsBuilder.() { + launchSingleTop = true + restoreState = true + } + ) + } + } } diff --git a/app/src/main/java/com/android/swingmusic/presentation/navigator/NavGraphs.kt b/app/src/main/java/com/android/swingmusic/presentation/navigator/NavGraphs.kt index 8159ba5..f8cf287 100644 --- a/app/src/main/java/com/android/swingmusic/presentation/navigator/NavGraphs.kt +++ b/app/src/main/java/com/android/swingmusic/presentation/navigator/NavGraphs.kt @@ -66,7 +66,8 @@ object NavGraphs { QueueScreenDestination, ArtistInfoScreenDestination, ViewAllScreenDestination, - AlbumWithInfoScreenDestination + AlbumWithInfoScreenDestination, + FoldersAndTracksScreenDestination ).routedIn(this).associateBy { it.route } } @@ -81,7 +82,8 @@ object NavGraphs { ArtistInfoScreenDestination, AlbumWithInfoScreenDestination, ViewAllScreenDestination, - NowPlayingScreenDestination + NowPlayingScreenDestination, + FoldersAndTracksScreenDestination ).routedIn(this).associateBy { it.route } } @@ -96,7 +98,8 @@ object NavGraphs { AlbumWithInfoScreenDestination, ArtistInfoScreenDestination, ViewAllScreenDestination, - NowPlayingScreenDestination + NowPlayingScreenDestination, + FoldersAndTracksScreenDestination ).routedIn(this).associateBy { it.route } } diff --git a/feature/artist/src/main/java/com/android/swingmusic/artist/presentation/screen/ArtistInfoScreen.kt b/feature/artist/src/main/java/com/android/swingmusic/artist/presentation/screen/ArtistInfoScreen.kt index 43189d3..1a25b59 100644 --- a/feature/artist/src/main/java/com/android/swingmusic/artist/presentation/screen/ArtistInfoScreen.kt +++ b/feature/artist/src/main/java/com/android/swingmusic/artist/presentation/screen/ArtistInfoScreen.kt @@ -81,6 +81,7 @@ import com.android.swingmusic.uicomponent.presentation.theme.SwingMusicTheme_Pre import com.android.swingmusic.uicomponent.presentation.util.Screen import com.android.swingmusic.uicomponent.presentation.util.formattedAlbumDuration import com.ramcosta.composedestinations.annotation.Destination +import timber.log.Timber @SuppressLint("UnusedMaterial3ScaffoldPaddingParameter") @Composable @@ -752,6 +753,7 @@ fun ArtistInfoScreen( mediaControllerViewModel: MediaControllerViewModel, artistInfoViewModel: ArtistInfoViewModel, artistHash: String, + loadNewArtist: Boolean, commonNavigator: CommonNavigator ) { val baseUrl = mediaControllerViewModel.baseUrl @@ -764,14 +766,12 @@ fun ArtistInfoScreen( var showOnRefreshIndicator by remember { mutableStateOf(false) } LaunchedEffect(key1 = Unit) { - if (artistInfoState.value.requiresReload) { - artistInfoViewModel.onArtistInfoUiEvent( - ArtistInfoUiEvent.OnLoadArtistInfo(artistHash) - ) - } else if (currentArtistHash != artistHash) { - artistInfoViewModel.onArtistInfoUiEvent( - ArtistInfoUiEvent.OnUpdateArtistHash(artistHash) - ) + if (artistInfoState.value.requiresReload || loadNewArtist) { + if (currentArtistHash != artistHash) { + artistInfoViewModel.onArtistInfoUiEvent( + ArtistInfoUiEvent.OnLoadArtistInfo(artistHash) + ) + } } } @@ -788,6 +788,9 @@ fun ArtistInfoScreen( ?: artistHash ) ) + + Timber.e("Artist Hash VM: ${artistInfoState.value.infoResource.data?.artist?.artistHash}") + Timber.e("Artist Hash Nav: $artistHash") }, ) { when (val res = artistInfoState.value.infoResource) { diff --git a/feature/artist/src/main/java/com/android/swingmusic/artist/presentation/screen/ViewAllScreen.kt b/feature/artist/src/main/java/com/android/swingmusic/artist/presentation/screen/ViewAllScreen.kt index a3f85df..89bbaf1 100644 --- a/feature/artist/src/main/java/com/android/swingmusic/artist/presentation/screen/ViewAllScreen.kt +++ b/feature/artist/src/main/java/com/android/swingmusic/artist/presentation/screen/ViewAllScreen.kt @@ -20,6 +20,7 @@ import androidx.compose.runtime.collectAsState import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import androidx.navigation.NavController import com.android.swingmusic.artist.presentation.viewmodel.ArtistInfoViewModel import com.android.swingmusic.common.presentation.navigator.CommonNavigator import com.android.swingmusic.core.domain.model.Album @@ -33,6 +34,7 @@ import com.android.swingmusic.uicomponent.presentation.component.TrackItem import com.android.swingmusic.uicomponent.presentation.theme.SwingMusicTheme import com.android.swingmusic.uicomponent.presentation.theme.SwingMusicTheme_Preview import com.ramcosta.composedestinations.annotation.Destination +import timber.log.Timber @Composable private fun ViewAll( @@ -131,6 +133,7 @@ private fun ViewAll( @Destination @Composable fun ViewAllScreen( + navController: NavController, commonNavigator: CommonNavigator, mediaControllerViewModel: MediaControllerViewModel, artistInfoViewModel: ArtistInfoViewModel, @@ -152,6 +155,8 @@ fun ViewAllScreen( else -> null } + Timber.e("View All Route: ${navController.currentDestination?.route}") + SwingMusicTheme { ViewAll( title = viewAllType, diff --git a/feature/common/src/main/java/com/android/swingmusic/common/presentation/navigator/CommonNavigator.kt b/feature/common/src/main/java/com/android/swingmusic/common/presentation/navigator/CommonNavigator.kt index b29ee02..65d34c4 100644 --- a/feature/common/src/main/java/com/android/swingmusic/common/presentation/navigator/CommonNavigator.kt +++ b/feature/common/src/main/java/com/android/swingmusic/common/presentation/navigator/CommonNavigator.kt @@ -20,4 +20,6 @@ interface CommonNavigator { fun gotoArtistInfo(artistHash: String) fun gotoViewAllScreen(viewAllType: String, artistName: String, baseUrl: String) + + fun gotoSourceFolder(name: String, path: String) } diff --git a/feature/folder/src/main/java/com/android/swingmusic/folder/presentation/screen/FoldersAndTracks.kt b/feature/folder/src/main/java/com/android/swingmusic/folder/presentation/screen/FoldersAndTracks.kt index 4130553..f951909 100644 --- a/feature/folder/src/main/java/com/android/swingmusic/folder/presentation/screen/FoldersAndTracks.kt +++ b/feature/folder/src/main/java/com/android/swingmusic/folder/presentation/screen/FoldersAndTracks.kt @@ -96,30 +96,30 @@ private fun FoldersAndTracks( // use a double scaffold to take advantage of padding values since the app uses full screen mode Scaffold { it -> - Scaffold( - modifier = Modifier.padding(it), - topBar = { - Text( - modifier = Modifier.padding( - top = 16.dp, - start = 16.dp, - bottom = 8.dp - ), - text = "Folders", - style = MaterialTheme.typography.headlineMedium - ) - } - ) { paddingValues -> - PullToRefreshBox( - modifier = Modifier.fillMaxSize(), - isRefreshing = showOnRefreshIndicator, - onRefresh = { - showOnRefreshIndicator = true + PullToRefreshBox( + modifier = Modifier.fillMaxSize(), + isRefreshing = showOnRefreshIndicator, + onRefresh = { + showOnRefreshIndicator = true - val event = FolderUiEvent.OnClickFolder(currentFolder) - onPullToRefresh(event) - }, - ) { + val event = FolderUiEvent.OnClickFolder(currentFolder) + onPullToRefresh(event) + }, + ) { + Scaffold( + modifier = Modifier.padding(it), + topBar = { + Text( + modifier = Modifier.padding( + top = 16.dp, + start = 16.dp, + bottom = 8.dp + ), + text = "Folders", + style = MaterialTheme.typography.headlineMedium + ) + } + ) { paddingValues -> Surface( modifier = Modifier .fillMaxSize() @@ -401,7 +401,9 @@ fun FoldersAndTracksScreen( foldersViewModel: FoldersViewModel = hiltViewModel(), albumWithInfoViewModel: AlbumWithInfoViewModel = hiltViewModel(), mediaControllerViewModel: MediaControllerViewModel, - albumNavigator: CommonNavigator + albumNavigator: CommonNavigator, + gotoFolderName: String? = null, + gotoFolderPath: String? = null ) { val currentFolder by remember { foldersViewModel.currentFolder } val foldersAndTracksState by remember { foldersViewModel.foldersAndTracks } @@ -410,6 +412,23 @@ fun FoldersAndTracksScreen( val playerUiState by mediaControllerViewModel.playerUiState.collectAsState() val baseUrl by mediaControllerViewModel.baseUrl.collectAsState() + LaunchedEffect(key1 = Unit) { + if (gotoFolderName != null && gotoFolderPath != null) { + val folder = Folder( + name = gotoFolderName, + path = gotoFolderPath, + trackCount = 0, + folderCount = 0, + isSym = false + ) + foldersViewModel.onFolderUiEvent(FolderUiEvent.OnClickFolder(folder)) + } else if (foldersAndTracksState.foldersAndTracks.folders.isEmpty() && + foldersAndTracksState.foldersAndTracks.tracks.isEmpty() + ) { + foldersViewModel.onFolderUiEvent(FolderUiEvent.OnClickFolder(homeDir)) + } + } + SwingMusicTheme(navBarColor = MaterialTheme.colorScheme.inverseOnSurface) { FoldersAndTracks( currentFolder = currentFolder, diff --git a/feature/folder/src/main/java/com/android/swingmusic/folder/presentation/viewmodel/FoldersViewModel.kt b/feature/folder/src/main/java/com/android/swingmusic/folder/presentation/viewmodel/FoldersViewModel.kt index 931aa7c..894ff27 100644 --- a/feature/folder/src/main/java/com/android/swingmusic/folder/presentation/viewmodel/FoldersViewModel.kt +++ b/feature/folder/src/main/java/com/android/swingmusic/folder/presentation/viewmodel/FoldersViewModel.kt @@ -43,7 +43,7 @@ class FoldersViewModel @Inject constructor( folders = emptyList(), tracks = emptyList() ), - isLoading = false, + isLoading = true, isError = false ) ) @@ -108,10 +108,6 @@ class FoldersViewModel @Inject constructor( } } - init { - getFoldersAndTracks(homeDir.path) - } - fun onFolderUiEvent(event: FolderUiEvent) { when (event) { is FolderUiEvent.OnClickNavPath -> { @@ -131,6 +127,7 @@ class FoldersViewModel @Inject constructor( if (!_navPaths.value.contains(event.folder)) { _navPaths.value = listOf(homeDir) + // TODO: rebuild the entire path (use network result path) .plus( (_navPaths.value.filter { event.folder.path.contains(it.path) diff --git a/feature/player/src/main/java/com/android/swingmusic/player/presentation/screen/NowPlaying.kt b/feature/player/src/main/java/com/android/swingmusic/player/presentation/screen/NowPlaying.kt index 8d265fa..00d3463 100644 --- a/feature/player/src/main/java/com/android/swingmusic/player/presentation/screen/NowPlaying.kt +++ b/feature/player/src/main/java/com/android/swingmusic/player/presentation/screen/NowPlaying.kt @@ -23,6 +23,8 @@ import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.wrapContentSize import androidx.compose.foundation.lazy.LazyRow +import androidx.compose.foundation.pager.HorizontalPager +import androidx.compose.foundation.pager.rememberPagerState import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.CircularProgressIndicator @@ -32,10 +34,14 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.runtime.snapshotFlow import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip @@ -61,6 +67,7 @@ import com.android.swingmusic.core.domain.util.PlaybackState import com.android.swingmusic.core.domain.util.RepeatMode import com.android.swingmusic.core.domain.util.ShuffleMode import com.android.swingmusic.player.presentation.event.PlayerUiEvent +import com.android.swingmusic.player.presentation.event.QueueEvent import com.android.swingmusic.player.presentation.viewmodel.MediaControllerViewModel import com.android.swingmusic.uicomponent.R import com.android.swingmusic.uicomponent.presentation.component.slider.WaveAnimationSpecs @@ -75,6 +82,8 @@ import java.util.Locale @Composable private fun NowPlaying( track: Track?, + playingTrackIndex: Int, + queue: List, seekPosition: Float = 0F, playbackDuration: String, trackDuration: String, @@ -83,6 +92,7 @@ private fun NowPlaying( repeatMode: RepeatMode, shuffleMode: ShuffleMode, baseUrl: String, + onPageSelect: (page: Int) -> Unit, onClickArtist: (artistHash: String) -> Unit, onToggleRepeatMode: (RepeatMode) -> Unit, onClickPrev: () -> Unit, @@ -107,9 +117,6 @@ private fun NowPlaying( return } - val artistsSeparator by remember { - derivedStateOf { if (track.trackArtists.size == 2) " & " else ", " } - } val fileType by remember { derivedStateOf { track.filepath.substringAfterLast(".").uppercase(Locale.ROOT) @@ -141,6 +148,34 @@ private fun NowPlaying( PlaybackState.ERROR -> R.drawable.error } + val pagerState = rememberPagerState( + initialPage = playingTrackIndex, + pageCount = { if (queue.isEmpty()) 1 else queue.size } + ) + + var isInitialComposition by remember { mutableStateOf(true) } + + LaunchedEffect( + key1 = playingTrackIndex, + key2 = pagerState + ) { + if (playingTrackIndex in queue.indices) { + if (playingTrackIndex != pagerState.currentPage) { + pagerState.animateScrollToPage(playingTrackIndex) + } + } + + snapshotFlow { pagerState.currentPage }.collect { page -> + if (isInitialComposition) { + isInitialComposition = false // Skip the first run + } else { + if (playingTrackIndex != page) { + onPageSelect(page) + } + } + } + } + Scaffold( modifier = Modifier.background(MaterialTheme.colorScheme.surface), bottomBar = { @@ -152,7 +187,6 @@ private fun NowPlaying( ) } ) { paddingValues -> - AsyncImage( modifier = Modifier .fillMaxWidth() @@ -195,30 +229,44 @@ private fun NowPlaying( horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.SpaceBetween ) { - // Artwork, SeekBar... Column( modifier = Modifier - .wrapContentSize() + .fillMaxWidth() .padding(horizontal = 24.dp), horizontalAlignment = Alignment.CenterHorizontally, ) { - Spacer(modifier = Modifier.height(12.dp)) - - // Artwork - AsyncImage( - modifier = Modifier - .size(356.dp) - .clip(RoundedCornerShape(7)), - model = ImageRequest.Builder(LocalContext.current) - .data("${baseUrl}img/thumbnail/${track.image}") - .crossfade(true) - .build(), - placeholder = painterResource(R.drawable.audio_fallback), - fallback = painterResource(R.drawable.audio_fallback), - error = painterResource(R.drawable.audio_fallback), - contentDescription = "Track Image", - contentScale = ContentScale.Crop - ) + // Artwork, SeekBar... + HorizontalPager( + modifier = Modifier.width(356.dp), + pageSpacing = 24.dp, + state = pagerState, + beyondViewportPageCount = 2, + verticalAlignment = Alignment.CenterVertically + ) { page -> + val imageData = if (page == playingTrackIndex) { + "${baseUrl}img/thumbnail/${queue.getOrNull(playingTrackIndex)?.image ?: track.image}" + } else { + "${baseUrl}img/thumbnail/${queue.getOrNull(page)?.image ?: track.image}" + } + Column { + Spacer(modifier = Modifier.height(12.dp)) + // Artwork + AsyncImage( + modifier = Modifier + .size(356.dp) + .clip(RoundedCornerShape(7)), + model = ImageRequest.Builder(LocalContext.current) + .data(imageData) + .crossfade(true) + .build(), + placeholder = painterResource(R.drawable.audio_fallback), + fallback = painterResource(R.drawable.audio_fallback), + error = painterResource(R.drawable.audio_fallback), + contentDescription = "Track Image", + contentScale = ContentScale.Crop + ) + } + } Spacer(modifier = Modifier.height(28.dp)) @@ -256,7 +304,7 @@ private fun NowPlaying( ) if (index != track.trackArtists.lastIndex) { Text( - text = artistsSeparator, + text = ", ", style = MaterialTheme.typography.bodySmall, color = MaterialTheme.colorScheme.onSurface.copy( alpha = .84F @@ -431,8 +479,8 @@ private fun NowPlaying( ) } } - } + } // Bitrate, Track format Box( modifier = Modifier @@ -549,6 +597,8 @@ fun NowPlayingScreen( NowPlaying( track = playerUiState.nowPlayingTrack, + playingTrackIndex = playerUiState.playingTrackIndex, + queue = playerUiState.queue, seekPosition = playerUiState.seekPosition, playbackDuration = playerUiState.playbackDuration, trackDuration = playerUiState.trackDuration, @@ -557,6 +607,12 @@ fun NowPlayingScreen( shuffleMode = playerUiState.shuffleMode, isBuffering = playerUiState.isBuffering, baseUrl = baseUrl ?: "", + onPageSelect = { page -> + // treat this as clicking a track in queue + mediaControllerViewModel.onQueueEvent( + QueueEvent.SeekToQueueItem(page) + ) + }, onClickArtist = { navigator.gotoArtistInfo(it) }, @@ -666,6 +722,8 @@ fun FullPlayerPreview() { SwingMusicTheme_Preview { NowPlaying( track = track, + playingTrackIndex = 0, + queue = emptyList(), seekPosition = .22F, playbackDuration = "01:23", trackDuration = "02:59", @@ -674,6 +732,7 @@ fun FullPlayerPreview() { repeatMode = RepeatMode.REPEAT_OFF, shuffleMode = ShuffleMode.SHUFFLE_OFF, baseUrl = "", + onPageSelect = {}, onClickArtist = {}, onToggleRepeatMode = {}, onResumePlayBackFromError = {}, diff --git a/feature/player/src/main/java/com/android/swingmusic/player/presentation/util/Extension.kt b/feature/player/src/main/java/com/android/swingmusic/player/presentation/util/Extension.kt index 12717e2..cf73ffc 100644 --- a/feature/player/src/main/java/com/android/swingmusic/player/presentation/util/Extension.kt +++ b/feature/player/src/main/java/com/android/swingmusic/player/presentation/util/Extension.kt @@ -7,7 +7,7 @@ fun QueueSource.navigateToSource(navigator: CommonNavigator) { return when (this) { is QueueSource.ALBUM -> navigator.gotoAlbumWithInfo(this.albumHash) is QueueSource.ARTIST -> navigator.gotoArtistInfo(this.artistHash) - // is QueueSource.FOLDER -> navigator.gotoSourceFolder(this.path) + is QueueSource.FOLDER -> navigator.gotoSourceFolder(this.name, this.path) // is QueueSource.PLAYLIST -> ... // is QueueSource.QUERY -> ... else -> {} diff --git a/feature/player/src/main/java/com/android/swingmusic/player/presentation/viewmodel/MediaControllerViewModel.kt b/feature/player/src/main/java/com/android/swingmusic/player/presentation/viewmodel/MediaControllerViewModel.kt index 718858d..de44005 100644 --- a/feature/player/src/main/java/com/android/swingmusic/player/presentation/viewmodel/MediaControllerViewModel.kt +++ b/feature/player/src/main/java/com/android/swingmusic/player/presentation/viewmodel/MediaControllerViewModel.kt @@ -512,6 +512,8 @@ class MediaControllerViewModel @Inject constructor( } }.toMutableList() } + + else -> {} } } } @@ -890,6 +892,9 @@ class MediaControllerViewModel @Inject constructor( mediaItem?.let { try { val trackIndex = it.mediaId.toInt() // "id" == index + + Timber.e("Track Index On Transition: $trackIndex") + val playingTrack: Track = if (_playerUiState.value.shuffleMode == ShuffleMode.SHUFFLE_ON) { shuffledQueue[trackIndex]