From 10571219a5d9ad3ece079dbc666994188fe5d483 Mon Sep 17 00:00:00 2001 From: Matheus Sanches Date: Fri, 8 Sep 2023 00:08:12 -0300 Subject: [PATCH 01/11] chore: small padding changes --- .../mobile/components/HorizontalTracksRow.kt | 9 +++++++-- .../musicorum/mobile/components/ItemInformation.kt | 1 + .../io/musicorum/mobile/components/TopAlbumsRow.kt | 10 ++++++++-- .../io/musicorum/mobile/components/TopArtistsRow.kt | 10 ++++++++-- .../io/musicorum/mobile/components/TrackSheet.kt | 12 ++++++++++-- .../main/java/io/musicorum/mobile/ui/theme/Theme.kt | 1 + .../main/java/io/musicorum/mobile/views/Discover.kt | 2 +- app/src/main/java/io/musicorum/mobile/views/Home.kt | 2 +- .../java/io/musicorum/mobile/views/Scrobbling.kt | 10 +++++++--- .../java/io/musicorum/mobile/views/charts/Charts.kt | 4 ++-- .../mobile/views/charts/PeriodBottomSheet.kt | 5 +++++ 11 files changed, 51 insertions(+), 15 deletions(-) diff --git a/app/src/main/java/io/musicorum/mobile/components/HorizontalTracksRow.kt b/app/src/main/java/io/musicorum/mobile/components/HorizontalTracksRow.kt index fc5a6b5..7ffbb2b 100644 --- a/app/src/main/java/io/musicorum/mobile/components/HorizontalTracksRow.kt +++ b/app/src/main/java/io/musicorum/mobile/components/HorizontalTracksRow.kt @@ -1,6 +1,7 @@ package io.musicorum.mobile.components import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyRow import androidx.compose.foundation.lazy.items @@ -32,9 +33,10 @@ fun HorizontalTracksRow( } else { LazyRow( horizontalArrangement = Arrangement.spacedBy(10.dp), - modifier = Modifier - .padding(start = 20.dp) ) { + item { + Spacer(modifier = Modifier.padding(start = 10.dp)) + } if (tracks == null) { items(4) { _ -> GenericCardPlaceholder(visible = true) @@ -44,6 +46,9 @@ fun HorizontalTracksRow( TrackCard(track = track, labelType) } } + item { + Spacer(modifier = Modifier.padding(start = 10.dp)) + } } } } diff --git a/app/src/main/java/io/musicorum/mobile/components/ItemInformation.kt b/app/src/main/java/io/musicorum/mobile/components/ItemInformation.kt index 8837892..ea5c8f5 100644 --- a/app/src/main/java/io/musicorum/mobile/components/ItemInformation.kt +++ b/app/src/main/java/io/musicorum/mobile/components/ItemInformation.kt @@ -130,5 +130,6 @@ private fun InformationSheet(state: MutableState, text: String, palette Spacer(modifier = Modifier.width(10.dp)) Text(text = "More on Last.fm") } + Spacer(modifier = Modifier.windowInsetsBottomHeight(WindowInsets.navigationBars)) } } diff --git a/app/src/main/java/io/musicorum/mobile/components/TopAlbumsRow.kt b/app/src/main/java/io/musicorum/mobile/components/TopAlbumsRow.kt index 48f3dbf..b9ed48f 100644 --- a/app/src/main/java/io/musicorum/mobile/components/TopAlbumsRow.kt +++ b/app/src/main/java/io/musicorum/mobile/components/TopAlbumsRow.kt @@ -8,6 +8,7 @@ import androidx.compose.foundation.indication import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width @@ -38,12 +39,17 @@ import kotlinx.serialization.json.Json fun TopAlbumsRow(albums: List) { val nav = LocalNavigation.current!! LazyRow( - horizontalArrangement = Arrangement.spacedBy(15.dp), - modifier = Modifier.padding(start = 20.dp) + horizontalArrangement = Arrangement.spacedBy(10.dp), ) { + item { + Spacer(modifier = Modifier.padding(start = 10.dp)) + } items(albums) { album -> AlbumCard(album = album, nav) } + item { + Spacer(modifier = Modifier.padding(start = 10.dp)) + } } } diff --git a/app/src/main/java/io/musicorum/mobile/components/TopArtistsRow.kt b/app/src/main/java/io/musicorum/mobile/components/TopArtistsRow.kt index d8a8315..9ec15ac 100644 --- a/app/src/main/java/io/musicorum/mobile/components/TopArtistsRow.kt +++ b/app/src/main/java/io/musicorum/mobile/components/TopArtistsRow.kt @@ -6,6 +6,7 @@ import androidx.compose.foundation.indication import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size @@ -31,14 +32,19 @@ import io.musicorum.mobile.ui.theme.Typography @Composable fun TopArtistsRow(artists: List) { LazyRow( - horizontalArrangement = Arrangement.spacedBy(15.dp), + horizontalArrangement = Arrangement.spacedBy(10.dp), modifier = Modifier - .padding(start = 20.dp) .fillMaxWidth() ) { + item { + Spacer(modifier = Modifier.padding(start = 10.dp)) + } items(artists) { artist -> ArtistCard(artist) } + item { + Spacer(modifier = Modifier.padding(start = 10.dp)) + } } } diff --git a/app/src/main/java/io/musicorum/mobile/components/TrackSheet.kt b/app/src/main/java/io/musicorum/mobile/components/TrackSheet.kt index 4b9deb9..e2a1927 100644 --- a/app/src/main/java/io/musicorum/mobile/components/TrackSheet.kt +++ b/app/src/main/java/io/musicorum/mobile/components/TrackSheet.kt @@ -10,10 +10,16 @@ import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.navigationBars import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.systemGesturesPadding import androidx.compose.foundation.layout.width +import androidx.compose.foundation.layout.windowInsetsBottomHeight +import androidx.compose.foundation.layout.windowInsetsPadding import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.rounded.Album @@ -81,8 +87,9 @@ fun TrackSheet( ModalBottomSheet( onDismissRequest = { show.value = false }, containerColor = LighterGray, - sheetState = sheetState - ) { + sheetState = sheetState, + + ) { Row( horizontalArrangement = Arrangement.SpaceBetween, @@ -223,5 +230,6 @@ fun TrackSheet( leadingContent = { Icon(Icons.Rounded.Share, null) }, colors = listColors ) + Spacer(modifier = Modifier.windowInsetsBottomHeight(WindowInsets.navigationBars)) } } \ No newline at end of file diff --git a/app/src/main/java/io/musicorum/mobile/ui/theme/Theme.kt b/app/src/main/java/io/musicorum/mobile/ui/theme/Theme.kt index 8ea9dad..9f09119 100644 --- a/app/src/main/java/io/musicorum/mobile/ui/theme/Theme.kt +++ b/app/src/main/java/io/musicorum/mobile/ui/theme/Theme.kt @@ -13,6 +13,7 @@ private val ColorPalette = darkColorScheme( surface = KindaBlack, onPrimary = Color.White, onBackground = Color.White, + onSurfaceVariant = ContentSecondary ) @Composable diff --git a/app/src/main/java/io/musicorum/mobile/views/Discover.kt b/app/src/main/java/io/musicorum/mobile/views/Discover.kt index 5f8dc7b..1d66ee2 100644 --- a/app/src/main/java/io/musicorum/mobile/views/Discover.kt +++ b/app/src/main/java/io/musicorum/mobile/views/Discover.kt @@ -60,7 +60,7 @@ fun Discover(viewModel: DiscoverVm = viewModel()) { Text( "Discover", style = Typography.displaySmall, - modifier = Modifier.padding(start = 15.dp) + modifier = Modifier.padding(start = 20.dp) ) DockedSearchBar( diff --git a/app/src/main/java/io/musicorum/mobile/views/Home.kt b/app/src/main/java/io/musicorum/mobile/views/Home.kt index b6ee7cd..178da6e 100644 --- a/app/src/main/java/io/musicorum/mobile/views/Home.kt +++ b/app/src/main/java/io/musicorum/mobile/views/Home.kt @@ -94,7 +94,7 @@ fun Home(vm: HomeViewModel = hiltViewModel()) { .verticalScroll(rememberScrollState()) .background(KindaBlack) .fillMaxSize() - .padding(top = 30.dp, bottom = 20.dp) + .padding(top = 20.dp, bottom = 20.dp) ) { Row( modifier = Modifier.fillMaxWidth(), diff --git a/app/src/main/java/io/musicorum/mobile/views/Scrobbling.kt b/app/src/main/java/io/musicorum/mobile/views/Scrobbling.kt index b8863ec..0b8c062 100644 --- a/app/src/main/java/io/musicorum/mobile/views/Scrobbling.kt +++ b/app/src/main/java/io/musicorum/mobile/views/Scrobbling.kt @@ -90,13 +90,13 @@ fun Scrobbling(vm: ScrobblingViewModel = hiltViewModel()) { Column( modifier = Modifier .background(KindaBlack) - .padding(top = 30.dp, start = 20.dp, end = 20.dp) + .padding(top = 20.dp) .fillMaxSize() ) { Text( text = "Scrobbling", style = Typography.displaySmall, - modifier = Modifier.padding(bottom = 20.dp) + modifier = Modifier.padding(bottom = 20.dp, start = 20.dp) ) Column { NowPlayingCard( @@ -157,6 +157,7 @@ fun NowPlayingCard(track: Track?, fraction: Float, vm: ScrobblingViewModel) { Box( modifier = Modifier .fillMaxWidth() + .padding(horizontal = 20.dp) .clip(RoundedCornerShape(12.dp)) .background(brush) ) { @@ -236,7 +237,10 @@ private fun TrackList(vm: ScrobblingViewModel, state: LazyListState) { val refreshValue = vm.refreshing.observeAsState(false).value val refreshing = rememberSwipeRefreshState(isRefreshing = refreshValue) SwipeRefresh(state = refreshing, onRefresh = { vm.updateScrobbles() }) { - Column(modifier = Modifier.fillMaxHeight()) { + Column(modifier = Modifier + .fillMaxHeight() + .padding(horizontal = 4.dp) // sum up with 16dp of list padding + ) { val list = if (vm.recentScrobbles.value!!.firstOrNull()?.attributes?.nowPlaying == "true") { vm.recentScrobbles.value!!.drop(1) diff --git a/app/src/main/java/io/musicorum/mobile/views/charts/Charts.kt b/app/src/main/java/io/musicorum/mobile/views/charts/Charts.kt index 64e12d1..f80061d 100644 --- a/app/src/main/java/io/musicorum/mobile/views/charts/Charts.kt +++ b/app/src/main/java/io/musicorum/mobile/views/charts/Charts.kt @@ -116,12 +116,12 @@ fun Charts() { ) { Row( modifier = Modifier - .padding(15.dp) + .padding(20.dp) .fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween ) { Column { - Text(text = "Charts", style = Typography.displayMedium) + Text(text = "Charts", style = Typography.displaySmall) Box( modifier = Modifier .background(EvenLighterGray, RoundedCornerShape(15.dp)) diff --git a/app/src/main/java/io/musicorum/mobile/views/charts/PeriodBottomSheet.kt b/app/src/main/java/io/musicorum/mobile/views/charts/PeriodBottomSheet.kt index 1443c9e..6cba646 100644 --- a/app/src/main/java/io/musicorum/mobile/views/charts/PeriodBottomSheet.kt +++ b/app/src/main/java/io/musicorum/mobile/views/charts/PeriodBottomSheet.kt @@ -2,8 +2,12 @@ package io.musicorum.mobile.views.charts import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.navigationBars import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.windowInsetsBottomHeight import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ModalBottomSheet import androidx.compose.material3.Text @@ -36,5 +40,6 @@ internal fun PeriodBottomSheet( ) } } + Spacer(modifier = Modifier.windowInsetsBottomHeight(WindowInsets.navigationBars)) } } \ No newline at end of file From 6467abcefc90462b7f87d079829b84988dda2946 Mon Sep 17 00:00:00 2001 From: MysteryMS <38635037+MysteryMS@users.noreply.github.com> Date: Sat, 16 Sep 2023 23:00:34 -0300 Subject: [PATCH 02/11] Update source file strings.xml --- app/src/main/res/values/strings.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 8ce1e0e..9e77623 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -67,6 +67,7 @@ Friends activity %1$d scrobble • last 7 days + %1$d scrobbles • last 7 days %1$d scrobbles • last 7 days From From 5feed87fccece0b1bb68ed01f697e37182bd5695 Mon Sep 17 00:00:00 2001 From: MysteryMS <38635037+MysteryMS@users.noreply.github.com> Date: Fri, 22 Sep 2023 13:40:41 -0300 Subject: [PATCH 03/11] New translations strings.xml (Italian) --- app/src/main/res/values-it-rIT/strings.xml | 111 +++++++++++++++++++++ 1 file changed, 111 insertions(+) create mode 100644 app/src/main/res/values-it-rIT/strings.xml diff --git a/app/src/main/res/values-it-rIT/strings.xml b/app/src/main/res/values-it-rIT/strings.xml new file mode 100644 index 0000000..befec62 --- /dev/null +++ b/app/src/main/res/values-it-rIT/strings.xml @@ -0,0 +1,111 @@ + + + Scrobble + Profilo + + Mostra altro + Artisti + Artista + Albums + Album + Tracks + Track + Users + User + Top tracks + Top albums + Top artists + + %1$s track + %1$s tracks + + + %1$s album + %1$s albums + + + %1$s artist + %1$s artists + + Listeners + Scrobbles + Your scrobbles + Settings + Search + Back + + %1$s scrobbles • last 7 days + User profile image + + + %1$s result + %1$s results + + Nothing found + + Similar artists + Recent scrobbles + Most listened • last 7 days + Friends activity + + %1$d scrobble • last 7 days + %1$d scrobbles • last 7 days + + From + Appears on + Most listened tracks + Welcome to the Musicorum app + We need to access your Last.fm account to continue + LOG IN WITH LAST.FM + Scrobbling now + Similar Tracks + This section is coming soon. Stay tuned for updates! + Analytics is essential during internal testing for us to keep track of application crashes. Disabling analytics may difficult our job to provide high-quality builds and minimize the percentage of crash-free users. + Continue + Disable anyway + Cancel + Welcome, %1$s! + Share crash reports and device information + Hold on + Last 30 days + No data available + Register the songs you listen on this device to your Last.fm account + Enable Scrobbling + Notification access + Notification access is needed in order to see which song is being played on the device. Tap here to manage + Scrobble Point + The percentage where the song will be considered as listened + Media apps + Select apps to scrobble from + Enable scrobbling for Spotify? + Make sure you don’t have the Last.fm Spotify connection enabled, otherwise your scrobbles might start duplicating. + Dismiss + Enable + Disabled + Device Scrobbling + Scrobbling settings + About + Donate on Patreon + Join our Discord server + View source code on GitHub + Last.fm Website + Made by Pedro Piva and Matheus Dias. Powered by Musicorum + Grant notification access? + You need to grant notification access so we can see what you’re listening to if you want to scrobble from this device + Open notification settings + I\'ll do it later + Beta + Enabled • no apps + Update now playing + Whether this will update your profile with the current playing song, even if it hasn\'t finished scrobbling + Nothing playing + When you follow people, their listening activity will appear here + Your tracks will be shown here + You\'re offline. + You have pending scrobbles + Your data might be outdated. + + Enabled • %1$d app + Enabled • %1$d apps + + From 13e364662e1f80845582235fc9d8a6c7584f98f1 Mon Sep 17 00:00:00 2001 From: MysteryMS <38635037+MysteryMS@users.noreply.github.com> Date: Fri, 22 Sep 2023 13:40:42 -0300 Subject: [PATCH 04/11] New translations strings.xml (Italian, Switzerland) --- app/src/main/res/values-it-rCH/strings.xml | 111 +++++++++++++++++++++ 1 file changed, 111 insertions(+) create mode 100644 app/src/main/res/values-it-rCH/strings.xml diff --git a/app/src/main/res/values-it-rCH/strings.xml b/app/src/main/res/values-it-rCH/strings.xml new file mode 100644 index 0000000..9903c08 --- /dev/null +++ b/app/src/main/res/values-it-rCH/strings.xml @@ -0,0 +1,111 @@ + + + Scrobbles + Profile + + View more + Artists + Artist + Albums + Album + Tracks + Track + Users + User + Top tracks + Top albums + Top artists + + %1$s track + %1$s tracks + + + %1$s album + %1$s albums + + + %1$s artist + %1$s artists + + Listeners + Scrobbles + Your scrobbles + Settings + Search + Back + + %1$s scrobbles • last 7 days + User profile image + + + %1$s result + %1$s results + + Nothing found + + Similar artists + Recent scrobbles + Most listened • last 7 days + Friends activity + + %1$d scrobble • last 7 days + %1$d scrobbles • last 7 days + + From + Appears on + Most listened tracks + Welcome to the Musicorum app + We need to access your Last.fm account to continue + LOG IN WITH LAST.FM + Scrobbling now + Similar Tracks + This section is coming soon. Stay tuned for updates! + Analytics is essential during internal testing for us to keep track of application crashes. Disabling analytics may difficult our job to provide high-quality builds and minimize the percentage of crash-free users. + Continue + Disable anyway + Cancel + Welcome, %1$s! + Share crash reports and device information + Hold on + Last 30 days + No data available + Register the songs you listen on this device to your Last.fm account + Enable Scrobbling + Notification access + Notification access is needed in order to see which song is being played on the device. Tap here to manage + Scrobble Point + The percentage where the song will be considered as listened + Media apps + Select apps to scrobble from + Enable scrobbling for Spotify? + Make sure you don’t have the Last.fm Spotify connection enabled, otherwise your scrobbles might start duplicating. + Dismiss + Enable + Disabled + Device Scrobbling + Scrobbling settings + About + Donate on Patreon + Join our Discord server + View source code on GitHub + Last.fm Website + Made by Pedro Piva and Matheus Dias. Powered by Musicorum + Grant notification access? + You need to grant notification access so we can see what you’re listening to if you want to scrobble from this device + Open notification settings + I\'ll do it later + Beta + Enabled • no apps + Update now playing + Whether this will update your profile with the current playing song, even if it hasn\'t finished scrobbling + Nothing playing + When you follow people, their listening activity will appear here + Your tracks will be shown here + You\'re offline. + You have pending scrobbles + Your data might be outdated. + + Enabled • %1$d app + Enabled • %1$d apps + + From 5f4f639694b6e9a153056e003d4dfad4b3cf7d85 Mon Sep 17 00:00:00 2001 From: MysteryMS <38635037+MysteryMS@users.noreply.github.com> Date: Fri, 22 Sep 2023 14:41:42 -0300 Subject: [PATCH 05/11] New translations strings.xml (Italian) --- app/src/main/res/values-it-rIT/strings.xml | 168 ++++++++++----------- 1 file changed, 84 insertions(+), 84 deletions(-) diff --git a/app/src/main/res/values-it-rIT/strings.xml b/app/src/main/res/values-it-rIT/strings.xml index befec62..dcfab7e 100644 --- a/app/src/main/res/values-it-rIT/strings.xml +++ b/app/src/main/res/values-it-rIT/strings.xml @@ -6,106 +6,106 @@ Mostra altro Artisti Artista - Albums + Album Album - Tracks - Track - Users - User - Top tracks - Top albums - Top artists + Tracce + Traccia + Utenti + Utente + Migliori tracce + Migliori album + Migliori artisti - %1$s track - %1$s tracks + %1$s traccia + %1$s tracce %1$s album - %1$s albums + %1$s album - %1$s artist - %1$s artists + %1$s artista + %1$s artisti - Listeners - Scrobbles - Your scrobbles - Settings - Search - Back + Ascoltatori + Scrobble + I tuoi scrobble + Impostazioni + Cerca + Indietro - %1$s scrobbles • last 7 days - User profile image + %1$s scrobble • ultimi 7 giorni + Immagine profilo utente - %1$s result - %1$s results + %1$s risultato + %1$s risultati - Nothing found + Nessun risultato - Similar artists - Recent scrobbles - Most listened • last 7 days - Friends activity + Artisti simili + Scrobble recenti + Più ascoltati • ultimi 7 giorni + Attività amici - %1$d scrobble • last 7 days - %1$d scrobbles • last 7 days + %1$d scrobble • ultimi 7 giorni + %1$d scrobble • ultimi 7 giorni - From - Appears on - Most listened tracks - Welcome to the Musicorum app - We need to access your Last.fm account to continue - LOG IN WITH LAST.FM - Scrobbling now - Similar Tracks - This section is coming soon. Stay tuned for updates! - Analytics is essential during internal testing for us to keep track of application crashes. Disabling analytics may difficult our job to provide high-quality builds and minimize the percentage of crash-free users. - Continue - Disable anyway - Cancel - Welcome, %1$s! - Share crash reports and device information - Hold on - Last 30 days - No data available - Register the songs you listen on this device to your Last.fm account - Enable Scrobbling - Notification access - Notification access is needed in order to see which song is being played on the device. Tap here to manage - Scrobble Point - The percentage where the song will be considered as listened - Media apps - Select apps to scrobble from - Enable scrobbling for Spotify? - Make sure you don’t have the Last.fm Spotify connection enabled, otherwise your scrobbles might start duplicating. - Dismiss - Enable - Disabled - Device Scrobbling - Scrobbling settings - About - Donate on Patreon - Join our Discord server - View source code on GitHub - Last.fm Website - Made by Pedro Piva and Matheus Dias. Powered by Musicorum - Grant notification access? - You need to grant notification access so we can see what you’re listening to if you want to scrobble from this device - Open notification settings - I\'ll do it later + Da + Appare in + Tracce più ascoltate + Benvenuto nell\'app Musicorum + È necessario l\'accesso al tuo account Last.fm per continuare + LOG IN CON LAST.FM + Scrobbling in corso + Tracce Simili + Questa sezione è in arrivo. Rimani sintonizzato per aggiornamenti! + Le analytics per noi sono essenziali durante la fase di testing interno per tenere traccia dei crash imprevisti. Disabilitarli potrebbe rendere difficile il nostro lavoro per fornire build di alta qualità e massimizzare la percentuale di utenti senza crash. + Continua + Disabilita comunque + Annulla + Benvenuto, %1$s! + Condividi crash report e informazioni sul dispositivo + Atttendere + Ultimi 30 giorni + Nessun dato disponibile + Registra i brani che ascolti su questo dispositivo nel tuo account Last.fm + Abilita scrobbling + Accesso alle notifiche + L\'accesso alle notifiche è necessario per vedere quale brano sta venendo riprodotto sul dispositivo. Tocca qui per gestire + Punto di scrobble + La percentuale dopo la quale la canzone verrà considerata come ascoltata + App multimediali + Scegli le app per lo scrobbling + Abilitare scrobbling per Spotify? + Assicurati di non avere la connessione di Last.fm a Spotify abilitata, altrimenti i tuoi scrobble potrebbero iniziare a duplicarsi. + Chiudi + Abilita + Disabilitato + Scrobbling dispositivo + Impostazioni scrobbling + Info + Dona su Patreon + Unisciti al nostro server Discord + Visualizza il codice sorgente su GitHub + Sito Last.fm + Realizzato da Pedro Piva e Matheus Dias. Reso possibile da Musicorum + Concedere l\'accesso alle notifiche? + È necessario concedere l\'accesso alle notifiche in modo da poter vedere cosa stai ascoltando se si desidera fare scrobbling da questo dispositivo + Apri impostazioni di notifica + Lo farò più tardi Beta - Enabled • no apps - Update now playing - Whether this will update your profile with the current playing song, even if it hasn\'t finished scrobbling - Nothing playing - When you follow people, their listening activity will appear here - Your tracks will be shown here - You\'re offline. - You have pending scrobbles - Your data might be outdated. + Abilitato • nessuna app + Aggiorna riproduzione attuale + Questo aggiornerà il tuo profilo con la canzone attualmente in riproduzione, anche se lo scrobbling non è ancora terminato + Niente in riproduzione + Quando segui qualcuno, le loro attività di ascolto appariranno qui + Le tue tracce verranno mostrate qui + Sei offline. + Hai scrobble in sospeso + I tuoi dati potrebbero essere obsoleti. - Enabled • %1$d app - Enabled • %1$d apps + Abilitato • %1$d app + Abilitato • %1$d app From 02d7cc112313a09375cc2271d206df1360f06a16 Mon Sep 17 00:00:00 2001 From: Matheus Dias Date: Fri, 22 Sep 2023 23:18:35 -0300 Subject: [PATCH 06/11] chore: update version --- app/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 9952498..fd4c68f 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -49,8 +49,8 @@ android { applicationId "io.musicorum.mobile" minSdk 28 targetSdk 34 - versionCode 62 - versionName "1.18.2-pre-release" + versionCode 63 + versionName "1.18.3-pre-release" //compileSdkPreview = "UpsideDownCake" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" From 5da9115cfac980163fd88c733d338a259e63d2f6 Mon Sep 17 00:00:00 2001 From: MysteryMS Date: Sat, 23 Sep 2023 12:10:15 -0300 Subject: [PATCH 07/11] fix track card text padding and scrobbling --- .../musicorum/mobile/components/TrackCard.kt | 7 +++-- .../mobile/components/TrackListItem.kt | 3 +- .../mobile/services/NotificationListener.kt | 28 ++++++++----------- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/app/src/main/java/io/musicorum/mobile/components/TrackCard.kt b/app/src/main/java/io/musicorum/mobile/components/TrackCard.kt index 756ca8b..d573d7c 100644 --- a/app/src/main/java/io/musicorum/mobile/components/TrackCard.kt +++ b/app/src/main/java/io/musicorum/mobile/components/TrackCard.kt @@ -108,7 +108,7 @@ fun TrackCard(track: Track, labelType: LabelType) { } } - Row { + Row(modifier = Modifier.padding(top = 7.dp)) { if (track.pending) { Icon( Icons.Rounded.CloudOff, @@ -120,12 +120,15 @@ fun TrackCard(track: Track, labelType: LabelType) { tint = ContentSecondary ) } + val maxWidth = if (track.pending) { + 100.dp + } else 120.dp Text( text = track.name, textAlign = TextAlign.Start, style = Typography.bodyLarge, modifier = Modifier - .widthIn(10.dp, 120.dp) + .widthIn(10.dp, maxWidth) .indication(interactionSource, null) .align(CenterVertically), maxLines = 1, diff --git a/app/src/main/java/io/musicorum/mobile/components/TrackListItem.kt b/app/src/main/java/io/musicorum/mobile/components/TrackListItem.kt index f21d395..3571444 100644 --- a/app/src/main/java/io/musicorum/mobile/components/TrackListItem.kt +++ b/app/src/main/java/io/musicorum/mobile/components/TrackListItem.kt @@ -21,9 +21,9 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember -import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment.Companion.CenterVertically import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.alpha import androidx.compose.ui.draw.clip import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource @@ -74,6 +74,7 @@ fun TrackListItem( modifier = Modifier .size(22.dp) .padding(end = 5.dp) + .alpha(.70f) ) } Text( diff --git a/app/src/main/java/io/musicorum/mobile/services/NotificationListener.kt b/app/src/main/java/io/musicorum/mobile/services/NotificationListener.kt index 495cc43..12301ac 100644 --- a/app/src/main/java/io/musicorum/mobile/services/NotificationListener.kt +++ b/app/src/main/java/io/musicorum/mobile/services/NotificationListener.kt @@ -38,13 +38,10 @@ import java.util.Date class NotificationListener : NotificationListenerService() { private val tag = "NotificationListener" - private var lastListened = "" private var job: Job? = null - private var isScrobbleAllowed = false private val networkCallback = object : ConnectivityManager.NetworkCallback() { override fun onAvailable(network: Network) { super.onAvailable(network) - isScrobbleAllowed = true Log.d(tag, "internet connection available. syncing offline scrobbles") CoroutineScope(Dispatchers.IO).launch { val sessionKey = applicationContext.userData.data.map { p -> @@ -75,7 +72,6 @@ class NotificationListener : NotificationListenerService() { override fun onLost(network: Network) { super.onLost(network) Log.d(tag, "internet connection lost") - isScrobbleAllowed = false } } private lateinit var offlineScrobblesRepo: OfflineScrobblesRepository @@ -106,9 +102,6 @@ class NotificationListener : NotificationListenerService() { val artist = player.metadata?.getString(MediaMetadata.METADATA_KEY_ARTIST) val album = player.metadata?.getString(MediaMetadata.METADATA_KEY_ALBUM) - if (lastListened == "$track - $artist") return - lastListened = "$track - $artist" - val scrobbleEnabled = runBlocking { applicationContext.scrobblePrefs.data.map { p -> p[booleanPreferencesKey("enabled")] @@ -166,16 +159,19 @@ class NotificationListener : NotificationListenerService() { return } - if (!isPlayerPaused && updateNowPlaying == true && isScrobbleAllowed) { + if (!isPlayerPaused && updateNowPlaying == true) { CoroutineScope(Dispatchers.IO).launch { - val success = UserEndpoint.updateNowPlaying( - track = track, - album = album, - artist = artist, - albumArtist = albumArtist, - sessionKey = sessionKey!! - ) - Log.d(tag, "is now playing? $success") + try { + val success = UserEndpoint.updateNowPlaying( + track = track, + album = album, + artist = artist, + albumArtist = albumArtist, + sessionKey = sessionKey!! + ) + Log.d(tag, "is now playing? $success") + } catch (_: UnknownHostException) { + } } } if (timeToScrobble < 0) return From 62950411a77f61c9719dc04e84e7033016bfe29f Mon Sep 17 00:00:00 2001 From: MysteryMS Date: Sun, 24 Sep 2023 23:05:16 -0300 Subject: [PATCH 08/11] wip: revamped charts page --- app/build.gradle | 2 +- .../java/io/musicorum/mobile/MainActivity.kt | 15 +- .../mobile/views/charts/BaseChartDetail.kt | 68 ++---- .../mobile/views/charts/ChartTabs.kt | 23 +- .../musicorum/mobile/views/charts/Charts.kt | 223 ++++++++---------- .../mobile/views/charts/PeriodPicker.kt | 82 +++++++ 6 files changed, 230 insertions(+), 183 deletions(-) create mode 100644 app/src/main/java/io/musicorum/mobile/views/charts/PeriodPicker.kt diff --git a/app/build.gradle b/app/build.gradle index fd4c68f..685e824 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -171,7 +171,7 @@ dependencies { implementation 'androidx.core:core-ktx:1.10.0' implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.6.1' - implementation 'androidx.activity:activity-compose:1.7.1' + implementation 'androidx.activity:activity-compose:1.8.0-rc01' implementation "androidx.compose.ui:ui" implementation "androidx.compose.ui:ui-tooling-preview" implementation 'androidx.compose.material3:material3:1.2.0-alpha06' diff --git a/app/src/main/java/io/musicorum/mobile/MainActivity.kt b/app/src/main/java/io/musicorum/mobile/MainActivity.kt index 584591a..3f6ee8a 100644 --- a/app/src/main/java/io/musicorum/mobile/MainActivity.kt +++ b/app/src/main/java/io/musicorum/mobile/MainActivity.kt @@ -9,9 +9,9 @@ import android.os.Bundle import android.util.Log import androidx.activity.ComponentActivity import androidx.activity.compose.setContent +import androidx.activity.enableEdgeToEdge import androidx.activity.result.contract.ActivityResultContracts import androidx.compose.animation.AnimatedVisibility -import androidx.compose.animation.ExperimentalAnimationApi import androidx.compose.animation.core.CubicBezierEasing import androidx.compose.animation.core.tween import androidx.compose.animation.slideInHorizontally @@ -129,11 +129,10 @@ class MainActivity : ComponentActivity() { super.attachBaseContext(Crowdin.wrapContext(newBase)) } - @OptIn(ExperimentalAnimationApi::class) override fun onCreate(savedInstanceState: Bundle?) { + enableEdgeToEdge() super.onCreate(savedInstanceState) - val remoteConfig: FirebaseRemoteConfig = Firebase.remoteConfig val configSettings = remoteConfigSettings { minimumFetchIntervalInSeconds = 10 @@ -185,12 +184,12 @@ class MainActivity : ComponentActivity() { WindowCompat.setDecorFitsSystemWindows(window, true) firebaseAnalytics = Firebase.analytics - /* window.setFlags( - WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS, - WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS - ) + /*window.setFlags( + WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS, + WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS + )*/ - WindowCompat.setDecorFitsSystemWindows(window, false)*/ + //WindowCompat.setDecorFitsSystemWindows(window, false) FirebaseMessaging.getInstance().token.addOnCompleteListener { task -> if (task.isSuccessful) { diff --git a/app/src/main/java/io/musicorum/mobile/views/charts/BaseChartDetail.kt b/app/src/main/java/io/musicorum/mobile/views/charts/BaseChartDetail.kt index eafcd6b..311dabf 100644 --- a/app/src/main/java/io/musicorum/mobile/views/charts/BaseChartDetail.kt +++ b/app/src/main/java/io/musicorum/mobile/views/charts/BaseChartDetail.kt @@ -1,46 +1,39 @@ package io.musicorum.mobile.views.charts import androidx.compose.foundation.background -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.AutoAwesomeMosaic import androidx.compose.material.icons.rounded.ArrowBack -import androidx.compose.material.icons.rounded.DateRange import androidx.compose.material.icons.rounded.GridView import androidx.compose.material.icons.rounded.List -import androidx.compose.material.icons.rounded.MoreVert -import androidx.compose.material3.DropdownMenu -import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.FloatingActionButton import androidx.compose.material3.Icon import androidx.compose.material3.IconButton -import androidx.compose.material3.MediumTopAppBar import androidx.compose.material3.Scaffold import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBar +import androidx.compose.material3.TopAppBarDefaults import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.livedata.observeAsState import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember -import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.unit.dp +import androidx.compose.ui.graphics.Color +import com.google.accompanist.systemuicontroller.rememberSystemUiController import io.musicorum.mobile.LocalNavigation import io.musicorum.mobile.components.CenteredLoadingSpinner import io.musicorum.mobile.models.FetchPeriod import io.musicorum.mobile.models.ResourceEntity import io.musicorum.mobile.router.Routes -import io.musicorum.mobile.ui.theme.EvenLighterGray import io.musicorum.mobile.ui.theme.KindaBlack +import io.musicorum.mobile.ui.theme.LighterGray import io.musicorum.mobile.ui.theme.MostlyRed -import io.musicorum.mobile.utils.PeriodResolver @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -51,6 +44,7 @@ fun BaseChartDetail( val nav = LocalNavigation.current val tabIndex = remember { mutableIntStateOf(index) } val showBottomSheet = remember { mutableStateOf(false) } + val sysUiController = rememberSystemUiController() val busy = viewModel.busy.observeAsState().value val artists = viewModel.artists.observeAsState().value @@ -58,7 +52,6 @@ fun BaseChartDetail( val tracks = viewModel.tracks.observeAsState().value val fetchPeriod = viewModel.period.observeAsState(FetchPeriod.WEEK) - val showDropdown = remember { mutableStateOf(false) } val currentViewMode = viewModel.viewMode.observeAsState(ViewMode.List) if (showBottomSheet.value) { @@ -67,6 +60,7 @@ fun BaseChartDetail( } } + LaunchedEffect(key1 = tabIndex.intValue) { val entity = when (tabIndex.intValue) { 0 -> ResourceEntity.Artist @@ -77,15 +71,11 @@ fun BaseChartDetail( viewModel.refetch(entity) } - val viewModeText = if (currentViewMode.value == ViewMode.Grid) { - "View as list" - } else "View as grid" val viewModeIcon = if (currentViewMode.value == ViewMode.Grid) { Icons.Rounded.List } else Icons.Rounded.GridView fun updateViewMode() { - showDropdown.value = false if (currentViewMode.value == ViewMode.List) { viewModel.viewMode.value = ViewMode.Grid } else { @@ -93,34 +83,18 @@ fun BaseChartDetail( } } + val appBarColors = TopAppBarDefaults.topAppBarColors( + containerColor = LighterGray + ) + Scaffold( topBar = { - MediumTopAppBar( + TopAppBar( + colors = appBarColors, title = { Text("Charts") }, actions = { - IconButton(onClick = { showDropdown.value = true }) { - Box { - Icon(Icons.Rounded.MoreVert, null) - DropdownMenu( - expanded = showDropdown.value, - modifier = Modifier.background(EvenLighterGray), - onDismissRequest = { showDropdown.value = false } - ) { - DropdownMenuItem( - text = { Text(text = viewModeText) }, - onClick = { updateViewMode() }, - leadingIcon = { Icon(viewModeIcon, null) } - ) - DropdownMenuItem( - text = { Text("Change period") }, - onClick = { - showDropdown.value = false - showBottomSheet.value = true - }, - leadingIcon = { Icon(Icons.Rounded.DateRange, null) } - ) - } - } + IconButton(onClick = { updateViewMode() }) { + Icon(viewModeIcon, null, tint = Color.White) } }, navigationIcon = { @@ -145,17 +119,7 @@ fun BaseChartDetail( .fillMaxSize() ) { ChartTabs(tabIndex) - Box(modifier = Modifier.padding(start = 20.dp, top = 10.dp, bottom = 30.dp)) { - Box( - modifier = Modifier - .background(EvenLighterGray, RoundedCornerShape(27.dp)) - .padding(horizontal = 9.dp, vertical = 1.dp) - .clickable { showBottomSheet.value = true }, - contentAlignment = Alignment.Center - ) { - Text(text = PeriodResolver.resolve(fetchPeriod.value)) - } - } + if (busy == true) { CenteredLoadingSpinner() } else { diff --git a/app/src/main/java/io/musicorum/mobile/views/charts/ChartTabs.kt b/app/src/main/java/io/musicorum/mobile/views/charts/ChartTabs.kt index ff9c724..a95d683 100644 --- a/app/src/main/java/io/musicorum/mobile/views/charts/ChartTabs.kt +++ b/app/src/main/java/io/musicorum/mobile/views/charts/ChartTabs.kt @@ -1,27 +1,44 @@ package io.musicorum.mobile.views.charts import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.rounded.Album +import androidx.compose.material.icons.outlined.Album import androidx.compose.material.icons.rounded.MusicNote import androidx.compose.material.icons.rounded.Star import androidx.compose.material3.Icon import androidx.compose.material3.Tab import androidx.compose.material3.TabRow +import androidx.compose.material3.TabRowDefaults +import androidx.compose.material3.TabRowDefaults.tabIndicatorOffset import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.MutableState +import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color +import androidx.compose.ui.unit.dp import io.musicorum.mobile.ui.theme.ContentSecondary +import io.musicorum.mobile.ui.theme.LighterGray @Composable internal fun ChartTabs(index: MutableState) { val titles = listOf( "Artists" to Icons.Rounded.Star, - "Albums" to Icons.Rounded.Album, + "Albums" to Icons.Outlined.Album, "Tracks" to Icons.Rounded.MusicNote ) - TabRow(selectedTabIndex = index.value) { + + TabRow( + indicator = { + if (index.value < it.size) { + TabRowDefaults.PrimaryIndicator( + modifier = Modifier.tabIndicatorOffset(it[index.value]), + width = 40.dp + ) + } + }, + selectedTabIndex = index.value, + containerColor = LighterGray + ) { titles.forEachIndexed { i, v -> Tab( selected = index.value == i, diff --git a/app/src/main/java/io/musicorum/mobile/views/charts/Charts.kt b/app/src/main/java/io/musicorum/mobile/views/charts/Charts.kt index f80061d..f37b5b0 100644 --- a/app/src/main/java/io/musicorum/mobile/views/charts/Charts.kt +++ b/app/src/main/java/io/musicorum/mobile/views/charts/Charts.kt @@ -3,12 +3,12 @@ package io.musicorum.mobile.views.charts import androidx.compose.animation.animateColorAsState import androidx.compose.foundation.Image import androidx.compose.foundation.background -import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.navigationBarsPadding @@ -24,10 +24,8 @@ import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.AutoAwesomeMosaic import androidx.compose.material.icons.outlined.Album import androidx.compose.material.icons.rounded.ChevronRight -import androidx.compose.material.icons.rounded.DateRange import androidx.compose.material.icons.rounded.MusicNote import androidx.compose.material.icons.rounded.Star -import androidx.compose.material3.FilledIconButton import androidx.compose.material3.FloatingActionButton import androidx.compose.material3.Icon import androidx.compose.material3.IconButton @@ -70,12 +68,10 @@ import io.musicorum.mobile.components.CenteredLoadingSpinner import io.musicorum.mobile.models.ResourceEntity import io.musicorum.mobile.router.Routes import io.musicorum.mobile.ui.theme.ContentSecondary -import io.musicorum.mobile.ui.theme.EvenLighterGray import io.musicorum.mobile.ui.theme.LighterGray import io.musicorum.mobile.ui.theme.MostlyRed import io.musicorum.mobile.ui.theme.SkeletonSecondaryColor import io.musicorum.mobile.ui.theme.Typography -import io.musicorum.mobile.utils.PeriodResolver import io.musicorum.mobile.utils.Placeholders import io.musicorum.mobile.utils.createPalette import io.musicorum.mobile.utils.getBitmap @@ -108,131 +104,120 @@ fun Charts() { val userGradient = getDarkenGradient(userColor) Scaffold(floatingActionButton = { CollageFab() }) { paddingValues -> - Column( - modifier = Modifier - .padding(paddingValues) - .navigationBarsPadding() - .verticalScroll(rememberScrollState()) - ) { - Row( + Column { + Column( modifier = Modifier - .padding(20.dp) - .fillMaxWidth(), - horizontalArrangement = Arrangement.SpaceBetween + .padding(paddingValues) + .navigationBarsPadding() + .fillMaxHeight(.94f) + .verticalScroll(rememberScrollState()) ) { - Column { - Text(text = "Charts", style = Typography.displaySmall) + Text( + text = "Charts", + style = Typography.displaySmall, + modifier = Modifier.padding(20.dp) + ) + + Box(modifier = Modifier.padding(15.dp)) { Box( modifier = Modifier - .background(EvenLighterGray, RoundedCornerShape(15.dp)) - .clickable { showBottomSheet.value = true } + .background( + Brush.linearGradient(userGradient.asReversed()), + RoundedCornerShape(12.dp) + ) + .padding(start = 10.dp) + .fillMaxWidth() + .height(70.dp), + contentAlignment = Alignment.CenterStart ) { - Text( - text = PeriodResolver.resolve(period.value!!), - modifier = Modifier.padding(vertical = 3.dp, horizontal = 7.dp) + Column { + Text( + text = topTracks?.attributes?.total ?: ".......", + style = Typography.headlineLarge, + modifier = Modifier.placeholder( + visible = busy, + color = SkeletonSecondaryColor, + highlight = PlaceholderHighlight.shimmer(), + shape = RoundedCornerShape(5.dp) + ) + ) + Text(text = "scrobbles", style = Typography.titleMedium) + } + Image( + painter = painterResource(id = R.drawable.chart_decorations), + contentDescription = null, + modifier = Modifier + .align(CenterEnd) + .alpha(.15f) ) } } - FilledIconButton(onClick = { showBottomSheet.value = true }) { - Icon(Icons.Rounded.DateRange, null) - } - } - Spacer(modifier = Modifier.height(20.dp)) - Box(modifier = Modifier.padding(15.dp)) { - Box( - modifier = Modifier - .background( - Brush.linearGradient(userGradient.asReversed()), - RoundedCornerShape(12.dp) - ) - .padding(start = 10.dp) - .fillMaxWidth() - .height(70.dp), - contentAlignment = Alignment.CenterStart - ) { - Column { - Text( - text = topTracks?.attributes?.total ?: ".......", - style = Typography.headlineLarge, - modifier = Modifier.placeholder( - visible = busy, - color = SkeletonSecondaryColor, - highlight = PlaceholderHighlight.shimmer(), - shape = RoundedCornerShape(5.dp) + + if (busy) { + CenteredLoadingSpinner() + } else { + val topArtist = topArtists?.getOrNull(0) + val topAlbum = topAlbums?.getOrNull(0) + val topTrack = topTracks?.tracks?.getOrNull(0) + ChartComponentBox( + leadImage = topArtist?.bestImageUrl, + trailDetail = Icons.Rounded.Star, + shape = CircleShape, + artist = topArtist?.name, + scrobbleCount = topArtist?.playCount, + top = ResourceEntity.Artist, + album = null, + innerData = topArtists?.drop(1)?.fold(mutableListOf()) { list, artist -> + list.add(ChartData(artist.name, artist.bestImageUrl, artist.playCount)) + list + } + ) + Spacer(modifier = Modifier.height(70.dp)) + ChartComponentBox( + leadImage = topAlbums?.getOrNull(0)?.bestImageUrl, + trailDetail = Icons.Outlined.Album, + shape = RoundedCornerShape(6.dp), + artist = topAlbum?.name, + scrobbleCount = topAlbum?.playCount?.toInt() ?: 0, + album = null, + top = ResourceEntity.Album, + innerData = topAlbums?.drop(1)?.fold(mutableListOf()) { list, album -> + list.add( + ChartData( + album.name, + album.bestImageUrl, + album.playCount?.toInt() ?: 0 + ) ) - ) - Text(text = "scrobbles", style = Typography.titleMedium) - } - Image( - painter = painterResource(id = R.drawable.chart_decorations), - contentDescription = null, - modifier = Modifier - .align(CenterEnd) - .alpha(.15f) + list + } + ) + Spacer(modifier = Modifier.height(70.dp)) + ChartComponentBox( + leadImage = topTracks?.tracks?.getOrNull(0)?.bestImageUrl, + trailDetail = Icons.Rounded.MusicNote, + shape = RoundedCornerShape(6.dp), + artist = topTrack?.name, + scrobbleCount = topTrack?.playCount?.toInt() ?: 0, + album = null, + top = ResourceEntity.Track, + innerData = topTracks?.tracks?.drop(1) + ?.fold(mutableListOf()) { list, track -> + list.add( + ChartData( + track.name, + track.bestImageUrl, + track.playCount?.toInt() ?: 0 + ) + ) + list + } ) + Spacer(modifier = Modifier.height(150.dp)) } } - - if (busy) { - CenteredLoadingSpinner() - } else { - val topArtist = topArtists?.getOrNull(0) - val topAlbum = topAlbums?.getOrNull(0) - val topTrack = topTracks?.tracks?.getOrNull(0) - ChartComponentBox( - leadImage = topArtist?.bestImageUrl, - trailDetail = Icons.Rounded.Star, - shape = CircleShape, - artist = topArtist?.name, - scrobbleCount = topArtist?.playCount, - top = ResourceEntity.Artist, - album = null, - innerData = topArtists?.drop(1)?.fold(mutableListOf()) { list, artist -> - list.add(ChartData(artist.name, artist.bestImageUrl, artist.playCount)) - list - } - ) - Spacer(modifier = Modifier.height(70.dp)) - ChartComponentBox( - leadImage = topAlbums?.getOrNull(0)?.bestImageUrl, - trailDetail = Icons.Outlined.Album, - shape = RoundedCornerShape(6.dp), - artist = topAlbum?.name, - scrobbleCount = topAlbum?.playCount?.toInt() ?: 0, - album = null, - top = ResourceEntity.Album, - innerData = topAlbums?.drop(1)?.fold(mutableListOf()) { list, album -> - list.add( - ChartData( - album.name, - album.bestImageUrl, - album.playCount?.toInt() ?: 0 - ) - ) - list - } - ) - Spacer(modifier = Modifier.height(70.dp)) - ChartComponentBox( - leadImage = topTracks?.tracks?.getOrNull(0)?.bestImageUrl, - trailDetail = Icons.Rounded.MusicNote, - shape = RoundedCornerShape(6.dp), - artist = topTrack?.name, - scrobbleCount = topTrack?.playCount?.toInt() ?: 0, - album = null, - top = ResourceEntity.Track, - innerData = topTracks?.tracks?.drop(1)?.fold(mutableListOf()) { list, track -> - list.add( - ChartData( - track.name, - track.bestImageUrl, - track.playCount?.toInt() ?: 0 - ) - ) - list - } - ) - Spacer(modifier = Modifier.height(150.dp)) + PeriodPicker(true, period.value!!) { + model.updatePeriod(it) } } } @@ -361,7 +346,7 @@ fun ChartComponentBox( model = defaultImageRequestBuilder(url = data.image), contentDescription = null, modifier = Modifier - .size(20.dp) + .size(24.dp) .clip(shape) ) Spacer(modifier = Modifier.width(5.dp)) diff --git a/app/src/main/java/io/musicorum/mobile/views/charts/PeriodPicker.kt b/app/src/main/java/io/musicorum/mobile/views/charts/PeriodPicker.kt new file mode 100644 index 0000000..ae13ff5 --- /dev/null +++ b/app/src/main/java/io/musicorum/mobile/views/charts/PeriodPicker.kt @@ -0,0 +1,82 @@ +package io.musicorum.mobile.views.charts + +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.lazy.LazyRow +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.draw.drawBehind +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.unit.dp +import io.musicorum.mobile.models.FetchPeriod +import io.musicorum.mobile.ui.theme.LighterGray +import io.musicorum.mobile.ui.theme.MostlyRed + +@Composable +fun PeriodPicker( + showDivider: Boolean = true, + selectedPeriod: FetchPeriod, + onPeriodChanged: (FetchPeriod) -> Unit +) { + var mod = Modifier + .clip(RoundedCornerShape(topStart = 16.dp, topEnd = 16.dp)) + .background(LighterGray) + .height(45.dp) + .fillMaxWidth() + + if (showDivider) { + mod = mod.drawBehind { + drawLine( + Color.White, + Offset.Zero, + Offset.Infinite, + 5f + ) + } + } + Box(modifier = mod) { + LazyRow( + modifier = Modifier.fillMaxSize(), + verticalAlignment = Alignment.CenterVertically + ) { + item { + Spacer(modifier = Modifier.width(15.dp)) + } + + FetchPeriod.values().forEach { fp -> + item { + PeriodComponent(period = fp, active = selectedPeriod == fp, onPeriodChanged) + } + } + } + } +} + +@Composable +private fun PeriodComponent(period: FetchPeriod, active: Boolean, onClick: (FetchPeriod) -> Unit) { + val modifier = if (active) { + Modifier + .padding(end = 15.dp) + .clip(RoundedCornerShape(100)) + .background(MostlyRed) + .padding(horizontal = 9.dp) + } else Modifier + .padding(end = 15.dp) + .clickable { onClick(period) } + + Box(modifier = modifier, contentAlignment = Alignment.Center) { + Text(text = period.value) + } +} \ No newline at end of file From 8fc52d860aadd87bd53de4e0a95cb0a300c68e87 Mon Sep 17 00:00:00 2001 From: MysteryMS Date: Mon, 25 Sep 2023 13:50:56 -0300 Subject: [PATCH 09/11] wip revamped charts page, deps upgrades, k2 --- app/build.gradle | 15 +-- .../musicorum/mobile/components/TrackSheet.kt | 6 +- .../mobile/views/charts/BaseChartDetail.kt | 43 +++++---- .../musicorum/mobile/views/charts/Charts.kt | 15 +-- .../mobile/views/charts/PeriodPicker.kt | 92 ++++++++++++------- .../mobile/views/individual/Album.kt | 8 +- .../mobile/views/individual/Track.kt | 3 +- app/src/main/res/values/strings.xml | 6 ++ build.gradle | 18 ++-- gradle.properties | 3 +- 10 files changed, 123 insertions(+), 86 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 685e824..e8c69c5 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -5,7 +5,7 @@ plugins { id 'com.google.dagger.hilt.android' id 'kotlin-kapt' id 'org.jetbrains.kotlin.android' - id 'org.jetbrains.kotlin.plugin.serialization' version '1.7.10' + id 'org.jetbrains.kotlin.plugin.serialization' version '1.9.10' id 'com.google.gms.google-services' id 'com.google.firebase.crashlytics' id 'dagger.hilt.android.plugin' @@ -97,7 +97,7 @@ android { compose true } composeOptions { - kotlinCompilerExtensionVersion '1.4.2' + kotlinCompilerExtensionVersion '1.5.3' } packagingOptions { resources { @@ -118,7 +118,7 @@ play { dependencies { implementation 'com.github.skydoves:balloon-compose:1.5.2' implementation 'androidx.work:work-runtime-ktx:2.7.1' - implementation "androidx.paging:paging-compose:3.2.0-rc01" + implementation "androidx.paging:paging-compose:3.3.0-alpha02" implementation 'com.github.crowdin.mobile-sdk-android:sdk:1.5.7' implementation platform('androidx.compose:compose-bom:2022.11.00') implementation 'com.google.android.play:app-update:2.1.0' @@ -136,11 +136,10 @@ dependencies { implementation "com.google.dagger:hilt-android:2.44.2" implementation 'androidx.core:core-ktx:1.10.0' - kapt "com.google.dagger:hilt-compiler:2.44.2" + kapt "com.google.dagger:hilt-compiler:2.48" implementation 'androidx.hilt:hilt-navigation-compose:1.0.0' - // implementation "com.google.accompanist:accompanist-permissions:0.29.0-alpha17" - implementation 'com.google.firebase:firebase-messaging-ktx:23.1.2' + implementation 'com.google.firebase:firebase-messaging-ktx:23.2.1' implementation "androidx.constraintlayout:constraintlayout-compose:1.0.1" implementation 'app.rive:rive-android:4.0.0' implementation "androidx.startup:startup-runtime:1.1.1" @@ -189,3 +188,7 @@ dependencies { kapt { correctErrorTypes true } + +hilt { + enableAggregatingTask true +} diff --git a/app/src/main/java/io/musicorum/mobile/components/TrackSheet.kt b/app/src/main/java/io/musicorum/mobile/components/TrackSheet.kt index e2a1927..4417831 100644 --- a/app/src/main/java/io/musicorum/mobile/components/TrackSheet.kt +++ b/app/src/main/java/io/musicorum/mobile/components/TrackSheet.kt @@ -16,10 +16,8 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.navigationBars import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size -import androidx.compose.foundation.layout.systemGesturesPadding import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.windowInsetsBottomHeight -import androidx.compose.foundation.layout.windowInsetsPadding import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.rounded.Album @@ -28,8 +26,8 @@ import androidx.compose.material.icons.rounded.FavoriteBorder import androidx.compose.material.icons.rounded.OpenInNew import androidx.compose.material.icons.rounded.Share import androidx.compose.material.icons.rounded.Star -import androidx.compose.material3.Divider import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Icon import androidx.compose.material3.IconToggleButton import androidx.compose.material3.ListItem @@ -148,7 +146,7 @@ fun TrackSheet( } } } - Divider( + HorizontalDivider( modifier = Modifier .fillMaxWidth() .padding(top = 10.dp, start = 20.dp, end = 20.dp), diff --git a/app/src/main/java/io/musicorum/mobile/views/charts/BaseChartDetail.kt b/app/src/main/java/io/musicorum/mobile/views/charts/BaseChartDetail.kt index 311dabf..dd767cd 100644 --- a/app/src/main/java/io/musicorum/mobile/views/charts/BaseChartDetail.kt +++ b/app/src/main/java/io/musicorum/mobile/views/charts/BaseChartDetail.kt @@ -2,6 +2,7 @@ package io.musicorum.mobile.views.charts import androidx.compose.foundation.background import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.consumeWindowInsets import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.material.icons.Icons @@ -25,7 +26,6 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color -import com.google.accompanist.systemuicontroller.rememberSystemUiController import io.musicorum.mobile.LocalNavigation import io.musicorum.mobile.components.CenteredLoadingSpinner import io.musicorum.mobile.models.FetchPeriod @@ -44,7 +44,6 @@ fun BaseChartDetail( val nav = LocalNavigation.current val tabIndex = remember { mutableIntStateOf(index) } val showBottomSheet = remember { mutableStateOf(false) } - val sysUiController = rememberSystemUiController() val busy = viewModel.busy.observeAsState().value val artists = viewModel.artists.observeAsState().value @@ -88,6 +87,11 @@ fun BaseChartDetail( ) Scaffold( + bottomBar = { + PeriodPicker(selectedPeriod = fetchPeriod.value, onPeriodChanged = { + viewModel.updatePeriod(it) + }) + }, topBar = { TopAppBar( colors = appBarColors, @@ -115,22 +119,27 @@ fun BaseChartDetail( Column( modifier = Modifier .padding(it) - .background(KindaBlack) - .fillMaxSize() + .consumeWindowInsets(it) ) { - ChartTabs(tabIndex) - - if (busy == true) { - CenteredLoadingSpinner() - } else { - if (tabIndex.intValue == 0) { - ArtistChartDetail(artists = artists, currentViewMode.value) - } - if (tabIndex.intValue == 1) { - AlbumChartDetail(albums = albums, currentViewMode.value) - } - if (tabIndex.intValue == 2) { - TrackChartDetail(tracks = tracks, currentViewMode.value) + Column( + modifier = Modifier + .background(KindaBlack) + .fillMaxSize() + ) { + ChartTabs(tabIndex) + + if (busy == true) { + CenteredLoadingSpinner() + } else { + if (tabIndex.intValue == 0) { + ArtistChartDetail(artists = artists, currentViewMode.value) + } + if (tabIndex.intValue == 1) { + AlbumChartDetail(albums = albums, currentViewMode.value) + } + if (tabIndex.intValue == 2) { + TrackChartDetail(tracks = tracks, currentViewMode.value) + } } } } diff --git a/app/src/main/java/io/musicorum/mobile/views/charts/Charts.kt b/app/src/main/java/io/musicorum/mobile/views/charts/Charts.kt index f37b5b0..aeefcfc 100644 --- a/app/src/main/java/io/musicorum/mobile/views/charts/Charts.kt +++ b/app/src/main/java/io/musicorum/mobile/views/charts/Charts.kt @@ -104,10 +104,9 @@ fun Charts() { val userGradient = getDarkenGradient(userColor) Scaffold(floatingActionButton = { CollageFab() }) { paddingValues -> - Column { + Column(modifier = Modifier.padding(paddingValues)) { Column( modifier = Modifier - .padding(paddingValues) .navigationBarsPadding() .fillMaxHeight(.94f) .verticalScroll(rememberScrollState()) @@ -226,11 +225,13 @@ fun Charts() { @Composable private fun CollageFab() { val nav = LocalNavigation.current - FloatingActionButton( - onClick = { nav?.navigate(Routes.collage()) }, - containerColor = MostlyRed - ) { - Icon(Icons.Filled.AutoAwesomeMosaic, null) + Box(modifier = Modifier.padding(bottom = 35.dp)) { + FloatingActionButton( + onClick = { nav?.navigate(Routes.collage()) }, + containerColor = MostlyRed + ) { + Icon(Icons.Filled.AutoAwesomeMosaic, null) + } } } diff --git a/app/src/main/java/io/musicorum/mobile/views/charts/PeriodPicker.kt b/app/src/main/java/io/musicorum/mobile/views/charts/PeriodPicker.kt index ae13ff5..9051896 100644 --- a/app/src/main/java/io/musicorum/mobile/views/charts/PeriodPicker.kt +++ b/app/src/main/java/io/musicorum/mobile/views/charts/PeriodPicker.kt @@ -1,8 +1,10 @@ package io.musicorum.mobile.views.charts +import androidx.compose.animation.AnimatedContent import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth @@ -11,15 +13,16 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyRow import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip -import androidx.compose.ui.draw.drawBehind -import androidx.compose.ui.geometry.Offset import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp +import io.musicorum.mobile.R import io.musicorum.mobile.models.FetchPeriod import io.musicorum.mobile.ui.theme.LighterGray import io.musicorum.mobile.ui.theme.MostlyRed @@ -30,53 +33,72 @@ fun PeriodPicker( selectedPeriod: FetchPeriod, onPeriodChanged: (FetchPeriod) -> Unit ) { - var mod = Modifier + val periods = listOf( + FetchPeriod.WEEK to stringResource(R.string.last_7_days), + FetchPeriod.MONTH to stringResource(R.string.last_30_days), + FetchPeriod.TRIMESTER to stringResource(R.string.last_90_days), + FetchPeriod.SEMESTER to stringResource(R.string.last_6_months), + FetchPeriod.YEAR to stringResource(R.string.last_12_months), + FetchPeriod.OVERALL to stringResource(R.string.overall) + ) + val mod = Modifier .clip(RoundedCornerShape(topStart = 16.dp, topEnd = 16.dp)) .background(LighterGray) .height(45.dp) .fillMaxWidth() - if (showDivider) { - mod = mod.drawBehind { - drawLine( - Color.White, - Offset.Zero, - Offset.Infinite, - 5f - ) - } - } - Box(modifier = mod) { - LazyRow( - modifier = Modifier.fillMaxSize(), - verticalAlignment = Alignment.CenterVertically - ) { - item { - Spacer(modifier = Modifier.width(15.dp)) - } - - FetchPeriod.values().forEach { fp -> + Column { + Box(modifier = mod) { + LazyRow( + modifier = Modifier.fillMaxSize(), + verticalAlignment = Alignment.CenterVertically + ) { item { - PeriodComponent(period = fp, active = selectedPeriod == fp, onPeriodChanged) + Spacer(modifier = Modifier.width(15.dp)) + } + + periods.forEach { fp -> + item { + PeriodComponent( + period = fp, + active = selectedPeriod == fp.first, + onPeriodChanged + ) + } } } } + if (showDivider) { + HorizontalDivider(thickness = 1.dp, color = Color.White) + } } } @Composable -private fun PeriodComponent(period: FetchPeriod, active: Boolean, onClick: (FetchPeriod) -> Unit) { - val modifier = if (active) { - Modifier - .padding(end = 15.dp) - .clip(RoundedCornerShape(100)) - .background(MostlyRed) - .padding(horizontal = 9.dp) - } else Modifier +private fun PeriodComponent( + period: Pair, + active: Boolean, + onClick: (FetchPeriod) -> Unit +) { + val activeMod = Modifier + .padding(end = 15.dp) + .clip(RoundedCornerShape(100)) + .background(MostlyRed) + .padding(horizontal = 9.dp, vertical = 1.dp) + + val normalMod = Modifier .padding(end = 15.dp) - .clickable { onClick(period) } + .clickable { onClick(period.first) } - Box(modifier = modifier, contentAlignment = Alignment.Center) { - Text(text = period.value) + AnimatedContent(targetState = active, label = "period_picker") { + when (it) { + true -> Box(modifier = activeMod, contentAlignment = Alignment.Center) { + Text(text = period.second) + } + + false -> Box(modifier = normalMod, contentAlignment = Alignment.Center) { + Text(text = period.second) + } + } } } \ No newline at end of file diff --git a/app/src/main/java/io/musicorum/mobile/views/individual/Album.kt b/app/src/main/java/io/musicorum/mobile/views/individual/Album.kt index fdb0a5b..cb8ab77 100644 --- a/app/src/main/java/io/musicorum/mobile/views/individual/Album.kt +++ b/app/src/main/java/io/musicorum/mobile/views/individual/Album.kt @@ -13,7 +13,6 @@ import androidx.compose.material3.* import androidx.compose.runtime.* import androidx.compose.runtime.livedata.observeAsState import androidx.compose.ui.Alignment -import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.pluralStringResource @@ -37,7 +36,6 @@ import io.musicorum.mobile.utils.createPalette import io.musicorum.mobile.utils.getBitmap import io.musicorum.mobile.viewmodels.AlbumViewModel import kotlinx.serialization.Serializable -import kotlinx.serialization.decodeFromString import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json @@ -137,7 +135,7 @@ fun Album( color = ContentSecondary ) - Divider(Modifier.padding(vertical = 20.dp)) + HorizontalDivider(Modifier.padding(vertical = 20.dp)) StatisticRow( true, @@ -162,7 +160,7 @@ fun Album( } } - Divider(Modifier.padding(vertical = 20.dp)) + HorizontalDivider(Modifier.padding(vertical = 20.dp)) ContextRow(appearsOn = null, from = Pair(album.artist, artistImage)) val navAlbum = @@ -170,7 +168,7 @@ fun Album( album.tracks?.let { if (it.size > 1) { - Divider(Modifier.padding(vertical = 20.dp)) + HorizontalDivider(Modifier.padding(vertical = 20.dp)) Row( horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically, diff --git a/app/src/main/java/io/musicorum/mobile/views/individual/Track.kt b/app/src/main/java/io/musicorum/mobile/views/individual/Track.kt index b12775a..05da525 100644 --- a/app/src/main/java/io/musicorum/mobile/views/individual/Track.kt +++ b/app/src/main/java/io/musicorum/mobile/views/individual/Track.kt @@ -36,7 +36,6 @@ import io.musicorum.mobile.utils.createPalette import io.musicorum.mobile.utils.getBitmap import io.musicorum.mobile.viewmodels.TrackViewModel import kotlinx.coroutines.launch -import kotlinx.serialization.decodeFromString import kotlinx.serialization.json.Json @@ -189,7 +188,7 @@ fun Track( from = track.artist.name to track.artist.bestImageUrl ) similarTracks?.let { - Divider(Modifier.padding(vertical = 20.dp)) + HorizontalDivider(Modifier.padding(vertical = 20.dp)) Column( Modifier .padding(horizontal = 5.dp) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 9e77623..74a5207 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -123,6 +123,12 @@ You\'re offline. You have pending scrobbles Your data might be outdated. + last 7 days + last 30 days + last 90 days + last 6 months + last 12 months + overall Enabled • %1$d app Enabled • %1$d apps diff --git a/build.gradle b/build.gradle index f4e0f9a..be25c3e 100644 --- a/build.gradle +++ b/build.gradle @@ -1,14 +1,14 @@ buildscript { ext { - compose_version = '1.2.1' - ktor_version = '2.2.1' - kotlin_version = '1.8.10' + compose_version = '1.5.1' + ktor_version = '2.3.4' + kotlin_version = '1.9.10' } dependencies { - classpath 'com.google.gms:google-services:4.3.15' - classpath 'com.google.firebase:firebase-crashlytics-gradle:2.9.2' - classpath "com.google.dagger:hilt-android-gradle-plugin:2.44.2" + classpath 'com.google.gms:google-services:4.4.0' + classpath 'com.google.firebase:firebase-crashlytics-gradle:2.9.9' + classpath "com.google.dagger:hilt-android-gradle-plugin:2.48" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" // classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } @@ -19,7 +19,7 @@ buildscript { 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.google.dagger.hilt.android' version '2.44.2' apply false - id 'com.google.devtools.ksp' version '1.8.10-1.0.9' apply false + id 'org.jetbrains.kotlin.android' version '1.9.10' apply false + id 'com.google.dagger.hilt.android' version '2.48' apply false + id 'com.google.devtools.ksp' version '1.9.10-1.0.13' apply false } \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index cc3ec91..23dc3cf 100644 --- a/gradle.properties +++ b/gradle.properties @@ -23,4 +23,5 @@ kotlin.code.style=official android.nonTransitiveRClass=true org.gradle.unsafe.configuration-cache=true android.defaults.buildfeatures.buildconfig=true -android.nonFinalResIds=true \ No newline at end of file +android.nonFinalResIds=true +kotlin.experimental.tryK2=true \ No newline at end of file From d70e4130a967a58cfe1ebf736ff27753dba9b7cf Mon Sep 17 00:00:00 2001 From: MysteryMS Date: Sun, 1 Oct 2023 13:07:12 -0300 Subject: [PATCH 10/11] feat: tag view --- app/build.gradle | 4 +- .../java/io/musicorum/mobile/MainActivity.kt | 13 +- .../musicorum/mobile/components/ArtistRow.kt | 9 +- .../mobile/components/GradientHeader.kt | 32 ++-- .../io/musicorum/mobile/components/TagList.kt | 7 + .../mobile/components/TopAlbumsRow.kt | 38 ++++ .../mobile/ktor/endpoints/TagEndpoint.kt | 118 ++++++++++++ .../musicorum/MusicorumAlbumEndpoint.kt | 23 +++ .../java/io/musicorum/mobile/router/Routes.kt | 3 +- .../io/musicorum/mobile/serialization/Tag.kt | 5 +- .../io/musicorum/mobile/serialization/Wiki.kt | 2 +- .../mobile/viewmodels/TagViewmodel.kt | 99 ++++++++++ .../mobile/views/individual/TagView.kt | 172 ++++++++++++++++++ 13 files changed, 503 insertions(+), 22 deletions(-) create mode 100644 app/src/main/java/io/musicorum/mobile/ktor/endpoints/TagEndpoint.kt create mode 100644 app/src/main/java/io/musicorum/mobile/viewmodels/TagViewmodel.kt create mode 100644 app/src/main/java/io/musicorum/mobile/views/individual/TagView.kt diff --git a/app/build.gradle b/app/build.gradle index e8c69c5..1938d11 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -29,7 +29,6 @@ android { namespace 'io.musicorum.mobile' compileSdk 34 - signingConfigs { release { storeFile file(appKeyStoreFile) @@ -39,12 +38,11 @@ android { } } - lintOptions { - disable 'MissingTranslation' disable 'NullSafeMutableLiveData' } + defaultConfig { applicationId "io.musicorum.mobile" minSdk 28 diff --git a/app/src/main/java/io/musicorum/mobile/MainActivity.kt b/app/src/main/java/io/musicorum/mobile/MainActivity.kt index 3f6ee8a..6f48ed7 100644 --- a/app/src/main/java/io/musicorum/mobile/MainActivity.kt +++ b/app/src/main/java/io/musicorum/mobile/MainActivity.kt @@ -86,6 +86,7 @@ import io.musicorum.mobile.views.individual.Album import io.musicorum.mobile.views.individual.AlbumTracklist import io.musicorum.mobile.views.individual.Artist import io.musicorum.mobile.views.individual.PartialAlbum +import io.musicorum.mobile.views.individual.TagScreen import io.musicorum.mobile.views.individual.Track import io.musicorum.mobile.views.individual.User import io.musicorum.mobile.views.login.loginGraph @@ -96,7 +97,6 @@ import io.sentry.android.core.SentryAndroid import io.sentry.compose.withSentryObservableEffect import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.flow.map -import kotlinx.serialization.decodeFromString import kotlinx.serialization.json.Json val Context.userData: DataStore by preferencesDataStore(name = "userdata") @@ -397,6 +397,17 @@ class MainActivity : ComponentActivity() { composable("settings") { Settings() } composable("settings/scrobble") { ScrobbleSettings() } + + composable( + "tag/{tagName}", + arguments = listOf( + navArgument("tagName") { + type = NavType.StringType + }, + ) + ) { + TagScreen() + } } } diff --git a/app/src/main/java/io/musicorum/mobile/components/ArtistRow.kt b/app/src/main/java/io/musicorum/mobile/components/ArtistRow.kt index 5b7e876..0578aa2 100644 --- a/app/src/main/java/io/musicorum/mobile/components/ArtistRow.kt +++ b/app/src/main/java/io/musicorum/mobile/components/ArtistRow.kt @@ -6,8 +6,8 @@ import androidx.compose.foundation.indication import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyRow @@ -32,12 +32,17 @@ fun ArtistRow(artists: List) { LazyRow( horizontalArrangement = Arrangement.spacedBy(15.dp), modifier = Modifier - .padding(start = 20.dp) .fillMaxWidth() ) { + item { + Spacer(modifier = Modifier.width(5.dp)) + } items(artists) { artist -> ArtistCard(artist) } + item { + Spacer(modifier = Modifier.width(5.dp)) + } } } diff --git a/app/src/main/java/io/musicorum/mobile/components/GradientHeader.kt b/app/src/main/java/io/musicorum/mobile/components/GradientHeader.kt index bf0f726..3cb2e29 100644 --- a/app/src/main/java/io/musicorum/mobile/components/GradientHeader.kt +++ b/app/src/main/java/io/musicorum/mobile/components/GradientHeader.kt @@ -25,7 +25,13 @@ import io.musicorum.mobile.ui.theme.KindaBlack val HEADER_HEIGHT = 400.dp @Composable -fun GradientHeader(backgroundUrl: String?, coverUrl: String?, shape: Shape, placeholderType: PlaceholderType) { +fun GradientHeader( + backgroundUrl: String?, + coverUrl: String?, + shape: Shape, + placeholderType: PlaceholderType, + showCover: Boolean = true +) { Box( modifier = Modifier .fillMaxWidth() @@ -52,17 +58,19 @@ fun GradientHeader(backgroundUrl: String?, coverUrl: String?, shape: Shape, plac ) ) ) - Row(Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.Center) { - AsyncImage( - model = defaultImageRequestBuilder(url = coverUrl, placeholderType), - contentDescription = "", - modifier = Modifier - .padding(top = 200.dp) - .shadow(elevation = 20.dp, shape = shape, spotColor = Color.Black) - .clip(shape) - .size(300.dp), - contentScale = ContentScale.Crop - ) + if (showCover) { + Row(Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.Center) { + AsyncImage( + model = defaultImageRequestBuilder(url = coverUrl, placeholderType), + contentDescription = "", + modifier = Modifier + .padding(top = 200.dp) + .shadow(elevation = 20.dp, shape = shape, spotColor = Color.Black) + .clip(shape) + .size(300.dp), + contentScale = ContentScale.Crop + ) + } } } } diff --git a/app/src/main/java/io/musicorum/mobile/components/TagList.kt b/app/src/main/java/io/musicorum/mobile/components/TagList.kt index 31e438e..f98d33e 100644 --- a/app/src/main/java/io/musicorum/mobile/components/TagList.kt +++ b/app/src/main/java/io/musicorum/mobile/components/TagList.kt @@ -2,6 +2,7 @@ package io.musicorum.mobile.components import androidx.compose.foundation.background import androidx.compose.foundation.border +import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxWidth @@ -21,6 +22,8 @@ import androidx.palette.graphics.Palette import com.google.accompanist.placeholder.PlaceholderHighlight import com.google.accompanist.placeholder.material.placeholder import com.google.accompanist.placeholder.shimmer +import io.musicorum.mobile.LocalNavigation +import io.musicorum.mobile.router.Routes import io.musicorum.mobile.serialization.TagData import io.musicorum.mobile.ui.theme.SkeletonSecondaryColor import io.musicorum.mobile.ui.theme.Typography @@ -32,6 +35,7 @@ fun TagList(tags: List, referencePalette: Palette?, visible: Boolean) { referencePalette?.getVibrantColor(Color.LightGray.toArgb())?.let { Color(it) } ?: Color.LightGray val background = darkenColor(borderColor.toArgb(), 0.70f) + val nav = LocalNavigation.current LazyRow( Modifier .fillMaxWidth() @@ -44,6 +48,9 @@ fun TagList(tags: List, referencePalette: Palette?, visible: Boolean) { modifier = Modifier .border(1.dp, borderColor, RoundedCornerShape(100)) .clip(RoundedCornerShape(100)) + .clickable { + nav?.navigate(Routes.tag(tag.name)) + } .placeholder( visible, highlight = PlaceholderHighlight.shimmer(SkeletonSecondaryColor) diff --git a/app/src/main/java/io/musicorum/mobile/components/TopAlbumsRow.kt b/app/src/main/java/io/musicorum/mobile/components/TopAlbumsRow.kt index b9ed48f..4981587 100644 --- a/app/src/main/java/io/musicorum/mobile/components/TopAlbumsRow.kt +++ b/app/src/main/java/io/musicorum/mobile/components/TopAlbumsRow.kt @@ -28,6 +28,8 @@ import coil.compose.AsyncImage import io.musicorum.mobile.LocalNavigation import io.musicorum.mobile.coil.PlaceholderType import io.musicorum.mobile.coil.defaultImageRequestBuilder +import io.musicorum.mobile.ktor.endpoints.TagAlbum +import io.musicorum.mobile.router.Routes import io.musicorum.mobile.serialization.TopAlbum import io.musicorum.mobile.ui.theme.Typography import io.musicorum.mobile.views.individual.PartialAlbum @@ -100,4 +102,40 @@ fun AlbumCard(album: TopAlbum, nav: NavHostController) { modifier = Modifier.alpha(0.55f) ) } +} + +@Composable +fun AlbumCard(album: TagAlbum) { + val interactionSource = remember { MutableInteractionSource() } + val nav = LocalNavigation.current + val partialAlbum = + Json.encodeToString(PartialAlbum(album.name, album.artist.name)) + Column(modifier = Modifier + .clickable( + enabled = true, + indication = null, + interactionSource = interactionSource + ) { nav?.navigate(Routes.album(partialAlbum)) } + ) { + AsyncImage( + model = defaultImageRequestBuilder(album.images[0].url, PlaceholderType.ALBUM), + contentDescription = null, + modifier = Modifier + .size(120.dp) + .clip(RoundedCornerShape(8.dp)) + .indication(interactionSource, LocalIndication.current) + ) + Text( + text = album.name, + style = Typography.bodyLarge, + modifier = Modifier.width(120.dp), + maxLines = 1, + overflow = TextOverflow.Ellipsis + ) + Text( + album.artist.name, + style = Typography.bodyMedium, + modifier = Modifier.alpha(0.55f) + ) + } } \ No newline at end of file diff --git a/app/src/main/java/io/musicorum/mobile/ktor/endpoints/TagEndpoint.kt b/app/src/main/java/io/musicorum/mobile/ktor/endpoints/TagEndpoint.kt new file mode 100644 index 0000000..59e0849 --- /dev/null +++ b/app/src/main/java/io/musicorum/mobile/ktor/endpoints/TagEndpoint.kt @@ -0,0 +1,118 @@ +package io.musicorum.mobile.ktor.endpoints + +import io.ktor.client.call.body +import io.ktor.client.request.get +import io.ktor.client.request.parameter +import io.ktor.http.isSuccess +import io.musicorum.mobile.ktor.KtorConfiguration +import io.musicorum.mobile.serialization.Image +import io.musicorum.mobile.serialization.TagData +import io.musicorum.mobile.serialization.entities.Artist +import io.musicorum.mobile.serialization.entities.Track +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +import kotlinx.serialization.builtins.ListSerializer +import kotlinx.serialization.json.Json +import kotlinx.serialization.json.JsonObject +import kotlinx.serialization.json.decodeFromJsonElement +import kotlinx.serialization.json.jsonArray +import kotlinx.serialization.json.jsonObject + +object TagEndpoint { + private val json = Json { ignoreUnknownKeys = true } + + /** + * @param tag The tag name + * @param locale The language to return the wiki in, expressed as an ISO 639 alpha-2 code. + */ + suspend fun getInfo(tag: String, locale: String? = null): TagData? { + val res = KtorConfiguration.lastFmClient.get { + parameter("method", "tag.getInfo") + parameter("tag", tag) + parameter("lang", locale) + } + + return if (res.status.isSuccess()) { + val bodyAsObject = res.body() + val parsed = bodyAsObject["tag"]?.jsonObject + if (parsed != null) { + json.decodeFromJsonElement(parsed) + } else null + } else null + } + + suspend fun getTopAlbums(tag: String, limit: Int? = null, page: Int? = null): List { + val res = KtorConfiguration.lastFmClient.get { + parameter("method", "tag.gettopalbums") + parameter("tag", tag) + parameter("limit", limit) + parameter("page", page) + } + + return if (res.status.isSuccess()) { + val resAsObject = res.body() + val albumList = resAsObject["albums"]?.jsonObject?.get("album")?.jsonArray + + if (albumList != null) { + json.decodeFromJsonElement(ListSerializer(TagAlbum.serializer()), albumList) + } else { + emptyList() + } + } else { + emptyList() + } + } + + suspend fun getTopArtists(tag: String, limit: Int? = null, page: Int? = null): List { + val res = KtorConfiguration.lastFmClient.get { + parameter("method", "tag.gettopartists") + parameter("tag", tag) + parameter("limit", limit) + parameter("page", page) + } + + return if (res.status.isSuccess()) { + val resAsObject = res.body() + val artistList = resAsObject["topartists"]?.jsonObject?.get("artist")?.jsonArray + + if (artistList != null) { + json.decodeFromJsonElement(ListSerializer(Artist.serializer()), artistList) + } else { + emptyList() + } + } else { + emptyList() + } + } + + suspend fun getTopTracks(tag: String, limit: Int? = null, page: Int? = null): List { + val res = KtorConfiguration.lastFmClient.get { + parameter("method", "tag.gettoptracks") + parameter("tag", tag) + parameter("limit", limit) + parameter("page", page) + } + + return if (res.status.isSuccess()) { + val resAsObject = res.body() + val artistList = resAsObject["tracks"]?.jsonObject?.get("track")?.jsonArray + + if (artistList != null) { + json.decodeFromJsonElement(ListSerializer(Track.serializer()), artistList) + } else { + emptyList() + } + } else { + emptyList() + } + } +} + +@Serializable +data class TagAlbum( + val name: String, + val url: String, + val artist: Artist, + @SerialName("image") + val images: List +) diff --git a/app/src/main/java/io/musicorum/mobile/ktor/endpoints/musicorum/MusicorumAlbumEndpoint.kt b/app/src/main/java/io/musicorum/mobile/ktor/endpoints/musicorum/MusicorumAlbumEndpoint.kt index 31b40c9..276eaa1 100644 --- a/app/src/main/java/io/musicorum/mobile/ktor/endpoints/musicorum/MusicorumAlbumEndpoint.kt +++ b/app/src/main/java/io/musicorum/mobile/ktor/endpoints/musicorum/MusicorumAlbumEndpoint.kt @@ -8,6 +8,7 @@ import io.ktor.http.ContentType import io.ktor.http.contentType import io.ktor.http.isSuccess import io.musicorum.mobile.ktor.KtorConfiguration +import io.musicorum.mobile.ktor.endpoints.TagAlbum import io.musicorum.mobile.serialization.entities.Album import io.musicorum.mobile.serialization.musicorum.TrackResponse import kotlinx.serialization.builtins.ListSerializer @@ -35,6 +36,28 @@ object MusicorumAlbumEndpoint { } else emptyList() } + @JvmName("fetchAlbums1") + suspend fun fetchAlbums(albums: List): List { + if (albums.isEmpty()) return emptyList() + val albumList: MutableList = mutableListOf() + albums.forEach { album -> + album?.let { + albumList.add(RequestAlbum(album.name.replace("- Single", ""), album.artist.name)) + } + } + val res = KtorConfiguration.musicorumClient.post { + url("/v2/resources/albums") + contentType(ContentType.Application.Json) + setBody(Body(albumList)) + } + return if (res.status.isSuccess()) { + json.decodeFromString( + ListSerializer(TrackResponse.serializer().nullable), + res.bodyAsText() + ) + } else emptyList() + } + @kotlinx.serialization.Serializable private data class Body( val albums: List diff --git a/app/src/main/java/io/musicorum/mobile/router/Routes.kt b/app/src/main/java/io/musicorum/mobile/router/Routes.kt index d6dcb2a..77ea166 100644 --- a/app/src/main/java/io/musicorum/mobile/router/Routes.kt +++ b/app/src/main/java/io/musicorum/mobile/router/Routes.kt @@ -3,12 +3,10 @@ package io.musicorum.mobile.router import io.musicorum.mobile.models.FetchPeriod import io.musicorum.mobile.models.ResourceEntity import io.musicorum.mobile.serialization.entities.Album -import io.musicorum.mobile.serialization.entities.Artist import io.musicorum.mobile.utils.PeriodResolver import io.musicorum.mobile.views.individual.PartialAlbum import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json -import kotlinx.serialization.json.decodeFromJsonElement object Routes { fun user(username: String) = "user/$username" @@ -28,6 +26,7 @@ object Routes { const val profile = "profile" fun track(data: String) = "track/$data" const val charts = "charts" + fun tag(tagName: String) = "tag/$tagName" fun chartsDetail(index: Int) = "charts/detail?index=${index}" fun collage(entity: ResourceEntity? = null, period: FetchPeriod? = null): String { val periodString = period?.let { PeriodResolver.resolve(it) } ?: "7day" diff --git a/app/src/main/java/io/musicorum/mobile/serialization/Tag.kt b/app/src/main/java/io/musicorum/mobile/serialization/Tag.kt index 9491fbe..d14cb46 100644 --- a/app/src/main/java/io/musicorum/mobile/serialization/Tag.kt +++ b/app/src/main/java/io/musicorum/mobile/serialization/Tag.kt @@ -11,5 +11,8 @@ data class Tag( @Serializable data class TagData( - val name: String + val name: String, + val total: Int? = null, + val reach: Int? = null, + val wiki: Wiki? = null ) diff --git a/app/src/main/java/io/musicorum/mobile/serialization/Wiki.kt b/app/src/main/java/io/musicorum/mobile/serialization/Wiki.kt index 3f3b34a..57c3d33 100644 --- a/app/src/main/java/io/musicorum/mobile/serialization/Wiki.kt +++ b/app/src/main/java/io/musicorum/mobile/serialization/Wiki.kt @@ -2,7 +2,7 @@ package io.musicorum.mobile.serialization @kotlinx.serialization.Serializable data class Wiki( - val published: String, + val published: String? = null, val summary: String, val content: String ) diff --git a/app/src/main/java/io/musicorum/mobile/viewmodels/TagViewmodel.kt b/app/src/main/java/io/musicorum/mobile/viewmodels/TagViewmodel.kt new file mode 100644 index 0000000..731c219 --- /dev/null +++ b/app/src/main/java/io/musicorum/mobile/viewmodels/TagViewmodel.kt @@ -0,0 +1,99 @@ +package io.musicorum.mobile.viewmodels + +import android.app.Application +import android.util.Log +import androidx.lifecycle.AndroidViewModel +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.SavedStateHandle +import androidx.lifecycle.viewModelScope +import androidx.palette.graphics.Palette +import io.musicorum.mobile.ktor.endpoints.TagAlbum +import io.musicorum.mobile.ktor.endpoints.TagEndpoint +import io.musicorum.mobile.ktor.endpoints.musicorum.MusicorumAlbumEndpoint +import io.musicorum.mobile.ktor.endpoints.musicorum.MusicorumArtistEndpoint +import io.musicorum.mobile.ktor.endpoints.musicorum.MusicorumTrackEndpoint +import io.musicorum.mobile.serialization.TagData +import io.musicorum.mobile.serialization.entities.Artist +import io.musicorum.mobile.serialization.entities.Track +import io.musicorum.mobile.utils.getBitmap +import kotlinx.coroutines.async +import kotlinx.coroutines.awaitAll +import kotlinx.coroutines.launch +import javax.inject.Inject + +class TagViewmodel @Inject constructor( + application: Application, + savedStateHandle: SavedStateHandle +) : AndroidViewModel(application) { + val tagInfo = MutableLiveData(null) + val tagAlbums = MutableLiveData?>(null) + val imagePalette = MutableLiveData(null) + val topArtists = MutableLiveData?>(null) + val tracks = MutableLiveData?>(null) + val ctx = application.applicationContext + + //private val _uiState = MutableStateFlow(TagViewState()) + //val uiState: StateFlow = _uiState.asStateFlow() + val tag = savedStateHandle.get("tagName") + + private fun init() { + if (tag == null) return + viewModelScope.launch { + awaitAll( + async { + val tagRes = TagEndpoint.getInfo(tag) + tagInfo.value = tagRes + + }, + async { + val tagRes = TagEndpoint.getTopAlbums(tag) + if (tagRes.isNotEmpty()) { + val musRes = MusicorumAlbumEndpoint.fetchAlbums(tagRes) + if (musRes.isNotEmpty()) { + tagRes.onEachIndexed { index, tagAlbum -> + tagAlbum.images[0].url = + musRes[index]?.bestResource?.bestImageUrl ?: "" + } + } + } + tagAlbums.value = tagRes + }, + + async { + val res = TagEndpoint.getTopArtists(tag) + val topArtist = res.firstOrNull() + topArtist?.let { + val musRes = MusicorumArtistEndpoint.fetchArtist(res) + if (musRes.isNotEmpty()) { + res.onEachIndexed { index, artist -> + artist.bestImageUrl = musRes[index].bestResource?.bestImageUrl ?: "" + } + } + topArtists.value = res + val bmp = getBitmap(it.bestImageUrl, ctx) + val palette = Palette.from(bmp).generate() + imagePalette.value = palette + } + }, + async { + val res = TagEndpoint.getTopTracks(tag) + if (res.isNotEmpty()) { + val musRes = MusicorumTrackEndpoint.fetchTracks(res) + if (musRes.isNotEmpty()) { + res.onEachIndexed { index, track -> + track.bestImageUrl = musRes[index]?.bestResource?.bestImageUrl ?: "" + } + } + tracks.value = res + } + } + ) + + } + } + + init { + init() + Log.d("Tag VM", savedStateHandle.get("tagName") ?: "missing tag") + } +} \ No newline at end of file diff --git a/app/src/main/java/io/musicorum/mobile/views/individual/TagView.kt b/app/src/main/java/io/musicorum/mobile/views/individual/TagView.kt new file mode 100644 index 0000000..01c99c1 --- /dev/null +++ b/app/src/main/java/io/musicorum/mobile/views/individual/TagView.kt @@ -0,0 +1,172 @@ +package io.musicorum.mobile.views.individual + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.offset +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.lazy.LazyRow +import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.HorizontalDivider +import androidx.compose.material3.ListItem +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.livedata.observeAsState +import androidx.compose.ui.Alignment.Companion.CenterHorizontally +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.pluralStringResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import androidx.lifecycle.viewmodel.compose.viewModel +import io.musicorum.mobile.R +import io.musicorum.mobile.coil.PlaceholderType +import io.musicorum.mobile.components.AlbumCard +import io.musicorum.mobile.components.ArtistRow +import io.musicorum.mobile.components.CenteredLoadingSpinner +import io.musicorum.mobile.components.GradientHeader +import io.musicorum.mobile.components.ItemInformation +import io.musicorum.mobile.components.TrackListItem +import io.musicorum.mobile.ui.theme.ContentSecondary +import io.musicorum.mobile.ui.theme.KindaBlack +import io.musicorum.mobile.ui.theme.Typography +import io.musicorum.mobile.viewmodels.TagViewmodel + +@Composable +fun TagScreen(viewModel: TagViewmodel = viewModel()) { + val tagInfo by viewModel.tagInfo.observeAsState(null) + val tagAlbums by viewModel.tagAlbums.observeAsState() + val palette by viewModel.imagePalette.observeAsState(null) + val tracks by viewModel.tracks.observeAsState(null) + val topArtists by viewModel.topArtists.observeAsState(null) + + if (tagInfo == null || tracks == null || topArtists == null) { + CenteredLoadingSpinner() + return + } + + Column( + modifier = Modifier + .fillMaxSize() + .background(KindaBlack) + .verticalScroll(rememberScrollState()) + ) { + GradientHeader( + backgroundUrl = topArtists?.get(0)?.bestImageUrl, + coverUrl = null, + showCover = false, + shape = CircleShape, + placeholderType = PlaceholderType.ARTIST + ) + Text( + text = tagInfo!!.name, + modifier = Modifier + .align(CenterHorizontally) + .offset(y = (-30).dp), + style = Typography.displaySmall + ) + + + tagInfo!!.wiki?.let { + Box(modifier = Modifier.padding(horizontal = 20.dp)) { + ItemInformation(palette = palette, info = it.summary) + } + Spacer(modifier = Modifier.height(20.dp)) + } + HorizontalDivider() + ListItem( + headlineContent = { + Text( + stringResource(id = R.string.top_artists), + style = Typography.headlineSmall + ) + }, + supportingContent = { + Text( + pluralStringResource( + id = R.plurals.artists_quantity, + count = topArtists?.size ?: 0, + topArtists?.size ?: 0 + ), + style = Typography.bodyMedium, + color = ContentSecondary + ) + } + ) + topArtists?.let { + ArtistRow(artists = it) + } + + Spacer(modifier = Modifier.height(35.dp)) + ListItem( + headlineContent = { + Text( + stringResource(id = R.string.top_tracks), + style = Typography.headlineSmall + ) + }, + supportingContent = { + Text( + pluralStringResource( + id = R.plurals.tracks_quantity, + count = tracks?.size ?: 0, + tracks?.size ?: 0 + ), + style = Typography.bodyMedium, + color = ContentSecondary + ) + } + ) + + + val t = tracks?.take(4) + t?.let { trackList -> + trackList.forEach { + TrackListItem(track = it, favoriteIcon = false) + } + } + + Spacer(modifier = Modifier.height(25.dp)) + + ListItem( + headlineContent = { + Text( + stringResource(id = R.string.top_albums), + style = Typography.headlineSmall + ) + }, + supportingContent = { + Text( + pluralStringResource( + id = R.plurals.albums_quantity, + count = tagAlbums?.size ?: 0, + tagAlbums?.size ?: 0 + ), + style = Typography.bodyMedium, + color = ContentSecondary + ) + } + ) + + tagAlbums?.let { + LazyRow(horizontalArrangement = Arrangement.spacedBy(15.dp)) { + item { + Spacer(modifier = Modifier.width(5.dp)) + } + items(it) { + AlbumCard(album = it) + } + } + } + + Spacer(modifier = Modifier.height(20.dp)) + } +} \ No newline at end of file From db35206121ebfea22c38804a9645b085371f88df Mon Sep 17 00:00:00 2001 From: MysteryMS <38635037+MysteryMS@users.noreply.github.com> Date: Sun, 1 Oct 2023 13:09:33 -0300 Subject: [PATCH 11/11] Update build.gradle --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 1938d11..7934293 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -48,7 +48,7 @@ android { minSdk 28 targetSdk 34 versionCode 63 - versionName "1.18.3-pre-release" + versionName "1.19.0-pre-release" //compileSdkPreview = "UpsideDownCake" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"