diff --git a/.idea/gradle.xml b/.idea/gradle.xml index a2d7c21..239e8d2 100644 --- a/.idea/gradle.xml +++ b/.idea/gradle.xml @@ -7,6 +7,7 @@ + diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml index e1eea1d..fdf8d99 100644 --- a/.idea/kotlinc.xml +++ b/.idea/kotlinc.xml @@ -1,6 +1,6 @@ - + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index 9b0b092..9e47cdd 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,6 +1,7 @@ + - + diff --git a/app/build.gradle b/app/build.gradle index b7ea026..d6670b5 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -3,7 +3,8 @@ plugins { id 'org.jetbrains.kotlin.android' id 'com.google.dagger.hilt.android' id 'kotlin-kapt' - id "com.google.devtools.ksp" version "1.7.20-1.0.6" +// id "com.google.devtools.ksp" version "1.7.20-1.0.6" + id "com.google.devtools.ksp" version "1.9.0-1.0.13" } kotlin { @@ -20,12 +21,14 @@ kotlin { android { namespace 'com.frontmatic.scrobbleview' - compileSdk 33 +// compileSdk 33 + compileSdk 34 defaultConfig { applicationId "com.frontmatic.scrobbleview" minSdk 24 - targetSdk 33 +// targetSdk 33 + targetSdk 34 versionCode 1 versionName "1.0" @@ -64,7 +67,8 @@ android { buildConfig true } composeOptions { - kotlinCompilerExtensionVersion '1.3.2' + kotlinCompilerExtensionVersion '1.5.1' +// kotlinCompilerExtensionVersion '1.3.2' } packagingOptions { resources { @@ -77,6 +81,9 @@ dependencies { def paging_version = "3.1.1" +// def destinations_version = "1.7.21-beta" + def destinations_version = "1.9.51" + implementation 'androidx.core:core-ktx:1.8.0' implementation platform('org.jetbrains.kotlin:kotlin-bom:1.8.0') implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.1' @@ -102,15 +109,16 @@ dependencies { implementation 'com.squareup.okhttp3:logging-interceptor:4.9.1' // Paging - implementation "androidx.paging:paging-runtime:$paging_version" - implementation "androidx.paging:paging-compose:3.2.0-rc01" + implementation "androidx.paging:paging-runtime-ktx:$paging_version" + implementation "androidx.paging:paging-compose:3.2.0" // Coil implementation("io.coil-kt:coil-compose:2.2.2") // Dagger - Hilt - implementation "com.google.dagger:hilt-android:2.44.2" - kapt "com.google.dagger:hilt-android-compiler:2.44" + implementation "com.google.dagger:hilt-android:2.47" + kapt 'com.google.dagger:hilt-compiler:2.47' + kapt "com.google.dagger:hilt-android-compiler:2.47" kapt 'androidx.hilt:hilt-compiler:1.0.0' implementation 'androidx.hilt:hilt-navigation-compose:1.0.0' @@ -118,15 +126,23 @@ dependencies { implementation("androidx.datastore:datastore-preferences:1.0.0") // Room - implementation "androidx.room:room-runtime:2.4.2" - kapt "androidx.room:room-compiler:2.4.2" - implementation "androidx.room:room-ktx:2.4.2" + implementation "androidx.room:room-runtime:2.5.2" +// kapt "androidx.room:room-compiler:2.5.2" + ksp "androidx.room:room-compiler:2.5.2" + implementation "androidx.room:room-ktx:2.5.2" // add room paging - implementation "androidx.room:room-paging:2.4.2" + implementation "androidx.room:room-paging:2.5.2" // Compose Destinations - implementation 'io.github.raamcosta.compose-destinations:core:1.7.21-beta' - ksp 'io.github.raamcosta.compose-destinations:ksp:1.7.21-beta' + implementation "io.github.raamcosta.compose-destinations:core:$destinations_version" + implementation "io.github.raamcosta.compose-destinations:animations-core:$destinations_version" + ksp "io.github.raamcosta.compose-destinations:ksp:$destinations_version" + + // Dropbox studio + +// implementation "com.dropbox.mobile.store:store4:4.0.0-beta01" + implementation "org.mobilenativefoundation.store:store5:5.0.0-beta03" + implementation "org.jetbrains.kotlinx:atomicfu:0.18.5" // def accompanist_version = "0.21.0" // // Swipe Refresh - Accompanist diff --git a/app/src/main/java/com/frontmatic/scrobbleview/MainActivity.kt b/app/src/main/java/com/frontmatic/scrobbleview/MainActivity.kt index f3d4399..fd07ea1 100644 --- a/app/src/main/java/com/frontmatic/scrobbleview/MainActivity.kt +++ b/app/src/main/java/com/frontmatic/scrobbleview/MainActivity.kt @@ -3,15 +3,14 @@ package com.frontmatic.scrobbleview import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent +import androidx.compose.animation.ExperimentalAnimationApi import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface -import androidx.compose.material3.Text -import androidx.compose.material3.TopAppBar import androidx.compose.ui.Modifier -import androidx.compose.ui.text.font.FontWeight -import androidx.navigation.compose.rememberNavController +import androidx.compose.ui.unit.dp +import androidx.navigation.plusAssign import com.frontmatic.scrobbleview.ui.components.BottomNavBar import com.frontmatic.scrobbleview.ui.components.RootScaffold import com.frontmatic.scrobbleview.ui.components.TopBar @@ -19,21 +18,36 @@ import com.frontmatic.scrobbleview.ui.screens.NavGraphs import com.frontmatic.scrobbleview.ui.screens.destinations.Destination import com.frontmatic.scrobbleview.ui.screens.destinations.SetupScreenDestination import com.frontmatic.scrobbleview.ui.screens.destinations.SplashScreenDestination +import com.frontmatic.scrobbleview.ui.screens.destinations.TrackDetailScreenDestination import com.frontmatic.scrobbleview.ui.theme.ScrobbleViewTheme +import com.google.accompanist.navigation.material.ExperimentalMaterialNavigationApi +import com.google.accompanist.navigation.material.ModalBottomSheetLayout +import com.google.accompanist.navigation.material.rememberBottomSheetNavigator import com.ramcosta.composedestinations.DestinationsNavHost +import com.ramcosta.composedestinations.animations.rememberAnimatedNavHostEngine import dagger.hilt.android.AndroidEntryPoint @AndroidEntryPoint class MainActivity : ComponentActivity() { - @OptIn(ExperimentalMaterial3Api::class) + @OptIn( + ExperimentalAnimationApi::class, + ExperimentalMaterialNavigationApi::class, + ) override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { - val navController = rememberNavController() + val engine = rememberAnimatedNavHostEngine() + val navController = engine.rememberNavController() + val bottomSheetNavigator = rememberBottomSheetNavigator() + navController.navigatorProvider += bottomSheetNavigator val startRoute = NavGraphs.root.startRoute ScrobbleViewTheme { // A surface container using the 'background' color from the theme + ModalBottomSheetLayout( + bottomSheetNavigator = bottomSheetNavigator, + sheetShape = RoundedCornerShape(16.dp), + ) { RootScaffold( navController = navController, startRoute = startRoute, @@ -52,11 +66,14 @@ class MainActivity : ComponentActivity() { Surface( modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background - ) { - DestinationsNavHost( - navController = navController, - navGraph = NavGraphs.root - ) + ) + { + DestinationsNavHost( + navController = navController, + engine = engine, + navGraph = NavGraphs.root + ) + } } } } @@ -68,5 +85,6 @@ private val Destination.shouldShowScaffoldElements get() = when (this) { is SplashScreenDestination -> false is SetupScreenDestination -> false + is TrackDetailScreenDestination -> false else -> true } \ No newline at end of file diff --git a/app/src/main/java/com/frontmatic/scrobbleview/data/DatabaseConverter.kt b/app/src/main/java/com/frontmatic/scrobbleview/data/DatabaseConverter.kt index fbd2c69..13ee884 100644 --- a/app/src/main/java/com/frontmatic/scrobbleview/data/DatabaseConverter.kt +++ b/app/src/main/java/com/frontmatic/scrobbleview/data/DatabaseConverter.kt @@ -1,81 +1,163 @@ package com.frontmatic.scrobbleview.data import androidx.room.TypeConverter +import com.frontmatic.scrobbleview.data.model.Artist import com.frontmatic.scrobbleview.data.model.Image import com.frontmatic.scrobbleview.data.model.Registered -import com.frontmatic.scrobbleview.data.model.TopTrackArtist +import com.frontmatic.scrobbleview.data.model.Streamable import com.frontmatic.scrobbleview.data.model.TrackAlbum import com.frontmatic.scrobbleview.data.model.TrackArtist import com.frontmatic.scrobbleview.data.model.TrackDate +import com.frontmatic.scrobbleview.data.model.TrackInfoAlbum +import com.frontmatic.scrobbleview.data.model.TrackTag +import com.frontmatic.scrobbleview.data.model.Wiki class DatabaseConverter { - private val separator = "," +// private val separator = "," - private val imageSeparator = "||" - private val imageInnerSeparator = "---" - @TypeConverter - fun convertListToStrings(list: List): String { - return list.joinToString(separator) - } + private val separator = "||" + private val innerSeparator = "---" - @TypeConverter - fun convertStringToList(string: String): List { - return string.split(separator) - } +// @TypeConverter +// fun convertListToStrings(list: List): String { +// return list.joinToString(separator) +// } +// +// @TypeConverter +// fun convertStringToList(string: String): List { +// return string.split(separator) +// } @TypeConverter fun convertListToImages(list: List): String { - return list.joinToString(imageSeparator) { - "${it.size}${imageInnerSeparator}${it.url}" + return list.joinToString(separator) { + "${it.size}${innerSeparator}${it.url}" } } @TypeConverter fun convertStringToImages(string: String): List { - return string.split(imageSeparator).map { - val (size, url) = it.split(imageInnerSeparator) - Image(size, url) + return string.split(separator).map { + val (size, url) = it.split(innerSeparator) + Image( + size=size, + url=url + ) } } @TypeConverter - fun convertArtisToString(artist: TrackArtist): String { - return "${artist.name}${imageInnerSeparator}${artist.mbid}" + fun convertTrackArtistToString(artist: TrackArtist): String { + return "${artist.name}${innerSeparator}${artist.mbid}" } @TypeConverter - fun convertStringToArtist(string: String): TrackArtist { - val (name, mbid) = string.split(imageInnerSeparator) - return TrackArtist(name, mbid) + fun convertStringToTrackArtist(string: String): TrackArtist { + val (name, mbid) = string.split(innerSeparator) + return TrackArtist( + name=name, + mbid=mbid + ) } @TypeConverter - fun convertTopTrackArtistToString(artist: TopTrackArtist): String { - return "${artist.name}${imageInnerSeparator}${artist.mbid}${imageInnerSeparator}${artist.url}" + fun convertTopTrackArtistToString(artist: Artist): String { + return "${artist.name}${innerSeparator}${artist.mbid}${innerSeparator}${artist.url}" } @TypeConverter - fun convertStringToTopTrackArtist(string: String): TopTrackArtist { - val (name, mbid, url) = string.split(imageInnerSeparator) - return TopTrackArtist(name, mbid, url) + fun convertStringToTopTrackArtist(string: String): Artist { + val (name, mbid, url) = string.split(innerSeparator) + return Artist( + name=name, + mbid=mbid, + url=url + ) } @TypeConverter fun convertAlbumToString(album: TrackAlbum): String { - return "${album.name}${imageInnerSeparator}${album.mbid}" + return "${album.name}${innerSeparator}${album.mbid}" } @TypeConverter fun convertStringToAlbum(string: String): TrackAlbum { - val (name, mbid) = string.split(imageInnerSeparator) - return TrackAlbum(name, mbid) + val (name, mbid) = string.split(innerSeparator) + return TrackAlbum( + name=name, + mbid=mbid + ) + } + + @TypeConverter + fun convertTrackInfoAlbumToString(album: TrackInfoAlbum): String { + return "${album.artist}${innerSeparator}${convertListToImages(album.image)}${innerSeparator}${album.mbid}${innerSeparator}${album.title}${innerSeparator}${album.url}" + } + + @TypeConverter + fun convertStringToTrackInfoAlbum(string: String): TrackInfoAlbum { + val (artist, images, mbid, title, url) = string.split(innerSeparator) + return TrackInfoAlbum( + artist=artist, + image=convertStringToImages(images), + mbid=mbid, + title=title, + url=url + ) + } + + @TypeConverter + fun convertWikiToString(wiki: Wiki): String { + return "${wiki.content}${innerSeparator}${wiki.published}${innerSeparator}${wiki.summary}" + } + + + @TypeConverter + fun convertStringToWiki(string: String): Wiki { + val (content, published, summary) = string.split(innerSeparator) + return Wiki( + content=content, + published=published, + summary=summary + ) + } + + @TypeConverter + fun convertTrackTagsToString(tags: List): String { + return tags.joinToString(separator) { + "${it.name}${innerSeparator}${it.url}" + } + } + + @TypeConverter + fun convertStringToTrackTags(string: String): List { + return string.split(separator).map { + val (name, url) = it.split(innerSeparator) + TrackTag( + name=name, + url=url + ) + } + } + + @TypeConverter + fun convertStreamableToString(streamable: Streamable): String { + return "${streamable.fulltrack}${innerSeparator}${streamable.text}" + } + @TypeConverter + fun convertStringToStreamable(string: String): Streamable { + val (fulltrack, text) = string.split(innerSeparator) + return Streamable( + fulltrack=fulltrack, + text=text + ) } @TypeConverter fun convertDateToString(date: TrackDate?): String { - return if (date != null) "${date.uts}${imageInnerSeparator}${date.text}" else "" + return if (date != null) "${date.uts}${innerSeparator}${date.text}" else "" } @TypeConverter @@ -83,19 +165,25 @@ class DatabaseConverter { if (string.isEmpty()) { return null } - val (uts, text) = string.split(imageInnerSeparator) - return TrackDate(uts.toLong(), text) + val (uts, text) = string.split(innerSeparator) + return TrackDate( + uts=uts.toLong(), + text=text + ) } @TypeConverter fun convertRegisteredToString(registered: Registered): String { - return "${registered.unixtime}${imageInnerSeparator}${registered.text}" + return "${registered.unixtime}${innerSeparator}${registered.text}" } @TypeConverter fun convertStringToRegistered(string: String): Registered { - val (unixtime, text) = string.split(imageInnerSeparator) - return Registered(unixtime.toLong(), text) + val (unixtime, text) = string.split(innerSeparator) + return Registered( + unixtime=unixtime.toLong(), + text=text + ) } } \ No newline at end of file diff --git a/app/src/main/java/com/frontmatic/scrobbleview/data/ScrobbleDatabase.kt b/app/src/main/java/com/frontmatic/scrobbleview/data/ScrobbleDatabase.kt index 66e9d8b..ef7a2d4 100644 --- a/app/src/main/java/com/frontmatic/scrobbleview/data/ScrobbleDatabase.kt +++ b/app/src/main/java/com/frontmatic/scrobbleview/data/ScrobbleDatabase.kt @@ -11,11 +11,13 @@ import com.frontmatic.scrobbleview.data.dao.RecentTrackDao import com.frontmatic.scrobbleview.data.dao.RecentTracksRemoteKeysDao import com.frontmatic.scrobbleview.data.dao.TopTrackDao import com.frontmatic.scrobbleview.data.dao.TopTracksRemoteKeysDao +import com.frontmatic.scrobbleview.data.dao.TrackInfoDao import com.frontmatic.scrobbleview.data.dao.UserDao import com.frontmatic.scrobbleview.data.model.RecentTrack import com.frontmatic.scrobbleview.data.model.RecentTracksRemoteKeys import com.frontmatic.scrobbleview.data.model.TopTrack import com.frontmatic.scrobbleview.data.model.TopTracksRemoteKeys +import com.frontmatic.scrobbleview.data.model.TrackInfo import com.frontmatic.scrobbleview.data.model.User @Database( @@ -26,7 +28,8 @@ import com.frontmatic.scrobbleview.data.model.User RecentTrack::class, RecentTracksRemoteKeys::class, TopTrack::class, - TopTracksRemoteKeys::class + TopTracksRemoteKeys::class, + TrackInfo::class, ], version = 1 ) @@ -37,7 +40,7 @@ abstract class ScrobbleDatabase: RoomDatabase() { abstract fun userDao(): UserDao abstract fun recentTrackDao(): RecentTrackDao abstract fun recentTracksRemoteKeysDao(): RecentTracksRemoteKeysDao - abstract fun topTrackDao(): TopTrackDao abstract fun topTracksRemoteKeysDao(): TopTracksRemoteKeysDao + abstract fun trackInfoDao(): TrackInfoDao } \ No newline at end of file diff --git a/app/src/main/java/com/frontmatic/scrobbleview/data/api/LastFMApi.kt b/app/src/main/java/com/frontmatic/scrobbleview/data/api/LastFMApi.kt index 1ee741e..e1f3ea0 100644 --- a/app/src/main/java/com/frontmatic/scrobbleview/data/api/LastFMApi.kt +++ b/app/src/main/java/com/frontmatic/scrobbleview/data/api/LastFMApi.kt @@ -1,9 +1,11 @@ package com.frontmatic.scrobbleview.data.api +import com.frontmatic.scrobbleview.data.model.TrackInfo import com.frontmatic.scrobbleview.data.model.response.FriendApiResponse import com.frontmatic.scrobbleview.data.model.UserApiResponse import com.frontmatic.scrobbleview.data.model.response.RecentTracksApiResponse import com.frontmatic.scrobbleview.data.model.response.TopTracksApiResponse +import com.frontmatic.scrobbleview.data.model.response.TrackInfoResponse import retrofit2.Response import retrofit2.http.GET import retrofit2.http.Query @@ -59,4 +61,14 @@ interface LastFMApi { @Query("page") page: Int?, @Query("period") period : RequestPeriod? ): TopTracksApiResponse + + + @GET(".") + suspend fun getTrackInfo( + @Query("method") method: String = "track.getInfo", + @Query("mbid") mbid: String?, + @Query("username") user: String?, + @Query("artist") artist: String?, + @Query("track") track: String?, + ): TrackInfoResponse } \ No newline at end of file diff --git a/app/src/main/java/com/frontmatic/scrobbleview/data/dao/TrackInfoDao.kt b/app/src/main/java/com/frontmatic/scrobbleview/data/dao/TrackInfoDao.kt new file mode 100644 index 0000000..7346b3f --- /dev/null +++ b/app/src/main/java/com/frontmatic/scrobbleview/data/dao/TrackInfoDao.kt @@ -0,0 +1,23 @@ +package com.frontmatic.scrobbleview.data.dao + +import androidx.room.Dao +import androidx.room.Insert +import androidx.room.OnConflictStrategy +import androidx.room.Query +import com.frontmatic.scrobbleview.data.model.TrackInfo +import kotlinx.coroutines.flow.Flow + +@Dao +interface TrackInfoDao { + @Query("SELECT * FROM track_info WHERE name = :name") + fun getOne(name: String): Flow + + @Insert(onConflict = OnConflictStrategy.REPLACE) + fun addOne(trackInfo: TrackInfo) + + @Query("DELETE FROM track_info WHERE name = :name") + fun deleteOne(name: String) + + @Query("DELETE FROM track_info") + fun deleteAll() +} \ No newline at end of file diff --git a/app/src/main/java/com/frontmatic/scrobbleview/data/model/Artist.kt b/app/src/main/java/com/frontmatic/scrobbleview/data/model/Artist.kt new file mode 100644 index 0000000..e2234c5 --- /dev/null +++ b/app/src/main/java/com/frontmatic/scrobbleview/data/model/Artist.kt @@ -0,0 +1,7 @@ +package com.frontmatic.scrobbleview.data.model + +data class Artist( + val mbid: String, + val name: String, + val url: String +) \ No newline at end of file diff --git a/app/src/main/java/com/frontmatic/scrobbleview/data/model/Streamable.kt b/app/src/main/java/com/frontmatic/scrobbleview/data/model/Streamable.kt new file mode 100644 index 0000000..92fde39 --- /dev/null +++ b/app/src/main/java/com/frontmatic/scrobbleview/data/model/Streamable.kt @@ -0,0 +1,11 @@ +package com.frontmatic.scrobbleview.data.model + +import com.google.gson.annotations.SerializedName +import kotlinx.serialization.Serializable + +@Serializable +data class Streamable( + @SerializedName("#text") + val text: String, + val fulltrack: String +) \ No newline at end of file diff --git a/app/src/main/java/com/frontmatic/scrobbleview/data/model/TopTrack.kt b/app/src/main/java/com/frontmatic/scrobbleview/data/model/TopTrack.kt index 9c6a243..7f71bce 100644 --- a/app/src/main/java/com/frontmatic/scrobbleview/data/model/TopTrack.kt +++ b/app/src/main/java/com/frontmatic/scrobbleview/data/model/TopTrack.kt @@ -21,7 +21,7 @@ data class TopTrack( val period: RequestPeriod, - val artist: TopTrackArtist + val artist: Artist ) { val durationString: String @RequiresApi(Build.VERSION_CODES.O) @@ -35,8 +35,8 @@ data class TopTrack( } -data class TopTrackArtist( - val name: String, - val mbid: String, - val url: String -) +//data class TopTrackArtist( +// val name: String, +// val mbid: String, +// val url: String +//) diff --git a/app/src/main/java/com/frontmatic/scrobbleview/data/model/Track.kt b/app/src/main/java/com/frontmatic/scrobbleview/data/model/Track.kt index 1be10f1..18eae21 100644 --- a/app/src/main/java/com/frontmatic/scrobbleview/data/model/Track.kt +++ b/app/src/main/java/com/frontmatic/scrobbleview/data/model/Track.kt @@ -1,17 +1,7 @@ package com.frontmatic.scrobbleview.data.model -import android.util.Base64 -import com.google.gson.JsonObject -import com.google.gson.JsonPrimitive import com.google.gson.annotations.SerializedName -import kotlinx.serialization.KSerializer import kotlinx.serialization.Serializable -import kotlinx.serialization.SerializationException -import kotlinx.serialization.Serializer -import kotlinx.serialization.descriptors.SerialDescriptor -import kotlinx.serialization.encoding.Encoder -import java.text.SimpleDateFormat -import java.util.Date @Serializable data class Track( @@ -22,7 +12,6 @@ data class Track( val streamable: Int, val image: List, val mbid: String, - ) @Serializable diff --git a/app/src/main/java/com/frontmatic/scrobbleview/data/model/TrackInfo.kt b/app/src/main/java/com/frontmatic/scrobbleview/data/model/TrackInfo.kt new file mode 100644 index 0000000..ab4e36a --- /dev/null +++ b/app/src/main/java/com/frontmatic/scrobbleview/data/model/TrackInfo.kt @@ -0,0 +1,23 @@ +package com.frontmatic.scrobbleview.data.model + +import androidx.room.Entity +import androidx.room.PrimaryKey +import kotlinx.serialization.Serializable + +@Serializable +@Entity(tableName = "track_info") +data class TrackInfo( + @PrimaryKey(autoGenerate = true) + val id: Int, + val album: TrackInfoAlbum, + val artist: Artist, + val duration: String, + val listeners: String, + val mbid: String, + val name: String, + val playcount: String, + val streamable: Streamable, + val toptags: List, + val url: String, + val wiki: Wiki +) \ No newline at end of file diff --git a/app/src/main/java/com/frontmatic/scrobbleview/data/model/TrackInfoAlbum.kt b/app/src/main/java/com/frontmatic/scrobbleview/data/model/TrackInfoAlbum.kt new file mode 100644 index 0000000..9d8f8aa --- /dev/null +++ b/app/src/main/java/com/frontmatic/scrobbleview/data/model/TrackInfoAlbum.kt @@ -0,0 +1,9 @@ +package com.frontmatic.scrobbleview.data.model + +data class TrackInfoAlbum( + val artist: String, + val image: List, + val mbid: String, + val title: String, + val url: String +) \ No newline at end of file diff --git a/app/src/main/java/com/frontmatic/scrobbleview/data/model/TrackTag.kt b/app/src/main/java/com/frontmatic/scrobbleview/data/model/TrackTag.kt new file mode 100644 index 0000000..dca4e6d --- /dev/null +++ b/app/src/main/java/com/frontmatic/scrobbleview/data/model/TrackTag.kt @@ -0,0 +1,6 @@ +package com.frontmatic.scrobbleview.data.model + +data class TrackTag( + val name: String, + val url: String +) \ No newline at end of file diff --git a/app/src/main/java/com/frontmatic/scrobbleview/data/model/Wiki.kt b/app/src/main/java/com/frontmatic/scrobbleview/data/model/Wiki.kt new file mode 100644 index 0000000..da1c6b3 --- /dev/null +++ b/app/src/main/java/com/frontmatic/scrobbleview/data/model/Wiki.kt @@ -0,0 +1,7 @@ +package com.frontmatic.scrobbleview.data.model + +data class Wiki( + val content: String, + val published: String, + val summary: String +) \ No newline at end of file diff --git a/app/src/main/java/com/frontmatic/scrobbleview/data/model/response/TrackInfoResponse.kt b/app/src/main/java/com/frontmatic/scrobbleview/data/model/response/TrackInfoResponse.kt new file mode 100644 index 0000000..ef2c96b --- /dev/null +++ b/app/src/main/java/com/frontmatic/scrobbleview/data/model/response/TrackInfoResponse.kt @@ -0,0 +1,7 @@ +package com.frontmatic.scrobbleview.data.model.response + +import com.frontmatic.scrobbleview.data.model.TrackInfo + +data class TrackInfoResponse( + val track: TrackInfo +) diff --git a/app/src/main/java/com/frontmatic/scrobbleview/data/paging/FriendsRemoteMediator.kt b/app/src/main/java/com/frontmatic/scrobbleview/data/paging/FriendsRemoteMediator.kt index a46a2b4..48cec54 100644 --- a/app/src/main/java/com/frontmatic/scrobbleview/data/paging/FriendsRemoteMediator.kt +++ b/app/src/main/java/com/frontmatic/scrobbleview/data/paging/FriendsRemoteMediator.kt @@ -9,7 +9,7 @@ import com.frontmatic.scrobbleview.data.ScrobbleDatabase import com.frontmatic.scrobbleview.data.api.LastFMApi import com.frontmatic.scrobbleview.data.model.Friend import com.frontmatic.scrobbleview.data.model.FriendRemoteKeys -import com.frontmatic.scrobbleview.data.repository.DataStoreOperations +import com.frontmatic.scrobbleview.data.source.DataStoreOperations import kotlinx.coroutines.flow.first import javax.inject.Inject diff --git a/app/src/main/java/com/frontmatic/scrobbleview/data/paging/RecentTracksRemoteMediator.kt b/app/src/main/java/com/frontmatic/scrobbleview/data/paging/RecentTracksRemoteMediator.kt index febc18c..50caccf 100644 --- a/app/src/main/java/com/frontmatic/scrobbleview/data/paging/RecentTracksRemoteMediator.kt +++ b/app/src/main/java/com/frontmatic/scrobbleview/data/paging/RecentTracksRemoteMediator.kt @@ -9,7 +9,7 @@ import com.frontmatic.scrobbleview.data.ScrobbleDatabase import com.frontmatic.scrobbleview.data.api.LastFMApi import com.frontmatic.scrobbleview.data.model.RecentTrack import com.frontmatic.scrobbleview.data.model.RecentTracksRemoteKeys -import com.frontmatic.scrobbleview.data.repository.DataStoreOperations +import com.frontmatic.scrobbleview.data.source.DataStoreOperations import kotlinx.coroutines.flow.first import javax.inject.Inject diff --git a/app/src/main/java/com/frontmatic/scrobbleview/data/paging/TopTracksRemoteMediator.kt b/app/src/main/java/com/frontmatic/scrobbleview/data/paging/TopTracksRemoteMediator.kt index 405b1c2..285dbee 100644 --- a/app/src/main/java/com/frontmatic/scrobbleview/data/paging/TopTracksRemoteMediator.kt +++ b/app/src/main/java/com/frontmatic/scrobbleview/data/paging/TopTracksRemoteMediator.kt @@ -10,7 +10,7 @@ import com.frontmatic.scrobbleview.data.api.LastFMApi import com.frontmatic.scrobbleview.data.api.RequestPeriod import com.frontmatic.scrobbleview.data.model.TopTrack import com.frontmatic.scrobbleview.data.model.TopTracksRemoteKeys -import com.frontmatic.scrobbleview.data.repository.DataStoreOperations +import com.frontmatic.scrobbleview.data.source.DataStoreOperations import kotlinx.coroutines.flow.first import javax.inject.Inject diff --git a/app/src/main/java/com/frontmatic/scrobbleview/data/repository/Repository.kt b/app/src/main/java/com/frontmatic/scrobbleview/data/repository/Repository.kt index 4049356..8acfd9e 100644 --- a/app/src/main/java/com/frontmatic/scrobbleview/data/repository/Repository.kt +++ b/app/src/main/java/com/frontmatic/scrobbleview/data/repository/Repository.kt @@ -1,10 +1,10 @@ package com.frontmatic.scrobbleview.data.repository -import androidx.paging.PagingData import com.frontmatic.scrobbleview.data.api.RequestPeriod -import com.frontmatic.scrobbleview.data.model.TopTrack import com.frontmatic.scrobbleview.data.model.User -import kotlinx.coroutines.flow.Flow +import com.frontmatic.scrobbleview.data.source.DataStoreOperations +import com.frontmatic.scrobbleview.data.source.LocalDataSource +import com.frontmatic.scrobbleview.data.source.RemoteDataSource import javax.inject.Inject class Repository @Inject constructor( @@ -20,6 +20,7 @@ class Repository @Inject constructor( fun getAllTopTracks(period: RequestPeriod) = remote.getAllTopTracks(period) + suspend fun deleteAllTopTracks() = localDataSource.deleteAllTopTracks() suspend fun deleteAllRecentTracks() = localDataSource.deleteAllRecentTracks() @@ -41,4 +42,5 @@ class Repository @Inject constructor( fun getUserChanged() = dataStore.getUserChanged() fun getUsername() = dataStore.getUsername() + } \ No newline at end of file diff --git a/app/src/main/java/com/frontmatic/scrobbleview/data/repository/TrackRepository.kt b/app/src/main/java/com/frontmatic/scrobbleview/data/repository/TrackRepository.kt new file mode 100644 index 0000000..e32632d --- /dev/null +++ b/app/src/main/java/com/frontmatic/scrobbleview/data/repository/TrackRepository.kt @@ -0,0 +1,79 @@ +package com.frontmatic.scrobbleview.data.repository + +import com.frontmatic.scrobbleview.data.ScrobbleDatabase +import com.frontmatic.scrobbleview.data.api.LastFMApi +import com.frontmatic.scrobbleview.data.model.TrackInfo +import com.frontmatic.scrobbleview.data.model.response.TrackInfoResponse +import com.frontmatic.scrobbleview.data.source.DataStoreOperations +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.FlowPreview +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.firstOrNull +import kotlinx.coroutines.flow.flow +import org.mobilenativefoundation.store.store5.Fetcher +import org.mobilenativefoundation.store.store5.SourceOfTruth +import org.mobilenativefoundation.store.store5.StoreBuilder +import org.mobilenativefoundation.store.store5.StoreReadRequest +import org.mobilenativefoundation.store.store5.StoreReadResponse +import javax.inject.Inject + + +sealed class TrackKey { + data class Mbid(val mbid: String) : TrackKey() + data class NameAndArtist(val track: String, val artist: String) : TrackKey() +} + + +class TrackRepository @Inject constructor( + private val api: LastFMApi, + private val dataStore: DataStoreOperations, + database: ScrobbleDatabase, +) { + private val trackInfoDao = database.trackInfoDao() + + private val trackFetcher = Fetcher.of { key: TrackKey -> + when (key) { + is TrackKey.Mbid -> api.getTrackInfo( + mbid = key.mbid, + user = null, + track = null, + artist = null, + ) + is TrackKey.NameAndArtist -> api.getTrackInfo( + mbid = null, + user = null, + track = key.track, + artist = key.artist + ) + } + + } + + private val trackSourceOfTruth = SourceOfTruth.of( + reader = { mbid: String -> + trackInfoDao.getOne(mbid) + }, + writer = { _: Any, trackInfo: TrackInfo -> + trackInfoDao.addOne(trackInfo) + }, + delete = trackInfoDao::deleteOne, + deleteAll = trackInfoDao::deleteAll, + ) + + + @OptIn(FlowPreview::class, ExperimentalCoroutinesApi::class) + val trackInfoStore = StoreBuilder + .from( + fetcher = trackFetcher, +// sourceOfTruth = trackSourceOfTruth + ).build() + + + fun getTrackInfoByArtistAndTrack(artist: String, track: String): Flow = flow { + trackInfoStore.stream(StoreReadRequest.fresh(TrackKey.NameAndArtist(track, artist))) + .firstOrNull { it is StoreReadResponse.Data } + .let { (it as StoreReadResponse.Data).value } + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/frontmatic/scrobbleview/data/repository/DataStoreOperations.kt b/app/src/main/java/com/frontmatic/scrobbleview/data/source/DataStoreOperations.kt similarity index 82% rename from app/src/main/java/com/frontmatic/scrobbleview/data/repository/DataStoreOperations.kt rename to app/src/main/java/com/frontmatic/scrobbleview/data/source/DataStoreOperations.kt index 792b1b9..9981e8f 100644 --- a/app/src/main/java/com/frontmatic/scrobbleview/data/repository/DataStoreOperations.kt +++ b/app/src/main/java/com/frontmatic/scrobbleview/data/source/DataStoreOperations.kt @@ -1,4 +1,4 @@ -package com.frontmatic.scrobbleview.data.repository +package com.frontmatic.scrobbleview.data.source import kotlinx.coroutines.flow.Flow diff --git a/app/src/main/java/com/frontmatic/scrobbleview/data/repository/LocalDataSource.kt b/app/src/main/java/com/frontmatic/scrobbleview/data/source/LocalDataSource.kt similarity index 84% rename from app/src/main/java/com/frontmatic/scrobbleview/data/repository/LocalDataSource.kt rename to app/src/main/java/com/frontmatic/scrobbleview/data/source/LocalDataSource.kt index e4783e7..d7b23be 100644 --- a/app/src/main/java/com/frontmatic/scrobbleview/data/repository/LocalDataSource.kt +++ b/app/src/main/java/com/frontmatic/scrobbleview/data/source/LocalDataSource.kt @@ -1,4 +1,4 @@ -package com.frontmatic.scrobbleview.data.repository +package com.frontmatic.scrobbleview.data.source import com.frontmatic.scrobbleview.data.model.User diff --git a/app/src/main/java/com/frontmatic/scrobbleview/data/repository/RemoteDataSource.kt b/app/src/main/java/com/frontmatic/scrobbleview/data/source/RemoteDataSource.kt similarity index 82% rename from app/src/main/java/com/frontmatic/scrobbleview/data/repository/RemoteDataSource.kt rename to app/src/main/java/com/frontmatic/scrobbleview/data/source/RemoteDataSource.kt index f80e8bf..be83f36 100644 --- a/app/src/main/java/com/frontmatic/scrobbleview/data/repository/RemoteDataSource.kt +++ b/app/src/main/java/com/frontmatic/scrobbleview/data/source/RemoteDataSource.kt @@ -1,4 +1,4 @@ -package com.frontmatic.scrobbleview.data.repository +package com.frontmatic.scrobbleview.data.source import androidx.paging.PagingData import com.frontmatic.scrobbleview.data.api.RequestPeriod @@ -6,11 +6,11 @@ import com.frontmatic.scrobbleview.data.model.Friend import com.frontmatic.scrobbleview.data.model.RecentTrack import com.frontmatic.scrobbleview.data.model.TopTrack import com.frontmatic.scrobbleview.data.model.Track +import com.frontmatic.scrobbleview.data.model.response.TrackInfoResponse import kotlinx.coroutines.flow.Flow interface RemoteDataSource { fun getAllFriends(): Flow> fun getAllRecentTracks(): Flow> fun getAllTopTracks(period: RequestPeriod): Flow> - } \ No newline at end of file diff --git a/app/src/main/java/com/frontmatic/scrobbleview/data/repository/impl/DataStoreOperationsImpl.kt b/app/src/main/java/com/frontmatic/scrobbleview/data/source/impl/DataStoreOperationsImpl.kt similarity index 94% rename from app/src/main/java/com/frontmatic/scrobbleview/data/repository/impl/DataStoreOperationsImpl.kt rename to app/src/main/java/com/frontmatic/scrobbleview/data/source/impl/DataStoreOperationsImpl.kt index ddb998d..30d843d 100644 --- a/app/src/main/java/com/frontmatic/scrobbleview/data/repository/impl/DataStoreOperationsImpl.kt +++ b/app/src/main/java/com/frontmatic/scrobbleview/data/source/impl/DataStoreOperationsImpl.kt @@ -1,4 +1,4 @@ -package com.frontmatic.scrobbleview.data.repository.impl +package com.frontmatic.scrobbleview.data.source.impl import android.content.Context import androidx.datastore.core.DataStore @@ -8,7 +8,7 @@ import androidx.datastore.preferences.core.edit import androidx.datastore.preferences.core.emptyPreferences import androidx.datastore.preferences.core.stringPreferencesKey import androidx.datastore.preferences.preferencesDataStore -import com.frontmatic.scrobbleview.data.repository.DataStoreOperations +import com.frontmatic.scrobbleview.data.source.DataStoreOperations import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.map diff --git a/app/src/main/java/com/frontmatic/scrobbleview/data/repository/impl/LocalDataSourceImpl.kt b/app/src/main/java/com/frontmatic/scrobbleview/data/source/impl/LocalDataSourceImpl.kt similarity index 87% rename from app/src/main/java/com/frontmatic/scrobbleview/data/repository/impl/LocalDataSourceImpl.kt rename to app/src/main/java/com/frontmatic/scrobbleview/data/source/impl/LocalDataSourceImpl.kt index a4765bc..ecf6086 100644 --- a/app/src/main/java/com/frontmatic/scrobbleview/data/repository/impl/LocalDataSourceImpl.kt +++ b/app/src/main/java/com/frontmatic/scrobbleview/data/source/impl/LocalDataSourceImpl.kt @@ -1,8 +1,8 @@ -package com.frontmatic.scrobbleview.data.repository.impl +package com.frontmatic.scrobbleview.data.source.impl import com.frontmatic.scrobbleview.data.ScrobbleDatabase import com.frontmatic.scrobbleview.data.model.User -import com.frontmatic.scrobbleview.data.repository.LocalDataSource +import com.frontmatic.scrobbleview.data.source.LocalDataSource class LocalDataSourceImpl(database: ScrobbleDatabase): LocalDataSource { private val userDao = database.userDao() diff --git a/app/src/main/java/com/frontmatic/scrobbleview/data/repository/impl/RemoteDataSourceImpl.kt b/app/src/main/java/com/frontmatic/scrobbleview/data/source/impl/RemoteDataSourceImpl.kt similarity index 89% rename from app/src/main/java/com/frontmatic/scrobbleview/data/repository/impl/RemoteDataSourceImpl.kt rename to app/src/main/java/com/frontmatic/scrobbleview/data/source/impl/RemoteDataSourceImpl.kt index 58411c0..b64dbdb 100644 --- a/app/src/main/java/com/frontmatic/scrobbleview/data/repository/impl/RemoteDataSourceImpl.kt +++ b/app/src/main/java/com/frontmatic/scrobbleview/data/source/impl/RemoteDataSourceImpl.kt @@ -1,4 +1,4 @@ -package com.frontmatic.scrobbleview.data.repository.impl +package com.frontmatic.scrobbleview.data.source.impl import androidx.paging.ExperimentalPagingApi import androidx.paging.Pager @@ -10,11 +10,12 @@ import com.frontmatic.scrobbleview.data.api.RequestPeriod import com.frontmatic.scrobbleview.data.model.Friend import com.frontmatic.scrobbleview.data.model.RecentTrack import com.frontmatic.scrobbleview.data.model.TopTrack +import com.frontmatic.scrobbleview.data.model.response.TrackInfoResponse import com.frontmatic.scrobbleview.data.paging.FriendsRemoteMediator import com.frontmatic.scrobbleview.data.paging.RecentTracksRemoteMediator import com.frontmatic.scrobbleview.data.paging.TopTracksRemoteMediator -import com.frontmatic.scrobbleview.data.repository.DataStoreOperations -import com.frontmatic.scrobbleview.data.repository.RemoteDataSource +import com.frontmatic.scrobbleview.data.source.DataStoreOperations +import com.frontmatic.scrobbleview.data.source.RemoteDataSource import kotlinx.coroutines.flow.Flow @@ -51,5 +52,4 @@ class RemoteDataSourceImpl( pagingSourceFactory = { topTrackDao.getAllByPeriod(period = period) } ).flow } - } \ No newline at end of file diff --git a/app/src/main/java/com/frontmatic/scrobbleview/di/DatabaseModule.kt b/app/src/main/java/com/frontmatic/scrobbleview/di/DatabaseModule.kt index 6699195..41e9609 100644 --- a/app/src/main/java/com/frontmatic/scrobbleview/di/DatabaseModule.kt +++ b/app/src/main/java/com/frontmatic/scrobbleview/di/DatabaseModule.kt @@ -3,8 +3,8 @@ package com.frontmatic.scrobbleview.di import android.content.Context import androidx.room.Room import com.frontmatic.scrobbleview.data.ScrobbleDatabase -import com.frontmatic.scrobbleview.data.repository.LocalDataSource -import com.frontmatic.scrobbleview.data.repository.impl.LocalDataSourceImpl +import com.frontmatic.scrobbleview.data.source.LocalDataSource +import com.frontmatic.scrobbleview.data.source.impl.LocalDataSourceImpl import dagger.Module import dagger.Provides import dagger.hilt.InstallIn diff --git a/app/src/main/java/com/frontmatic/scrobbleview/di/NetworkModule.kt b/app/src/main/java/com/frontmatic/scrobbleview/di/NetworkModule.kt index 1c42bc7..85300ad 100644 --- a/app/src/main/java/com/frontmatic/scrobbleview/di/NetworkModule.kt +++ b/app/src/main/java/com/frontmatic/scrobbleview/di/NetworkModule.kt @@ -4,9 +4,9 @@ import androidx.paging.ExperimentalPagingApi import com.frontmatic.scrobbleview.BuildConfig import com.frontmatic.scrobbleview.data.ScrobbleDatabase import com.frontmatic.scrobbleview.data.api.LastFMApi -import com.frontmatic.scrobbleview.data.repository.DataStoreOperations -import com.frontmatic.scrobbleview.data.repository.RemoteDataSource -import com.frontmatic.scrobbleview.data.repository.impl.RemoteDataSourceImpl +import com.frontmatic.scrobbleview.data.source.DataStoreOperations +import com.frontmatic.scrobbleview.data.source.RemoteDataSource +import com.frontmatic.scrobbleview.data.source.impl.RemoteDataSourceImpl import com.frontmatic.scrobbleview.util.Constants.BASE_URL import com.google.gson.GsonBuilder import dagger.Module diff --git a/app/src/main/java/com/frontmatic/scrobbleview/di/RepositoryModule.kt b/app/src/main/java/com/frontmatic/scrobbleview/di/RepositoryModule.kt index 83eba0f..45bce9d 100644 --- a/app/src/main/java/com/frontmatic/scrobbleview/di/RepositoryModule.kt +++ b/app/src/main/java/com/frontmatic/scrobbleview/di/RepositoryModule.kt @@ -14,9 +14,9 @@ import com.frontmatic.scrobbleview.data.SaveUserChangedUseCase import com.frontmatic.scrobbleview.data.SaveUserInfoUseCase import com.frontmatic.scrobbleview.data.SaveUsernameUseCase import com.frontmatic.scrobbleview.data.UseCases -import com.frontmatic.scrobbleview.data.repository.DataStoreOperations +import com.frontmatic.scrobbleview.data.source.DataStoreOperations import com.frontmatic.scrobbleview.data.repository.Repository -import com.frontmatic.scrobbleview.data.repository.impl.DataStoreOperationsImpl +import com.frontmatic.scrobbleview.data.source.impl.DataStoreOperationsImpl import dagger.Module import dagger.Provides import dagger.hilt.InstallIn diff --git a/app/src/main/java/com/frontmatic/scrobbleview/ui/screens/charts/ChartsScreen.kt b/app/src/main/java/com/frontmatic/scrobbleview/ui/screens/charts/ChartsScreen.kt index d26325b..f0ea939 100644 --- a/app/src/main/java/com/frontmatic/scrobbleview/ui/screens/charts/ChartsScreen.kt +++ b/app/src/main/java/com/frontmatic/scrobbleview/ui/screens/charts/ChartsScreen.kt @@ -1,13 +1,19 @@ package com.frontmatic.scrobbleview.ui.screens.charts import androidx.compose.foundation.ExperimentalFoundationApi +import androidx.compose.foundation.gestures.Orientation import androidx.compose.foundation.horizontalScroll import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.wrapContentSize import androidx.compose.foundation.pager.HorizontalPager +import androidx.compose.foundation.pager.PageSize +import androidx.compose.foundation.pager.PagerDefaults +import androidx.compose.foundation.pager.PagerScope +//import androidx.compose.foundation.pager.PagerScope import androidx.compose.foundation.pager.PagerState import androidx.compose.foundation.pager.rememberPagerState import androidx.compose.foundation.rememberScrollState @@ -16,6 +22,7 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import com.frontmatic.scrobbleview.data.api.RequestPeriod import com.frontmatic.scrobbleview.ui.components.TabRow @@ -23,6 +30,7 @@ import com.frontmatic.scrobbleview.ui.components.TabTitle import com.frontmatic.scrobbleview.ui.screens.charts.tabs.RecentTab import com.frontmatic.scrobbleview.ui.screens.charts.tabs.TopTrackTab import com.ramcosta.composedestinations.annotation.Destination +import com.ramcosta.composedestinations.navigation.DestinationsNavigator import kotlinx.coroutines.launch val tabs = listOf( @@ -41,9 +49,15 @@ val tabs = listOf( ) @Composable fun ChartsScreen( + navigator: DestinationsNavigator, chartViewModel: ChartViewModel = hiltViewModel() ) { - val pagerState = rememberPagerState() + val pagerState = rememberPagerState( + initialPage = 0, + initialPageOffsetFraction = 0f + ) { + tabs.size + } val scope = rememberCoroutineScope() val scrollState = rememberScrollState() val currentPage = pagerState.currentPage @@ -83,42 +97,83 @@ fun ChartsScreen( } } } - TabContent(pagerState = pagerState, viewModel = chartViewModel) + TabContent( + pagerState = pagerState, + navigator = navigator, + viewModel = chartViewModel, + ) } } @OptIn(ExperimentalFoundationApi::class) @Composable -fun TabContent(pagerState: PagerState, viewModel: ChartViewModel) { - HorizontalPager(pageCount = tabs.size, state = pagerState) { page -> - Column { - when (page) { - 0 -> RecentTab() - 1 -> TopTrackTab( - tracks = viewModel.sevenDaysTopTracks, - isRefreshing = viewModel.isSevenDayTabRefreshing.value - ) - 2 -> TopTrackTab( - tracks = viewModel.oneMonthTopTracks, - isRefreshing = viewModel.isOneMonthTabRefreshing.value - ) - 3 -> TopTrackTab( - tracks = viewModel.threeMonthsTopTracks, - isRefreshing = viewModel.isThreeMonthTabRefreshing.value - ) - 4 -> TopTrackTab( - tracks = viewModel.sixMonthsTopTracks, - isRefreshing = viewModel.isSixMonthTabRefreshing.value - ) - 5 -> TopTrackTab( - tracks = viewModel.twelveMonthsTopTracks, - isRefreshing = viewModel.isTwelveMonthTabRefreshing.value - ) - 6 -> TopTrackTab( - tracks = viewModel.overallTopTracks, - isRefreshing = viewModel.isOverallTabRefreshing.value - ) +fun TabContent( + pagerState: PagerState, + navigator: DestinationsNavigator, + viewModel: ChartViewModel +) { + HorizontalPager( + modifier = Modifier, + state = pagerState, + pageSpacing = 0.dp, + userScrollEnabled = true, + reverseLayout = false, + contentPadding = PaddingValues(0.dp), + beyondBoundsPageCount = 0, + pageSize = PageSize.Fill, + key = null, + pageNestedScrollConnection = PagerDefaults.pageNestedScrollConnection( + Orientation.Horizontal + ), + ) { + Column { + when (it) { + 0 -> RecentTab(navigator = navigator) + else -> { + val (tracks, isRefreshing) = when (it) { + 1 -> Pair( + viewModel.sevenDaysTopTracks, + viewModel.isSevenDayTabRefreshing.value + ) + + 2 -> Pair( + viewModel.oneMonthTopTracks, + viewModel.isOneMonthTabRefreshing.value + ) + + 3 -> Pair( + viewModel.threeMonthsTopTracks, + viewModel.isThreeMonthTabRefreshing.value + ) + + 4 -> Pair( + viewModel.sixMonthsTopTracks, + viewModel.isSixMonthTabRefreshing.value + ) + + 5 -> Pair( + viewModel.twelveMonthsTopTracks, + viewModel.isTwelveMonthTabRefreshing.value + ) + + 6 -> Pair( + viewModel.overallTopTracks, + viewModel.isOverallTabRefreshing.value + ) + + else -> Pair( + viewModel.sevenDaysTopTracks, + viewModel.isSevenDayTabRefreshing.value + ) + } + TopTrackTab( + tracks = tracks, + navigator = navigator, + isRefreshing = isRefreshing + ) + } + } } } - } + } \ No newline at end of file diff --git a/app/src/main/java/com/frontmatic/scrobbleview/ui/screens/charts/tabs/RecentTab.kt b/app/src/main/java/com/frontmatic/scrobbleview/ui/screens/charts/tabs/RecentTab.kt index 1141864..4bf3409 100644 --- a/app/src/main/java/com/frontmatic/scrobbleview/ui/screens/charts/tabs/RecentTab.kt +++ b/app/src/main/java/com/frontmatic/scrobbleview/ui/screens/charts/tabs/RecentTab.kt @@ -19,6 +19,8 @@ import com.frontmatic.scrobbleview.R import com.frontmatic.scrobbleview.ui.components.ListItem import com.frontmatic.scrobbleview.ui.theme.PAGE_PADDING import com.frontmatic.scrobbleview.util.handlePagingResult +import com.ramcosta.composedestinations.navigation.DestinationsNavigator +import com.frontmatic.scrobbleview.ui.screens.destinations.TrackDetailScreenDestination import java.text.SimpleDateFormat import java.util.Date import java.util.concurrent.TimeUnit @@ -27,6 +29,7 @@ import java.util.concurrent.TimeUnit @OptIn(ExperimentalMaterialApi::class) @Composable fun RecentTab( + navigator: DestinationsNavigator, recentTabViewModel: RecentTabViewModel = hiltViewModel(), ) { val recentTracks = recentTabViewModel.recentTracks.collectAsLazyPagingItems() @@ -55,7 +58,14 @@ fun RecentTab( secondaryText = track.artist.name, trailingText = if (track.date != null) fromSecondsSinceEpoch(track.date.uts) else "Now playing", trailingTextBold = track.date == null, - onClick = { /*TODO*/ }) + onClick = { + navigator.navigate( + TrackDetailScreenDestination( + track.artist.name, + track.name, + ) + ) + }) } } PullRefreshIndicator(refreshing, pullRefreshState, Modifier.align(Alignment.TopCenter)) diff --git a/app/src/main/java/com/frontmatic/scrobbleview/ui/screens/charts/tabs/TopTrackTab.kt b/app/src/main/java/com/frontmatic/scrobbleview/ui/screens/charts/tabs/TopTrackTab.kt index 171d657..c3a87f3 100644 --- a/app/src/main/java/com/frontmatic/scrobbleview/ui/screens/charts/tabs/TopTrackTab.kt +++ b/app/src/main/java/com/frontmatic/scrobbleview/ui/screens/charts/tabs/TopTrackTab.kt @@ -22,6 +22,7 @@ import com.frontmatic.scrobbleview.data.model.TopTrack import com.frontmatic.scrobbleview.ui.components.ListItem import com.frontmatic.scrobbleview.ui.theme.PAGE_PADDING import com.frontmatic.scrobbleview.util.handlePagingResult +import com.ramcosta.composedestinations.navigation.DestinationsNavigator import kotlinx.coroutines.flow.Flow @OptIn(ExperimentalMaterialApi::class) @@ -29,6 +30,7 @@ import kotlinx.coroutines.flow.Flow fun TopTrackTab( tracks: Flow>, isRefreshing: Boolean, + navigator: DestinationsNavigator, ) { val topTracks = tracks.collectAsLazyPagingItems() diff --git a/app/src/main/java/com/frontmatic/scrobbleview/ui/screens/track/TrackDetailScreen.kt b/app/src/main/java/com/frontmatic/scrobbleview/ui/screens/track/TrackDetailScreen.kt new file mode 100644 index 0000000..ad6431f --- /dev/null +++ b/app/src/main/java/com/frontmatic/scrobbleview/ui/screens/track/TrackDetailScreen.kt @@ -0,0 +1,38 @@ +package com.frontmatic.scrobbleview.ui.screens.track + +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.ui.Modifier +import androidx.hilt.navigation.compose.hiltViewModel +import com.ramcosta.composedestinations.annotation.Destination +import com.ramcosta.composedestinations.spec.DestinationStyle +import com.ramcosta.composedestinations.spec.DestinationStyleBottomSheet + +@Destination( + navArgsDelegate = TrackScreenNavArgs::class, +// style = DestinationStyleBottomSheet::class +) +@Composable +fun TrackDetailScreen( + viewModel: TrackDetailViewModel = hiltViewModel(), + +) { + val track by viewModel.trackInfo.collectAsState(initial = null) + + if (track != null) { + Text( + text = "TrackDetailScreen", + modifier = Modifier.fillMaxSize() + ) + + Text(text = track!!.name) + } + + Text( + text = "TrackDetailScreen", + modifier = Modifier.fillMaxSize() + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/frontmatic/scrobbleview/ui/screens/track/TrackDetailViewModel.kt b/app/src/main/java/com/frontmatic/scrobbleview/ui/screens/track/TrackDetailViewModel.kt new file mode 100644 index 0000000..e2d26eb --- /dev/null +++ b/app/src/main/java/com/frontmatic/scrobbleview/ui/screens/track/TrackDetailViewModel.kt @@ -0,0 +1,37 @@ +package com.frontmatic.scrobbleview.ui.screens.track + +import android.util.Log +import androidx.lifecycle.SavedStateHandle +import androidx.lifecycle.ViewModel +import com.frontmatic.scrobbleview.data.UseCases +import com.frontmatic.scrobbleview.data.repository.TrackRepository +import com.frontmatic.scrobbleview.ui.screens.navArgs +import dagger.hilt.android.lifecycle.HiltViewModel +import javax.inject.Inject + +@HiltViewModel +class TrackDetailViewModel @Inject constructor( + private val savedStateHandle: SavedStateHandle, + private val trackRepository: TrackRepository, +) : ViewModel() { + val trackInfo by lazy { + val args = savedStateHandle.navArgs() + val track = args.track + val artist = args.artist + Log.d("TrackDetailViewModel", "track: $track, artist: $artist") + trackRepository.getTrackInfoByArtistAndTrack(artist, track) + } + +// init { +// val args = savedStateHandle.navArgs() +// val track = args.track +// val artist = args.artist +// Log.d("TrackDetailViewModel", "track: $track, artist: $artist") +////// trackRepository.trackInfoStore.stream() +//// } +// +// +// fun getTrackInfoByArtistAndTrack(artis, tracl) = +// trackRepository.getTrackInfoByArtistAndTrack(artist, track) + +} \ No newline at end of file diff --git a/app/src/main/java/com/frontmatic/scrobbleview/ui/screens/track/TrackScreenNavArgs.kt b/app/src/main/java/com/frontmatic/scrobbleview/ui/screens/track/TrackScreenNavArgs.kt new file mode 100644 index 0000000..ef529b2 --- /dev/null +++ b/app/src/main/java/com/frontmatic/scrobbleview/ui/screens/track/TrackScreenNavArgs.kt @@ -0,0 +1,6 @@ +package com.frontmatic.scrobbleview.ui.screens.track + +data class TrackScreenNavArgs( + val artist: String, + val track: String, +) diff --git a/build.gradle b/build.gradle index 0fa7bfa..850a4fa 100644 --- a/build.gradle +++ b/build.gradle @@ -1,9 +1,17 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. +buildscript { + ext { + kotlin_version = '1.9.0' + } +} + plugins { - id 'com.android.application' version '8.0.2' apply false - id 'com.android.library' version '8.0.2' apply false - id 'org.jetbrains.kotlin.android' version '1.7.20' apply false - id 'com.google.dagger.hilt.android' version '2.44' apply false + id 'com.android.application' version '8.1.0' apply false + id 'com.android.library' version '8.1.0' apply false +// id 'org.jetbrains.kotlin.android' version '1.7.20' apply false + id 'org.jetbrains.kotlin.android' version '1.9.0' apply false + id 'com.google.dagger.hilt.android' version '2.46' apply false // add kotlinx serialization plugin - id 'org.jetbrains.kotlin.plugin.serialization' version '1.7.20' apply false +// id 'org.jetbrains.kotlin.plugin.serialization' version '1.7.20' apply false + id 'org.jetbrains.kotlin.plugin.serialization' version '1.9.0' apply false } \ No newline at end of file