From e061007e1d265dec4ba083f31793a8fa8f0dd26b Mon Sep 17 00:00:00 2001
From: Outlet7493 <122801553+Outlet7493@users.noreply.github.com>
Date: Mon, 15 Jan 2024 23:32:37 +0000
Subject: [PATCH 01/17] fix crash due to missing permission
---
app/src/main/AndroidManifest.xml | 1 +
1 file changed, 1 insertion(+)
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 115c17e..508ea44 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -5,6 +5,7 @@
+
From 09b307aecefcade75fb654015e5b2c1a449439b1 Mon Sep 17 00:00:00 2001
From: Outlet7493 <122801553+Outlet7493@users.noreply.github.com>
Date: Mon, 15 Jan 2024 23:32:55 +0000
Subject: [PATCH 02/17] typo
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 09424dd..976b73a 100644
--- a/README.md
+++ b/README.md
@@ -21,7 +21,7 @@ Spotify Premium account is **REQUIRED***. Offline caching, DRM bypassing, or raw
- Basic playback with Spotify Connect support (Spotify Connect support is actually WIP)
- Fairly optimized R8 rules, providing the release APKs with a size of 4-5mb (with the playback and protobuf parts!)
-## 📸 Screentshots
+## 📸 Screenshots
From 7059e50adeda855b8fb0b04b2e146c1aa46e5173 Mon Sep 17 00:00:00 2001
From: Outlet7493 <122801553+Outlet7493@users.noreply.github.com>
Date: Mon, 15 Jan 2024 23:33:34 +0000
Subject: [PATCH 03/17] bump hilt; optimize imports; add http logger for debug
---
app/build.gradle.kts | 7 ++++---
.../itaysonlab/jetispot/core/di/ApiModule.kt | 6 ++++++
.../jetispot/ui/screens/yourlibrary2/YlRenderer.kt | 12 ++++++++++--
build.gradle.kts | 2 +-
4 files changed, 21 insertions(+), 6 deletions(-)
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index a6204b8..23f7955 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -1,6 +1,6 @@
+import com.google.protobuf.gradle.*
import java.io.FileInputStream
import java.util.Properties
-import com.google.protobuf.gradle.*
plugins {
id("com.android.application")
@@ -30,7 +30,7 @@ val room_version: String by rootProject.extra
val librespot_commit: String by rootProject.extra
val hilt_version: String by rootProject.extra
-val keystorePropertiesFile = rootProject.file("keystore.properties")
+val keystorePropertiesFile: File = rootProject.file("keystore.properties")
val splitApks = !project.hasProperty("noSplits")
@@ -145,7 +145,7 @@ android {
disable.addAll(listOf("MissingTranslation", "ExtraTranslation"))
}
- packagingOptions {
+ packaging {
resources {
excludes += "/META-INF/*.kotlin_module"
excludes += "/META-INF/*.version"
@@ -229,6 +229,7 @@ dependencies {
implementation("com.squareup.retrofit2:retrofit:2.9.0")
implementation("com.squareup.retrofit2:converter-moshi:2.9.0")
implementation("com.squareup.retrofit2:converter-protobuf:2.9.0")
+ implementation("com.squareup.okhttp3:logging-interceptor:4.12.0")
// Data - SQL
implementation("androidx.room:room-runtime:$room_version")
diff --git a/app/src/main/java/bruhcollective/itaysonlab/jetispot/core/di/ApiModule.kt b/app/src/main/java/bruhcollective/itaysonlab/jetispot/core/di/ApiModule.kt
index 710c57d..8329646 100644
--- a/app/src/main/java/bruhcollective/itaysonlab/jetispot/core/di/ApiModule.kt
+++ b/app/src/main/java/bruhcollective/itaysonlab/jetispot/core/di/ApiModule.kt
@@ -13,6 +13,7 @@ import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import okhttp3.OkHttpClient
+import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Retrofit
import retrofit2.converter.moshi.MoshiConverterFactory
import retrofit2.converter.protobuf.ProtoConverterFactory
@@ -59,6 +60,11 @@ object ApiModule {
}.build())
}
}
+ if (BuildConfig.DEBUG) {
+ addInterceptor(HttpLoggingInterceptor().apply {
+ setLevel(HttpLoggingInterceptor.Level.BODY)
+ })
+ }
}.build()
@Provides
diff --git a/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/yourlibrary2/YlRenderer.kt b/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/yourlibrary2/YlRenderer.kt
index 3ea50b4..955bb54 100644
--- a/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/yourlibrary2/YlRenderer.kt
+++ b/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/yourlibrary2/YlRenderer.kt
@@ -7,7 +7,10 @@ import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.rounded.*
+import androidx.compose.material.icons.rounded.Favorite
+import androidx.compose.material.icons.rounded.Photo
+import androidx.compose.material.icons.rounded.Podcasts
+import androidx.compose.material.icons.rounded.PushPin
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
@@ -19,7 +22,12 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import bruhcollective.itaysonlab.jetispot.R
-import bruhcollective.itaysonlab.jetispot.core.collection.db.model2.*
+import bruhcollective.itaysonlab.jetispot.core.collection.db.model2.CollectionAlbum
+import bruhcollective.itaysonlab.jetispot.core.collection.db.model2.CollectionArtist
+import bruhcollective.itaysonlab.jetispot.core.collection.db.model2.CollectionEntry
+import bruhcollective.itaysonlab.jetispot.core.collection.db.model2.CollectionPinnedItem
+import bruhcollective.itaysonlab.jetispot.core.collection.db.model2.CollectionShow
+import bruhcollective.itaysonlab.jetispot.core.collection.db.model2.PredefCeType
import bruhcollective.itaysonlab.jetispot.core.collection.db.model2.rootlist.CollectionRootlistItem
import bruhcollective.itaysonlab.jetispot.ui.shared.ImagePreview
import bruhcollective.itaysonlab.jetispot.ui.shared.PreviewableAsyncImage
diff --git a/build.gradle.kts b/build.gradle.kts
index d926a48..99942c5 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -11,7 +11,7 @@ buildscript {
val room_version by extra("2.6.1")
val librespot_commit by extra("6244f91aeb")
- val hilt_version by extra("2.49")
+ val hilt_version by extra("2.50")
}
plugins {
From 2fe35449a57b32afba9eb12de0151ae539f3079b Mon Sep 17 00:00:00 2001
From: Outlet7493 <122801553+Outlet7493@users.noreply.github.com>
Date: Mon, 15 Jan 2024 23:52:34 +0000
Subject: [PATCH 04/17] bump kotlin, ksp, moshix
---
.idea/kotlinc.xml | 2 +-
app/build.gradle.kts | 6 +++---
build.gradle.kts | 10 +++++-----
3 files changed, 9 insertions(+), 9 deletions(-)
diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml
index fdf8d99..8d81632 100644
--- a/.idea/kotlinc.xml
+++ b/.idea/kotlinc.xml
@@ -1,6 +1,6 @@
-
+
\ No newline at end of file
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index 23f7955..1aafeba 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -5,12 +5,12 @@ import java.util.Properties
plugins {
id("com.android.application")
id("org.jetbrains.kotlin.android")
- id("dev.zacsweers.moshix") version "0.24.0"
+ id("dev.zacsweers.moshix") version "0.25.1"
id("com.google.devtools.ksp")
id("dagger.hilt.android.plugin")
id("kotlin-kapt")
id("com.google.protobuf") version "0.9.0"
- kotlin("plugin.serialization") version "1.9.0"
+ kotlin("plugin.serialization") version "1.9.22"
}
apply(plugin = "dagger.hilt.android.plugin")
@@ -181,7 +181,7 @@ dependencies {
coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.0.4")
implementation("androidx.core:core-ktx:1.12.0")
implementation("androidx.palette:palette-ktx:1.0.0")
- implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.6.2")
+ implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.7.0")
implementation("androidx.appcompat:appcompat:1.7.0-alpha03")
// Compose
diff --git a/build.gradle.kts b/build.gradle.kts
index 99942c5..32c68ad 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -4,7 +4,7 @@ buildscript {
val compose_version by extra("1.5.0")
val compose_m3_version by extra("1.1.2")
- val compose_compiler_version by extra("1.5.1")
+ val compose_compiler_version by extra("1.5.8")
val media2_version by extra("1.2.1")
val accompanist_version by extra("0.33.0-alpha")
@@ -17,10 +17,10 @@ buildscript {
plugins {
id("com.android.application") version "8.1.4" apply false
id("com.android.library") version "8.1.4" apply false
- id("org.jetbrains.kotlin.android") version "1.9.0" apply false
- id("com.google.dagger.hilt.android") version "2.49" apply false
- id("com.google.devtools.ksp") version "1.9.0-1.0.13" apply false
- kotlin("plugin.serialization") version "1.9.0" apply false
+ id("org.jetbrains.kotlin.android") version "1.9.22" apply false
+ id("com.google.dagger.hilt.android") version "2.50" apply false
+ id("com.google.devtools.ksp") version "1.9.22-1.0.16" apply false
+ kotlin("plugin.serialization") version "1.9.22" apply false
}
tasks.register("clean", Delete::class) {
From 173a4c75411fb85106dd43d33b2294b1a4203d62 Mon Sep 17 00:00:00 2001
From: Outlet7493 <122801553+Outlet7493@users.noreply.github.com>
Date: Tue, 16 Jan 2024 20:39:03 +0000
Subject: [PATCH 05/17] support html for playlist descriptions (e.g. daylist)
---
.../itaysonlab/jetispot/ui/hub/HubBinder.kt | 29 ++++++++++++++++-
.../ui/hub/components/PlaylistHeader.kt | 32 ++++++++++++++-----
2 files changed, 52 insertions(+), 9 deletions(-)
diff --git a/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/hub/HubBinder.kt b/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/hub/HubBinder.kt
index 2fb1647..5d4b8be 100644
--- a/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/hub/HubBinder.kt
+++ b/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/hub/HubBinder.kt
@@ -10,7 +10,34 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import bruhcollective.itaysonlab.jetispot.core.objs.hub.HubComponent
import bruhcollective.itaysonlab.jetispot.core.objs.hub.HubItem
-import bruhcollective.itaysonlab.jetispot.ui.hub.components.*
+import bruhcollective.itaysonlab.jetispot.ui.hub.components.AlbumHeader
+import bruhcollective.itaysonlab.jetispot.ui.hub.components.AlbumTrackRow
+import bruhcollective.itaysonlab.jetispot.ui.hub.components.ArtistHeader
+import bruhcollective.itaysonlab.jetispot.ui.hub.components.ArtistPinnedItem
+import bruhcollective.itaysonlab.jetispot.ui.hub.components.ArtistTrackRow
+import bruhcollective.itaysonlab.jetispot.ui.hub.components.Carousel
+import bruhcollective.itaysonlab.jetispot.ui.hub.components.CollectionHeader
+import bruhcollective.itaysonlab.jetispot.ui.hub.components.EpisodeListItem
+import bruhcollective.itaysonlab.jetispot.ui.hub.components.FindCard
+import bruhcollective.itaysonlab.jetispot.ui.hub.components.GridMediumCard
+import bruhcollective.itaysonlab.jetispot.ui.hub.components.HomeSectionHeader
+import bruhcollective.itaysonlab.jetispot.ui.hub.components.HomeSectionLargeHeader
+import bruhcollective.itaysonlab.jetispot.ui.hub.components.ImageRow
+import bruhcollective.itaysonlab.jetispot.ui.hub.components.LargePlaylistHeader
+import bruhcollective.itaysonlab.jetispot.ui.hub.components.LargerRow
+import bruhcollective.itaysonlab.jetispot.ui.hub.components.LikedSongsRow
+import bruhcollective.itaysonlab.jetispot.ui.hub.components.MediumCard
+import bruhcollective.itaysonlab.jetispot.ui.hub.components.OutlineButton
+import bruhcollective.itaysonlab.jetispot.ui.hub.components.PlaylistHeader
+import bruhcollective.itaysonlab.jetispot.ui.hub.components.PlaylistTrackRow
+import bruhcollective.itaysonlab.jetispot.ui.hub.components.PlaylistTrackRowLarger
+import bruhcollective.itaysonlab.jetispot.ui.hub.components.PodcastTopicsStrip
+import bruhcollective.itaysonlab.jetispot.ui.hub.components.SectionHeader
+import bruhcollective.itaysonlab.jetispot.ui.hub.components.ShortcutsCard
+import bruhcollective.itaysonlab.jetispot.ui.hub.components.ShortcutsContainer
+import bruhcollective.itaysonlab.jetispot.ui.hub.components.ShowHeader
+import bruhcollective.itaysonlab.jetispot.ui.hub.components.SingleFocusCard
+import bruhcollective.itaysonlab.jetispot.ui.hub.components.TextRow
//TODO: FIX UNSUPPORTED ID IN LISTENING HISTORY - BOBBYESP
diff --git a/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/hub/components/PlaylistHeader.kt b/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/hub/components/PlaylistHeader.kt
index dad9f9e..81c732a 100644
--- a/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/hub/components/PlaylistHeader.kt
+++ b/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/hub/components/PlaylistHeader.kt
@@ -1,9 +1,21 @@
package bruhcollective.itaysonlab.jetispot.ui.hub.components
+import android.text.method.LinkMovementMethod
+import android.widget.TextView
import androidx.compose.animation.animateColorAsState
import androidx.compose.foundation.background
import androidx.compose.foundation.isSystemInDarkTheme
-import androidx.compose.foundation.layout.*
+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.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.statusBarsPadding
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.Language
@@ -24,8 +36,9 @@ import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
+import androidx.compose.ui.viewinterop.AndroidView
+import androidx.core.text.HtmlCompat
import bruhcollective.itaysonlab.jetispot.R
-import bruhcollective.itaysonlab.jetispot.SpApp
import bruhcollective.itaysonlab.jetispot.core.objs.hub.HubItem
import bruhcollective.itaysonlab.jetispot.ui.hub.LocalHubScreenDelegate
import bruhcollective.itaysonlab.jetispot.ui.hub.components.essentials.EntityActionStrip
@@ -136,13 +149,16 @@ fun LargePlaylistHeader(
}
if (!item.text?.subtitle.isNullOrEmpty()) {
- Text(
- color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.7f),
- fontSize = 12.sp,
- lineHeight = 18.sp,
- text = item.text?.subtitle!!, modifier = Modifier
+ AndroidView(
+ modifier = Modifier
.padding(horizontal = 16.dp)
- .padding(top = 16.dp)
+ .padding(top = 16.dp),
+ factory = { ctx ->
+ TextView(ctx).apply {
+ text = HtmlCompat.fromHtml(item.text?.subtitle!!, HtmlCompat.FROM_HTML_MODE_COMPACT)
+ movementMethod = LinkMovementMethod.getInstance()
+ }
+ }
)
}
From c1435e36e6944b6021448d44536d3a954d7654d5 Mon Sep 17 00:00:00 2001
From: Outlet7493 <122801553+Outlet7493@users.noreply.github.com>
Date: Tue, 16 Jan 2024 23:02:48 +0000
Subject: [PATCH 06/17] don't render playlists with empty names
---
.../itaysonlab/jetispot/ui/screens/yourlibrary2/YlRenderer.kt | 1 +
1 file changed, 1 insertion(+)
diff --git a/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/yourlibrary2/YlRenderer.kt b/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/yourlibrary2/YlRenderer.kt
index 955bb54..c8f9795 100644
--- a/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/yourlibrary2/YlRenderer.kt
+++ b/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/yourlibrary2/YlRenderer.kt
@@ -120,6 +120,7 @@ fun YLRRootlist(
item: CollectionRootlistItem,
modifier: Modifier
) {
+ if (item.name.isEmpty()) return
YLRGenericItem(
picUrl = item.picture,
picCircle = false,
From 4bb1efad843567f8019aae8e1f2742006ad22b0b Mon Sep 17 00:00:00 2001
From: Outlet7493 <122801553+Outlet7493@users.noreply.github.com>
Date: Wed, 17 Jan 2024 18:48:56 +0000
Subject: [PATCH 07/17] fix usernames on playlist screen and save playlist
images if seen on homescreen
---
.../core/collection/SpCollectionManager.kt | 17 ++++-
.../core/collection/SpCollectionWriter.kt | 28 +++++---
.../core/collection/db/LocalCollectionDao.kt | 23 +++++-
.../jetispot/core/di/CollectionModule.kt | 6 --
.../itaysonlab/jetispot/ui/AppNavigation.kt | 1 -
.../itaysonlab/jetispot/ui/dac/DacDelegate.kt | 5 ++
.../itaysonlab/jetispot/ui/dac/DacRender.kt | 45 +++++++++---
.../components_home/SectionComponentBinder.kt | 42 ++++++++---
.../SectionHeaderComponentBinder.kt | 4 +-
.../ui/dac/components_home/ShortcutsBinder.kt | 70 +++++++++++++------
.../itaysonlab/jetispot/ui/hub/HubBinder.kt | 2 +-
.../ui/hub/virt/PlaylistEntityView.kt | 25 +++++--
.../ui/screens/config/StorageScreen.kt | 50 ++++++++++---
.../ui/screens/dac/DacRendererScreen.kt | 27 ++++++-
.../jetispot/ui/screens/hub/HubExt.kt | 17 ++++-
.../jetispot/ui/screens/hub/HubScreen.kt | 8 +--
.../jetispot/ui/screens/hub/PlaylistScreen.kt | 6 +-
.../ui/screens/yourlibrary2/YlDelegate.kt | 11 +++
.../ui/screens/yourlibrary2/YlRenderer.kt | 14 +++-
.../YourLibraryContainerScreen.kt | 48 ++++++++++---
app/src/main/res/values/strings.xml | 2 +
21 files changed, 352 insertions(+), 99 deletions(-)
create mode 100644 app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/yourlibrary2/YlDelegate.kt
diff --git a/app/src/main/java/bruhcollective/itaysonlab/jetispot/core/collection/SpCollectionManager.kt b/app/src/main/java/bruhcollective/itaysonlab/jetispot/core/collection/SpCollectionManager.kt
index fc5720d..348e1b0 100644
--- a/app/src/main/java/bruhcollective/itaysonlab/jetispot/core/collection/SpCollectionManager.kt
+++ b/app/src/main/java/bruhcollective/itaysonlab/jetispot/core/collection/SpCollectionManager.kt
@@ -1,15 +1,19 @@
package bruhcollective.itaysonlab.jetispot.core.collection
import bruhcollective.itaysonlab.jetispot.core.SpMetadataRequester
-import bruhcollective.itaysonlab.jetispot.core.util.Log
import bruhcollective.itaysonlab.jetispot.core.SpSessionManager
import bruhcollective.itaysonlab.jetispot.core.api.SpCollectionApi
import bruhcollective.itaysonlab.jetispot.core.api.SpInternalApi
import bruhcollective.itaysonlab.jetispot.core.collection.db.LocalCollectionDao
import bruhcollective.itaysonlab.jetispot.core.collection.db.LocalCollectionRepository
+import bruhcollective.itaysonlab.jetispot.core.util.Log
import bruhcollective.itaysonlab.swedentricks.protos.CollectionUpdate
import com.spotify.playlist4.Playlist4ApiProto
-import kotlinx.coroutines.*
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.SupervisorJob
+import kotlinx.coroutines.asCoroutineDispatcher
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
import xyz.gianlu.librespot.common.Utils
import xyz.gianlu.librespot.dealer.DealerClient
import java.util.concurrent.Executors
@@ -87,6 +91,15 @@ class SpCollectionManager @Inject constructor(
writer.performRemove(id, set)
}
+ suspend fun getRootlistImage(
+ uri: String
+ ) = dao.getRootlistImage(uri)
+
+ suspend fun clearRootlist() {
+ dao.deleteRootList()
+ dao.deleteCollectionCategory("rootlist")
+ }
+
override fun onMessage(p0: String, p1: MutableMap
, p2: ByteArray) {
if (p0.startsWith("hm://playlist/v2/user/")) {
writer.pubsubUpdateRootlist(Playlist4ApiProto.PlaylistModificationInfo.parseFrom(p2))
diff --git a/app/src/main/java/bruhcollective/itaysonlab/jetispot/core/collection/SpCollectionWriter.kt b/app/src/main/java/bruhcollective/itaysonlab/jetispot/core/collection/SpCollectionWriter.kt
index 79e24cd..d51fa2c 100644
--- a/app/src/main/java/bruhcollective/itaysonlab/jetispot/core/collection/SpCollectionWriter.kt
+++ b/app/src/main/java/bruhcollective/itaysonlab/jetispot/core/collection/SpCollectionWriter.kt
@@ -1,29 +1,41 @@
package bruhcollective.itaysonlab.jetispot.core.collection
import bruhcollective.itaysonlab.jetispot.core.SpMetadataRequester
-import bruhcollective.itaysonlab.jetispot.core.util.Log
import bruhcollective.itaysonlab.jetispot.core.SpSessionManager
import bruhcollective.itaysonlab.jetispot.core.api.SpCollectionApi
import bruhcollective.itaysonlab.jetispot.core.api.SpInternalApi
import bruhcollective.itaysonlab.jetispot.core.collection.db.LocalCollectionDao
import bruhcollective.itaysonlab.jetispot.core.collection.db.LocalCollectionRepository
-import bruhcollective.itaysonlab.jetispot.core.collection.db.model2.*
+import bruhcollective.itaysonlab.jetispot.core.collection.db.model2.CollectionAlbum
+import bruhcollective.itaysonlab.jetispot.core.collection.db.model2.CollectionArtist
+import bruhcollective.itaysonlab.jetispot.core.collection.db.model2.CollectionContentFilter
+import bruhcollective.itaysonlab.jetispot.core.collection.db.model2.CollectionEpisode
+import bruhcollective.itaysonlab.jetispot.core.collection.db.model2.CollectionPinnedItem
+import bruhcollective.itaysonlab.jetispot.core.collection.db.model2.CollectionShow
+import bruhcollective.itaysonlab.jetispot.core.collection.db.model2.CollectionTrack
import bruhcollective.itaysonlab.jetispot.core.collection.db.model2.rootlist.CollectionRootlistItem
import bruhcollective.itaysonlab.jetispot.core.objs.tags.ContentFilterResponse
+import bruhcollective.itaysonlab.jetispot.core.util.Log
import bruhcollective.itaysonlab.jetispot.core.util.Revision
import bruhcollective.itaysonlab.jetispot.core.util.SpUtils
import bruhcollective.itaysonlab.swedentricks.protos.CollectionUpdate
import bruhcollective.itaysonlab.swedentricks.protos.CollectionUpdateEntry
import com.google.protobuf.ByteString
import com.spotify.collection2.v2.proto.Collection2V2
-import com.spotify.extendedmetadata.ExtendedMetadata
import com.spotify.extendedmetadata.ExtensionKindOuterClass
import com.spotify.metadata.Metadata
import com.spotify.playlist4.Playlist4ApiProto
-import kotlinx.coroutines.*
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.MainScope
+import kotlinx.coroutines.launch
import xyz.gianlu.librespot.common.Utils
-import xyz.gianlu.librespot.metadata.*
-import java.nio.charset.StandardCharsets
+import xyz.gianlu.librespot.metadata.AlbumId
+import xyz.gianlu.librespot.metadata.ArtistId
+import xyz.gianlu.librespot.metadata.EpisodeId
+import xyz.gianlu.librespot.metadata.ImageId
+import xyz.gianlu.librespot.metadata.PlaylistId
+import xyz.gianlu.librespot.metadata.ShowId
+import xyz.gianlu.librespot.metadata.TrackId
class SpCollectionWriter(
private val spSessionManager: SpSessionManager,
@@ -381,7 +393,7 @@ class SpCollectionWriter(
timestamp = pair.first.attributes.timestamp,
name = pair.second.attributes.name,
ownerUsername = pair.second.ownerUsername,
- picture = pair.second.attributes.pictureSizeList.find { it.targetName == "default" }?.url ?: "https://i.scdn.co/image/${Utils.bytesToHex(pair.second.attributes.picture).lowercase()}"
+ picture = pair.second.attributes.pictureSizeList.find { it.targetName == "default" }?.url ?: if (pair.second.attributes.picture.isEmpty) "" else "https://i.scdn.co/image/${Utils.bytesToHex(pair.second.attributes.picture).lowercase()}"
)
}.toTypedArray())
}
@@ -407,7 +419,7 @@ class SpCollectionWriter(
timestamp = pair.first.attributes.timestamp,
name = pair.second.attributes.name,
ownerUsername = pair.second.ownerUsername,
- picture = pair.second.attributes.pictureSizeList.find { it.targetName == "default" }?.url ?: "https://i.scdn.co/image/${Utils.bytesToHex(pair.second.attributes.picture).lowercase()}"
+ picture = pair.second.attributes.pictureSizeList.find { it.targetName == "default" }?.url ?: if (pair.second.attributes.picture.isEmpty) "" else "https://i.scdn.co/image/${Utils.bytesToHex(pair.second.attributes.picture).lowercase()}"
)
}.toTypedArray()
diff --git a/app/src/main/java/bruhcollective/itaysonlab/jetispot/core/collection/db/LocalCollectionDao.kt b/app/src/main/java/bruhcollective/itaysonlab/jetispot/core/collection/db/LocalCollectionDao.kt
index 12ca146..2b11805 100644
--- a/app/src/main/java/bruhcollective/itaysonlab/jetispot/core/collection/db/LocalCollectionDao.kt
+++ b/app/src/main/java/bruhcollective/itaysonlab/jetispot/core/collection/db/LocalCollectionDao.kt
@@ -1,10 +1,20 @@
package bruhcollective.itaysonlab.jetispot.core.collection.db
-import androidx.room.*
+import androidx.room.Dao
+import androidx.room.Insert
+import androidx.room.OnConflictStrategy
+import androidx.room.Query
+import androidx.room.RawQuery
import androidx.sqlite.db.SimpleSQLiteQuery
import androidx.sqlite.db.SupportSQLiteQuery
import bruhcollective.itaysonlab.jetispot.core.collection.db.model.LocalCollectionCategory
-import bruhcollective.itaysonlab.jetispot.core.collection.db.model2.*
+import bruhcollective.itaysonlab.jetispot.core.collection.db.model2.CollectionAlbum
+import bruhcollective.itaysonlab.jetispot.core.collection.db.model2.CollectionArtist
+import bruhcollective.itaysonlab.jetispot.core.collection.db.model2.CollectionContentFilter
+import bruhcollective.itaysonlab.jetispot.core.collection.db.model2.CollectionEpisode
+import bruhcollective.itaysonlab.jetispot.core.collection.db.model2.CollectionPinnedItem
+import bruhcollective.itaysonlab.jetispot.core.collection.db.model2.CollectionShow
+import bruhcollective.itaysonlab.jetispot.core.collection.db.model2.CollectionTrack
import bruhcollective.itaysonlab.jetispot.core.collection.db.model2.rootlist.CollectionRootlistItem
import kotlinx.coroutines.flow.Flow
@@ -37,6 +47,9 @@ interface LocalCollectionDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun addEpisodes(vararg items: CollectionEpisode)
+ @Query("UPDATE rootlist SET picture = :picture WHERE uri = :uri AND CASE WHEN :overwrite THEN 1 ELSE picture <> '' END")
+ suspend fun updateRootlistPicture(uri: String, picture: String, overwrite: Boolean)
+
@Query("SELECT * from lcTypes WHERE type = :of")
suspend fun getCollection(of: String): LocalCollectionCategory?
@@ -52,6 +65,9 @@ interface LocalCollectionDao {
@Query("DELETE FROM lcAlbums WHERE id IN (:ids)")
suspend fun deleteAlbums(vararg ids: String)
+ @Query("DELETE FROM lcTypes WHERE type = :of")
+ suspend fun deleteCollectionCategory(of: String)
+
@Query("SELECT * from lcArtists ORDER BY addedAt DESC")
suspend fun getArtists(): List
@@ -73,6 +89,9 @@ interface LocalCollectionDao {
@Query("SELECT * from rootlist ORDER BY timestamp DESC")
suspend fun getRootlist(): List
+ @Query("SELECT picture FROM rootlist WHERE uri = :uri")
+ suspend fun getRootlistImage(uri: String): String?
+
@Query("DELETE from lcTracks")
suspend fun deleteTracks()
diff --git a/app/src/main/java/bruhcollective/itaysonlab/jetispot/core/di/CollectionModule.kt b/app/src/main/java/bruhcollective/itaysonlab/jetispot/core/di/CollectionModule.kt
index 4e5f78a..c3bf8ec 100644
--- a/app/src/main/java/bruhcollective/itaysonlab/jetispot/core/di/CollectionModule.kt
+++ b/app/src/main/java/bruhcollective/itaysonlab/jetispot/core/di/CollectionModule.kt
@@ -2,14 +2,8 @@ package bruhcollective.itaysonlab.jetispot.core.di
import android.content.Context
import androidx.room.Room
-import bruhcollective.itaysonlab.jetispot.core.SpMetadataRequester
-import bruhcollective.itaysonlab.jetispot.core.SpSessionManager
-import bruhcollective.itaysonlab.jetispot.core.api.SpCollectionApi
-import bruhcollective.itaysonlab.jetispot.core.api.SpInternalApi
-import bruhcollective.itaysonlab.jetispot.core.collection.SpCollectionManager
import bruhcollective.itaysonlab.jetispot.core.collection.db.LocalCollectionDao
import bruhcollective.itaysonlab.jetispot.core.collection.db.LocalCollectionDatabase
-import bruhcollective.itaysonlab.jetispot.core.collection.db.LocalCollectionRepository
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
diff --git a/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/AppNavigation.kt b/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/AppNavigation.kt
index 4e4d59d..057b668 100644
--- a/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/AppNavigation.kt
+++ b/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/AppNavigation.kt
@@ -26,7 +26,6 @@ import bruhcollective.itaysonlab.jetispot.R
import bruhcollective.itaysonlab.jetispot.core.SpAuthManager
import bruhcollective.itaysonlab.jetispot.core.SpSessionManager
import bruhcollective.itaysonlab.jetispot.core.api.SpInternalApi
-import bruhcollective.itaysonlab.jetispot.core.util.Log
import bruhcollective.itaysonlab.jetispot.ui.bottomsheets.jump_to_artist.JumpToArtistBottomSheet
import bruhcollective.itaysonlab.jetispot.ui.screens.BottomSheet
import bruhcollective.itaysonlab.jetispot.ui.screens.Dialog
diff --git a/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/dac/DacDelegate.kt b/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/dac/DacDelegate.kt
index e760dc3..227d452 100644
--- a/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/dac/DacDelegate.kt
+++ b/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/dac/DacDelegate.kt
@@ -7,6 +7,11 @@ import com.spotify.dac.player.v1.proto.PlayCommand
@Stable
interface DacDelegate {
fun dispatchPlay(command: PlayCommand)
+ suspend fun updateRootlistImage(
+ uri: String,
+ image: String,
+ overwrite: Boolean
+ )
}
val LocalDacDelegate = staticCompositionLocalOf { error("DacDelegate should be initialized") }
\ No newline at end of file
diff --git a/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/dac/DacRender.kt b/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/dac/DacRender.kt
index 0686af0..fdad1bc 100644
--- a/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/dac/DacRender.kt
+++ b/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/dac/DacRender.kt
@@ -1,8 +1,6 @@
package bruhcollective.itaysonlab.jetispot.ui.dac
import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.Spacer
-import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
@@ -11,18 +9,49 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import bruhcollective.itaysonlab.jetispot.BuildConfig
import bruhcollective.itaysonlab.jetispot.proto.ErrorComponent
-import bruhcollective.itaysonlab.jetispot.ui.dac.components_home.*
-import bruhcollective.itaysonlab.jetispot.ui.dac.components_plans.*
+import bruhcollective.itaysonlab.jetispot.ui.dac.components_home.MediumActionCardBinder
+import bruhcollective.itaysonlab.jetispot.ui.dac.components_home.RecentlyPlayedSectionComponentBinder
+import bruhcollective.itaysonlab.jetispot.ui.dac.components_home.RecsplanationHeadingComponentBinder
+import bruhcollective.itaysonlab.jetispot.ui.dac.components_home.RecsplanationHeadingSingleTextComponentBinder
+import bruhcollective.itaysonlab.jetispot.ui.dac.components_home.SectionComponentBinder
+import bruhcollective.itaysonlab.jetispot.ui.dac.components_home.SectionHeaderComponentBinder
+import bruhcollective.itaysonlab.jetispot.ui.dac.components_home.ShortcutsBinder
+import bruhcollective.itaysonlab.jetispot.ui.dac.components_home.SmallActionCardBinder
+import bruhcollective.itaysonlab.jetispot.ui.dac.components_home.ToolbarComponent2Binder
+import bruhcollective.itaysonlab.jetispot.ui.dac.components_home.ToolbarComponentBinder
+import bruhcollective.itaysonlab.jetispot.ui.dac.components_plans.BenefitListComponentBinder
+import bruhcollective.itaysonlab.jetispot.ui.dac.components_plans.DisclaimerComponentBinder
+import bruhcollective.itaysonlab.jetispot.ui.dac.components_plans.FallbackPlanComponentBinder
+import bruhcollective.itaysonlab.jetispot.ui.dac.components_plans.MultiUserMemberComponentBinder
+import bruhcollective.itaysonlab.jetispot.ui.dac.components_plans.PlanComponentBinder
+import bruhcollective.itaysonlab.jetispot.ui.dac.components_plans.SingleUserComponentBinder
import com.google.protobuf.Message
import com.spotify.allplans.v1.DisclaimerComponent
import com.spotify.allplans.v1.PlanComponent
import com.spotify.home.dac.component.heading.v1.proto.RecsplanationHeadingSingleTextComponent
-import com.spotify.home.dac.component.v1.proto.*
+import com.spotify.home.dac.component.v1.proto.AlbumCardActionsMediumComponent
+import com.spotify.home.dac.component.v1.proto.AlbumCardActionsSmallComponent
+import com.spotify.home.dac.component.v1.proto.ArtistCardActionsMediumComponent
+import com.spotify.home.dac.component.v1.proto.ArtistCardActionsSmallComponent
+import com.spotify.home.dac.component.v1.proto.PlaylistCardActionsMediumComponent
+import com.spotify.home.dac.component.v1.proto.PlaylistCardActionsSmallComponent
+import com.spotify.home.dac.component.v1.proto.RecentlyPlayedSectionComponent
+import com.spotify.home.dac.component.v1.proto.RecsplanationHeadingComponent
+import com.spotify.home.dac.component.v1.proto.SectionComponent
+import com.spotify.home.dac.component.v1.proto.SectionHeaderComponent
+import com.spotify.home.dac.component.v1.proto.ShortcutsSectionComponent
+import com.spotify.home.dac.component.v1.proto.SnappyGridSectionComponent
+import com.spotify.home.dac.component.v1.proto.ToolbarComponent
import com.spotify.home.dac.component.v2.proto.ToolbarComponentV2
-import com.spotify.planoverview.v1.*
+import com.spotify.planoverview.v1.BenefitListComponent
+import com.spotify.planoverview.v1.FallbackPlanComponent
+import com.spotify.planoverview.v1.MultiUserMemberComponent
+import com.spotify.planoverview.v1.SingleUserPrepaidComponent
+import com.spotify.planoverview.v1.SingleUserRecurringComponent
+import com.spotify.planoverview.v1.SingleUserTrialComponent
@Composable
-fun DacRender (
+fun DacRender(
item: Message
) {
when (item) {
@@ -40,7 +69,7 @@ fun DacRender (
// Home
is ToolbarComponent -> ToolbarComponentBinder(item)
is ToolbarComponentV2 -> ToolbarComponent2Binder(item)
- is ShortcutsSectionComponent -> ShortcutsBinder(item)
+ is ShortcutsSectionComponent -> ShortcutsBinder(item) // e.g. small card playlist, episode, etc. on home screen
is AlbumCardActionsSmallComponent -> SmallActionCardBinder(title = item.title, subtitle = item.subtitle, navigateUri = item.navigateUri, likeUri = item.likeUri, imageUri = item.imageUri, imagePlaceholder = "album", playCommand = item.playCommand)
is ArtistCardActionsSmallComponent -> SmallActionCardBinder(title = item.title, subtitle = item.subtitle, navigateUri = item.navigateUri, likeUri = item.followUri, imageUri = item.imageUri, imagePlaceholder = "artist", playCommand = item.playCommand)
diff --git a/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/dac/components_home/SectionComponentBinder.kt b/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/dac/components_home/SectionComponentBinder.kt
index c514e80..72b559c 100644
--- a/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/dac/components_home/SectionComponentBinder.kt
+++ b/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/dac/components_home/SectionComponentBinder.kt
@@ -1,26 +1,43 @@
package bruhcollective.itaysonlab.jetispot.ui.dac.components_home
-import androidx.compose.foundation.layout.*
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.PaddingValues
+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
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.unit.dp
+import bruhcollective.itaysonlab.jetispot.ui.dac.LocalDacDelegate
import bruhcollective.itaysonlab.jetispot.ui.ext.dynamicUnpack
import bruhcollective.itaysonlab.jetispot.ui.shared.MediumText
import bruhcollective.itaysonlab.jetispot.ui.shared.PreviewableAsyncImage
import bruhcollective.itaysonlab.jetispot.ui.shared.Subtext
import bruhcollective.itaysonlab.jetispot.ui.shared.navClickable
-import com.spotify.home.dac.component.v1.proto.*
+import com.spotify.home.dac.component.v1.proto.AlbumCardMediumComponent
+import com.spotify.home.dac.component.v1.proto.ArtistCardMediumComponent
+import com.spotify.home.dac.component.v1.proto.EpisodeCardMediumComponent
+import com.spotify.home.dac.component.v1.proto.PlaylistCardMediumComponent
+import com.spotify.home.dac.component.v1.proto.SectionComponent
+import com.spotify.home.dac.component.v1.proto.ShowCardMediumComponent
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.withContext
@Composable
fun SectionComponentBinder(
item: SectionComponent
) {
+ val localDacDelegate = LocalDacDelegate.current
+
val list = item.componentsList.map { it.dynamicUnpack() }
LazyRow(contentPadding = PaddingValues(horizontal = 16.dp), horizontalArrangement = Arrangement.spacedBy(12.dp), modifier = Modifier.fillMaxWidth()) {
items(list) { listItem ->
@@ -33,13 +50,20 @@ fun SectionComponentBinder(
imagePlaceholder = "album"
)
- is PlaylistCardMediumComponent -> MediumCard(
- title = listItem.title,
- subtitle = listItem.subtitle,
- navigateUri = listItem.navigateUri,
- imageUri = listItem.imageUri,
- imagePlaceholder = "playlist"
- )
+ is PlaylistCardMediumComponent -> {
+ LaunchedEffect(Unit) {
+ withContext(Dispatchers.IO) {
+ localDacDelegate.updateRootlistImage(listItem.navigateUri, listItem.imageUri, overwrite = true)
+ }
+ }
+ MediumCard(
+ title = listItem.title,
+ subtitle = listItem.subtitle,
+ navigateUri = listItem.navigateUri,
+ imageUri = listItem.imageUri,
+ imagePlaceholder = "playlist"
+ )
+ }
is ArtistCardMediumComponent -> MediumCard(
title = listItem.title,
diff --git a/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/dac/components_home/SectionHeaderComponentBinder.kt b/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/dac/components_home/SectionHeaderComponentBinder.kt
index d46853d..5d0ac42 100644
--- a/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/dac/components_home/SectionHeaderComponentBinder.kt
+++ b/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/dac/components_home/SectionHeaderComponentBinder.kt
@@ -1,18 +1,16 @@
package bruhcollective.itaysonlab.jetispot.ui.dac.components_home
-import androidx.compose.foundation.background
import androidx.compose.foundation.layout.padding
import androidx.compose.material.Text
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
-import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
@Composable
-fun SectionHeaderComponentBinder (
+fun SectionHeaderComponentBinder(
text: String
) {
Text(text = text, color = MaterialTheme.colorScheme.onSurface, fontWeight = FontWeight.Bold, fontSize = 21.sp, modifier = Modifier.padding(16.dp))
diff --git a/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/dac/components_home/ShortcutsBinder.kt b/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/dac/components_home/ShortcutsBinder.kt
index 13493c9..345c0db 100644
--- a/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/dac/components_home/ShortcutsBinder.kt
+++ b/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/dac/components_home/ShortcutsBinder.kt
@@ -1,34 +1,53 @@
package bruhcollective.itaysonlab.jetispot.ui.dac.components_home
-import androidx.compose.foundation.layout.*
-import androidx.compose.material3.*
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.material3.Card
+import androidx.compose.material3.CardDefaults
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
+import bruhcollective.itaysonlab.jetispot.ui.dac.LocalDacDelegate
import bruhcollective.itaysonlab.jetispot.ui.ext.compositeSurfaceElevation
import bruhcollective.itaysonlab.jetispot.ui.ext.dynamicUnpack
import bruhcollective.itaysonlab.jetispot.ui.shared.PreviewableAsyncImage
import bruhcollective.itaysonlab.jetispot.ui.shared.navClickable
-import com.spotify.home.dac.component.v1.proto.*
+import com.spotify.home.dac.component.v1.proto.AlbumCardShortcutComponent
+import com.spotify.home.dac.component.v1.proto.ArtistCardShortcutComponent
+import com.spotify.home.dac.component.v1.proto.EpisodeCardShortcutComponent
+import com.spotify.home.dac.component.v1.proto.PlaylistCardShortcutComponent
+import com.spotify.home.dac.component.v1.proto.ShortcutsSectionComponent
+import com.spotify.home.dac.component.v1.proto.ShowCardShortcutComponent
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.withContext
@Composable
fun ShortcutsBinder(
item: ShortcutsSectionComponent
) {
+ val localDacDelegate = LocalDacDelegate.current
+
item.shortcutsList.map { it.dynamicUnpack() }.chunked(2).forEachIndexed { idx, pairs ->
Row(
- Modifier
- .padding(horizontal = 16.dp)
- .padding(bottom = if (idx != item.shortcutsList.lastIndex / 2) 8.dp else 0.dp)
+ Modifier
+ .padding(horizontal = 16.dp)
+ .padding(bottom = if (idx != item.shortcutsList.lastIndex / 2) 8.dp else 0.dp)
) {
pairs.forEachIndexed { xIdx, xItem ->
Box(
- Modifier
- .weight(1f)
- .padding(end = if (xIdx == 0 && pairs.size == 2) 8.dp else 0.dp)) {
+ Modifier
+ .weight(1f)
+ .padding(end = if (xIdx == 0 && pairs.size == 2) 8.dp else 0.dp)) {
when (xItem) {
is AlbumCardShortcutComponent -> ShortcutComponentBinder(
xItem.navigateUri,
@@ -36,12 +55,19 @@ fun ShortcutsBinder(
"album",
xItem.title
)
- is PlaylistCardShortcutComponent -> ShortcutComponentBinder(
- xItem.navigateUri,
- xItem.imageUri,
- "playlist",
- xItem.title
- )
+ is PlaylistCardShortcutComponent -> {
+ LaunchedEffect(Unit) {
+ withContext(Dispatchers.IO) {
+ localDacDelegate.updateRootlistImage(xItem.navigateUri, xItem.imageUri, overwrite = false)
+ }
+ }
+ ShortcutComponentBinder(
+ xItem.navigateUri,
+ xItem.imageUri,
+ "playlist",
+ xItem.title
+ )
+ }
is ShowCardShortcutComponent -> ShortcutComponentBinder(
xItem.navigateUri,
xItem.imageUri,
@@ -74,14 +100,16 @@ private fun ShortcutComponentBinder(
imagePlaceholder: String,
title: String
) {
+
+
Card(
colors = CardDefaults.cardColors(
containerColor = MaterialTheme.colorScheme.compositeSurfaceElevation(
3.dp
)
), modifier = Modifier
- .height(56.dp)
- .fillMaxWidth()
+ .height(56.dp)
+ .fillMaxWidth()
) {
Row(Modifier.navClickable { navController ->
navController.navigate(navigateUri)
@@ -98,10 +126,10 @@ private fun ShortcutComponentBinder(
maxLines = 2,
overflow = TextOverflow.Ellipsis,
modifier = Modifier
- .align(
- Alignment.CenterVertically
- )
- .padding(horizontal = 8.dp)
+ .align(
+ Alignment.CenterVertically
+ )
+ .padding(horizontal = 8.dp)
.fillMaxWidth(),
)
}
diff --git a/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/hub/HubBinder.kt b/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/hub/HubBinder.kt
index 5d4b8be..85d8bcc 100644
--- a/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/hub/HubBinder.kt
+++ b/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/hub/HubBinder.kt
@@ -42,7 +42,7 @@ import bruhcollective.itaysonlab.jetispot.ui.hub.components.TextRow
//TODO: FIX UNSUPPORTED ID IN LISTENING HISTORY - BOBBYESP
@Composable
-fun HubBinder (
+fun HubBinder(
item: HubItem,
isRenderingInGrid: Boolean = false,
) {
diff --git a/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/hub/virt/PlaylistEntityView.kt b/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/hub/virt/PlaylistEntityView.kt
index c455dad..b7d1e80 100644
--- a/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/hub/virt/PlaylistEntityView.kt
+++ b/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/hub/virt/PlaylistEntityView.kt
@@ -4,13 +4,25 @@ import android.text.format.DateUtils
import bruhcollective.itaysonlab.jetispot.core.SpMetadataRequester
import bruhcollective.itaysonlab.jetispot.core.SpSessionManager
import bruhcollective.itaysonlab.jetispot.core.api.SpInternalApi
-import bruhcollective.itaysonlab.jetispot.core.objs.hub.*
-import bruhcollective.itaysonlab.jetispot.core.objs.player.*
+import bruhcollective.itaysonlab.jetispot.core.collection.SpCollectionManager
+import bruhcollective.itaysonlab.jetispot.core.objs.hub.HubComponent
+import bruhcollective.itaysonlab.jetispot.core.objs.hub.HubEvent
+import bruhcollective.itaysonlab.jetispot.core.objs.hub.HubEvents
+import bruhcollective.itaysonlab.jetispot.core.objs.hub.HubImage
+import bruhcollective.itaysonlab.jetispot.core.objs.hub.HubImages
+import bruhcollective.itaysonlab.jetispot.core.objs.hub.HubItem
+import bruhcollective.itaysonlab.jetispot.core.objs.hub.HubResponse
+import bruhcollective.itaysonlab.jetispot.core.objs.hub.HubText
+import bruhcollective.itaysonlab.jetispot.core.objs.player.PfcContextData
+import bruhcollective.itaysonlab.jetispot.core.objs.player.PfcOptSkipTo
+import bruhcollective.itaysonlab.jetispot.core.objs.player.PfcOptions
+import bruhcollective.itaysonlab.jetispot.core.objs.player.PfcState
+import bruhcollective.itaysonlab.jetispot.core.objs.player.PfcStateOptions
+import bruhcollective.itaysonlab.jetispot.core.objs.player.PlayFromContextData
+import bruhcollective.itaysonlab.jetispot.core.objs.player.PlayFromContextPlayerData
import bruhcollective.itaysonlab.jetispot.core.tracks
import bruhcollective.itaysonlab.jetispot.core.user
-import bruhcollective.itaysonlab.jetispot.ui.screens.hub.LikedSongsViewModel
import com.google.protobuf.ByteString
-import com.google.protobuf.StringValue
import com.spotify.metadata.Metadata
import com.spotify.playlist4.Playlist4ApiProto
import kotlinx.coroutines.Dispatchers
@@ -27,7 +39,7 @@ object PlaylistEntityView {
val hubResponse: HubResponse
)
- suspend fun getPlaylistView(id: String, sessionManager: SpSessionManager, spInternalApi: SpInternalApi, spMetadataRequester: SpMetadataRequester): ApiPlaylist {
+ suspend fun getPlaylistView(id: String, sessionManager: SpSessionManager, spInternalApi: SpInternalApi, spMetadataRequester: SpMetadataRequester, spCollectionManager: SpCollectionManager): ApiPlaylist {
val playlist = withContext(Dispatchers.IO) { sessionManager.session.api().getPlaylist(PlaylistId.fromUri("spotify:playlist:$id")) }
val playlistTracks = playlist.contents.itemsList.distinctBy { it.uri }.filter { it.uri.startsWith("spotify:track:") }
val playlistOwnerUsername = "spotify:user:${playlist.ownerUsername}"
@@ -79,7 +91,8 @@ object PlaylistEntityView {
uri = playlist.attributes.formatAttributesList.find { it.key == "image" }?.value
?: playlist.attributes.formatAttributesList.find { it.key == "image_url" }?.value
?: playlist.attributes.pictureSizeList.find { it.targetName == "default" }?.url
- ?: if (playlist.attributes.hasPicture()) "https://i.scdn.co/image/${Utils.bytesToHex(playlist.attributes.picture).lowercase()}" else ""
+ ?: if (playlist.attributes.hasPicture()) "https://i.scdn.co/image/${Utils.bytesToHex(playlist.attributes.picture).lowercase()}"
+ else spCollectionManager.getRootlistImage("spotify:playlist:$id") ?: ""
)
)
)
diff --git a/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/config/StorageScreen.kt b/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/config/StorageScreen.kt
index 947a8c3..60b5e15 100644
--- a/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/config/StorageScreen.kt
+++ b/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/config/StorageScreen.kt
@@ -5,14 +5,37 @@ import android.text.format.Formatter
import androidx.annotation.StringRes
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.background
-import androidx.compose.foundation.layout.*
+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.WindowInsets
+import androidx.compose.foundation.layout.fillMaxHeight
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.offset
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.rounded.*
-import androidx.compose.material3.*
-import androidx.compose.runtime.*
+import androidx.compose.material.icons.rounded.ArrowBack
+import androidx.compose.material.icons.rounded.Cached
+import androidx.compose.material.icons.rounded.Image
+import androidx.compose.material.icons.rounded.Save
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.Icon
+import androidx.compose.material3.IconButton
+import androidx.compose.material3.LargeTopAppBar
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Scaffold
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
@@ -22,7 +45,6 @@ import androidx.compose.ui.graphics.StrokeCap
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalContext
-import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
@@ -31,6 +53,7 @@ import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.ViewModel
import bruhcollective.itaysonlab.jetispot.R
import bruhcollective.itaysonlab.jetispot.core.SpSessionManager
+import bruhcollective.itaysonlab.jetispot.core.collection.SpCollectionManager
import bruhcollective.itaysonlab.jetispot.core.metadata_db.SpMetadataDb
import bruhcollective.itaysonlab.jetispot.core.util.Device
import bruhcollective.itaysonlab.jetispot.ui.ext.blendWith
@@ -41,7 +64,11 @@ import bruhcollective.itaysonlab.jetispot.ui.shared.PagingLoadingPage
import coil.annotation.ExperimentalCoilApi
import coil.imageLoader
import dagger.hilt.android.lifecycle.HiltViewModel
-import kotlinx.coroutines.*
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
import java.io.File
import javax.inject.Inject
@@ -236,7 +263,8 @@ fun MultiStateProgressIndicator(
@HiltViewModel
class StorageViewModel @Inject constructor(
private val spSessionManager: SpSessionManager,
- private val spMetadataDb: SpMetadataDb
+ private val spMetadataDb: SpMetadataDb,
+ private val spCollectionManager: SpCollectionManager
) : ViewModel() {
val types = StorageFileKind.values()
val clearActions = ClearAction.values()
@@ -290,6 +318,10 @@ class StorageViewModel @Inject constructor(
}
ClearAction.ClearMetadata -> spMetadataDb.clear()
+
+ ClearAction.ClearRootlist -> spCollectionManager.clearRootlist()
+
+ ClearAction.ClearDb -> spCollectionManager.clean()
}
load(context)
@@ -357,6 +389,8 @@ class StorageViewModel @Inject constructor(
@StringRes val title: Int
) {
ClearCaches(R.string.storage_clear),
- ClearMetadata(R.string.storage_clear_metadata)
+ ClearMetadata(R.string.storage_clear_metadata),
+ ClearRootlist(R.string.clear_rootlist),
+ ClearDb(R.string.clear_db)
}
}
diff --git a/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/dac/DacRendererScreen.kt b/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/dac/DacRendererScreen.kt
index 176dcd6..f697d0e 100644
--- a/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/dac/DacRendererScreen.kt
+++ b/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/dac/DacRendererScreen.kt
@@ -1,12 +1,22 @@
package bruhcollective.itaysonlab.jetispot.ui.screens.dac
-import androidx.compose.foundation.ExperimentalFoundationApi
-import androidx.compose.foundation.layout.*
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.WindowInsets
+import androidx.compose.foundation.layout.fillMaxHeight
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.ArrowBack
-import androidx.compose.material3.*
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.Icon
+import androidx.compose.material3.IconButton
+import androidx.compose.material3.LargeTopAppBar
+import androidx.compose.material3.Scaffold
+import androidx.compose.material3.Text
+import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.nestedScroll
@@ -15,6 +25,7 @@ import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.ViewModel
import bruhcollective.itaysonlab.jetispot.core.SpPlayerServiceManager
import bruhcollective.itaysonlab.jetispot.core.api.SpInternalApi
+import bruhcollective.itaysonlab.jetispot.core.collection.db.LocalCollectionDao
import bruhcollective.itaysonlab.jetispot.core.util.toApplicationPlayCommand
import bruhcollective.itaysonlab.jetispot.proto.ErrorComponent
import bruhcollective.itaysonlab.jetispot.ui.dac.DacDelegate
@@ -120,6 +131,7 @@ fun DacRendererScreen(
class DacViewModel @Inject constructor(
private val spInternalApi: SpInternalApi,
private val spPlayerServiceManager: SpPlayerServiceManager,
+ private val dao: LocalCollectionDao,
private val moshi: Moshi
) : ViewModel(), DacDelegate {
var facet = "default"
@@ -165,6 +177,15 @@ class DacViewModel @Inject constructor(
load(loader)
}
+ // overwrite will update the image URI in db only if there isn't already one
+ override suspend fun updateRootlistImage(
+ uri: String,
+ image: String,
+ overwrite: Boolean
+ ) {
+ dao.updateRootlistPicture(uri, image, overwrite)
+ }
+
sealed class State {
class Loaded(val sticky: Message?, val data: List) : State()
class Error(val error: Exception) : State()
diff --git a/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/hub/HubExt.kt b/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/hub/HubExt.kt
index 773c1f1..d5864e2 100644
--- a/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/hub/HubExt.kt
+++ b/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/hub/HubExt.kt
@@ -1,12 +1,24 @@
package bruhcollective.itaysonlab.jetispot.ui.screens.hub
import androidx.compose.foundation.ExperimentalFoundationApi
-import androidx.compose.foundation.layout.*
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.WindowInsets
+import androidx.compose.foundation.layout.fillMaxHeight
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.ArrowBack
-import androidx.compose.material3.*
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.Icon
+import androidx.compose.material3.IconButton
+import androidx.compose.material3.LargeTopAppBar
+import androidx.compose.material3.MaterialTheme
+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.CompositionLocalProvider
import androidx.compose.runtime.rememberCoroutineScope
@@ -111,7 +123,6 @@ fun HubScaffold(
HubBinder(it)
}
}
-
}
}
}
diff --git a/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/hub/HubScreen.kt b/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/hub/HubScreen.kt
index db60b67..8aef788 100644
--- a/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/hub/HubScreen.kt
+++ b/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/hub/HubScreen.kt
@@ -1,6 +1,9 @@
package bruhcollective.itaysonlab.jetispot.ui.screens.hub
-import androidx.compose.foundation.layout.*
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.statusBarsPadding
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.GridItemSpan
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
@@ -20,7 +23,6 @@ import bruhcollective.itaysonlab.jetispot.core.objs.player.PlayFromContextData
import bruhcollective.itaysonlab.jetispot.ui.hub.HubBinder
import bruhcollective.itaysonlab.jetispot.ui.hub.HubScreenDelegate
import bruhcollective.itaysonlab.jetispot.ui.hub.LocalHubScreenDelegate
-import bruhcollective.itaysonlab.jetispot.ui.navigation.LocalNavigationController
import bruhcollective.itaysonlab.jetispot.ui.shared.PagingErrorPage
import bruhcollective.itaysonlab.jetispot.ui.shared.PagingLoadingPage
import dagger.hilt.android.lifecycle.HiltViewModel
@@ -37,8 +39,6 @@ fun HubScreen(
) {
val scope = rememberCoroutineScope()
- val navController = LocalNavigationController.current
-
viewModel.needContentPadding = needContentPadding
LaunchedEffect(Unit) {
diff --git a/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/hub/PlaylistScreen.kt b/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/hub/PlaylistScreen.kt
index 98ef880..bdc13e9 100644
--- a/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/hub/PlaylistScreen.kt
+++ b/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/hub/PlaylistScreen.kt
@@ -9,6 +9,7 @@ import bruhcollective.itaysonlab.jetispot.core.SpPlayerServiceManager
import bruhcollective.itaysonlab.jetispot.core.SpSessionManager
import bruhcollective.itaysonlab.jetispot.core.api.SpInternalApi
import bruhcollective.itaysonlab.jetispot.core.api.SpPartnersApi
+import bruhcollective.itaysonlab.jetispot.core.collection.SpCollectionManager
import bruhcollective.itaysonlab.jetispot.core.objs.player.PlayFromContextData
import bruhcollective.itaysonlab.jetispot.ui.hub.virt.PlaylistEntityView
import dagger.hilt.android.lifecycle.HiltViewModel
@@ -39,7 +40,8 @@ class PlaylistViewModel @Inject constructor(
private val spInternalApi: SpInternalApi,
private val spPartnersApi: SpPartnersApi,
private val spPlayerServiceManager: SpPlayerServiceManager,
- private val spMetadataRequester: SpMetadataRequester
+ private val spMetadataRequester: SpMetadataRequester,
+ private val spCollectionManager: SpCollectionManager
) : AbsHubViewModel() {
val title = mutableStateOf("")
@@ -48,7 +50,7 @@ class PlaylistViewModel @Inject constructor(
suspend fun loadPlaylist(id: String) = load { loadPlaylistInternal(id) }
suspend fun reloadPlaylist(id: String) = reload { loadPlaylistInternal(id) }
- suspend fun loadPlaylistInternal(id: String) = PlaylistEntityView.getPlaylistView(id, spSessionManager, spInternalApi, spMetadataRequester).also { _playlistMetadata.value = it; title.value = it.playlist.attributes.name; }.hubResponse
+ private suspend fun loadPlaylistInternal(id: String) = PlaylistEntityView.getPlaylistView(id, spSessionManager, spInternalApi, spMetadataRequester, spCollectionManager).also { _playlistMetadata.value = it; title.value = it.playlist.attributes.name; }.hubResponse
override fun play(data: PlayFromContextData) = play(spPlayerServiceManager, data)
override suspend fun calculateDominantColor(url: String, dark: Boolean) = calculateDominantColor(spPartnersApi, url, dark)
diff --git a/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/yourlibrary2/YlDelegate.kt b/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/yourlibrary2/YlDelegate.kt
new file mode 100644
index 0000000..ef84634
--- /dev/null
+++ b/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/yourlibrary2/YlDelegate.kt
@@ -0,0 +1,11 @@
+package bruhcollective.itaysonlab.jetispot.ui.screens.yourlibrary2
+
+import androidx.compose.runtime.Stable
+import androidx.compose.runtime.staticCompositionLocalOf
+
+@Stable
+interface YlDelegate {
+ suspend fun getDisplayName(ownerUsername: String): String
+}
+
+val LocalYlDelegate = staticCompositionLocalOf { error("YlDelegate should be initialized") }
\ No newline at end of file
diff --git a/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/yourlibrary2/YlRenderer.kt b/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/yourlibrary2/YlRenderer.kt
index c8f9795..c74e1c0 100644
--- a/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/yourlibrary2/YlRenderer.kt
+++ b/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/yourlibrary2/YlRenderer.kt
@@ -15,6 +15,11 @@ import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
@@ -121,12 +126,19 @@ fun YLRRootlist(
modifier: Modifier
) {
if (item.name.isEmpty()) return
+ val localYlDelegate = LocalYlDelegate.current
+ var playlistOwner by remember { mutableStateOf(item.ownerUsername) }
+
+ LaunchedEffect(Unit) {
+ playlistOwner = localYlDelegate.getDisplayName(item.ownerUsername)
+ }
+
YLRGenericItem(
picUrl = item.picture,
picCircle = false,
picPlaceholder = "playlist",
title = item.name,
- subtitle = item.ownerUsername,
+ subtitle = playlistOwner,
modifier = modifier
)
}
diff --git a/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/yourlibrary2/YourLibraryContainerScreen.kt b/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/yourlibrary2/YourLibraryContainerScreen.kt
index da0a53f..89dc3e2 100644
--- a/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/yourlibrary2/YourLibraryContainerScreen.kt
+++ b/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/yourlibrary2/YourLibraryContainerScreen.kt
@@ -2,7 +2,13 @@ package bruhcollective.itaysonlab.jetispot.ui.screens.yourlibrary2
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.clickable
-import androidx.compose.foundation.layout.*
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.WindowInsets
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.foundation.lazy.items
@@ -11,18 +17,31 @@ import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.AccountCircle
import androidx.compose.material.icons.rounded.Check
import androidx.compose.material.icons.rounded.Search
-import androidx.compose.material3.*
-import androidx.compose.runtime.*
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.FilterChip
+import androidx.compose.material3.Icon
+import androidx.compose.material3.IconButton
+import androidx.compose.material3.Scaffold
+import androidx.compose.material3.Text
+import androidx.compose.material3.TopAppBar
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.ViewModel
import bruhcollective.itaysonlab.jetispot.R
-import bruhcollective.itaysonlab.jetispot.core.api.SpInternalApi
+import bruhcollective.itaysonlab.jetispot.core.SpMetadataRequester
import bruhcollective.itaysonlab.jetispot.core.collection.db.LocalCollectionDao
import bruhcollective.itaysonlab.jetispot.core.collection.db.model2.CollectionEntry
import bruhcollective.itaysonlab.jetispot.core.collection.db.model2.PredefCeType
+import bruhcollective.itaysonlab.jetispot.core.user
import bruhcollective.itaysonlab.jetispot.ui.navigation.LocalNavigationController
import bruhcollective.itaysonlab.jetispot.ui.shared.PagingLoadingPage
import dagger.hilt.android.lifecycle.HiltViewModel
@@ -93,11 +112,13 @@ fun YourLibraryContainerScreen(
viewModel.content,
key = { it.javaClass.simpleName + "_" + it.ceId() },
contentType = { it.javaClass.simpleName }) { item ->
- YlRenderer(item, modifier = Modifier
- .clickable { navController.navigate(item.ceUri()) }
- .fillMaxWidth()
- .padding(horizontal = 16.dp, vertical = 8.dp)
- .animateItemPlacement())
+ CompositionLocalProvider(LocalYlDelegate provides viewModel) {
+ YlRenderer(item, modifier = Modifier
+ .clickable { navController.navigate(item.ceUri()) }
+ .fillMaxWidth()
+ .padding(horizontal = 16.dp, vertical = 8.dp)
+ .animateItemPlacement())
+ }
}
}
} else {
@@ -141,11 +162,16 @@ fun AnimatedChipRow(
@HiltViewModel
class YourLibraryContainerViewModel @Inject constructor(
- private val dao: LocalCollectionDao
-) : ViewModel() {
+ private val dao: LocalCollectionDao,
+ private val spMetadataRequester: SpMetadataRequester
+) : ViewModel(), YlDelegate {
var selectedTabId: String by mutableStateOf("")
var content by mutableStateOf>(emptyList())
+ override suspend fun getDisplayName(ownerUsername: String) = spMetadataRequester.request {
+ user("spotify:user:$ownerUsername")
+ }.userProfiles["spotify:user:$ownerUsername"]?.name?.value ?: ownerUsername
+
suspend fun load() {
val type = when (selectedTabId) {
"playlists" -> FetchType.Playlists
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index c2ff85b..7797eda 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -128,6 +128,8 @@
Actions
Clear application cache
Clear content metadata
+ Clear rootlist (playlists)
+ Clear database
An error occurred while loading the page.
Copy details
From 3b3fce65582429447616580b6631814dccc27510 Mon Sep 17 00:00:00 2001
From: Outlet7493 <122801553+Outlet7493@users.noreply.github.com>
Date: Wed, 17 Jan 2024 21:10:50 +0000
Subject: [PATCH 08/17] flip equality sign on updaterootlist image
---
.../jetispot/core/collection/db/LocalCollectionDao.kt | 2 +-
.../itaysonlab/jetispot/ui/screens/dac/DacRendererScreen.kt | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/app/src/main/java/bruhcollective/itaysonlab/jetispot/core/collection/db/LocalCollectionDao.kt b/app/src/main/java/bruhcollective/itaysonlab/jetispot/core/collection/db/LocalCollectionDao.kt
index 2b11805..984e98c 100644
--- a/app/src/main/java/bruhcollective/itaysonlab/jetispot/core/collection/db/LocalCollectionDao.kt
+++ b/app/src/main/java/bruhcollective/itaysonlab/jetispot/core/collection/db/LocalCollectionDao.kt
@@ -47,7 +47,7 @@ interface LocalCollectionDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun addEpisodes(vararg items: CollectionEpisode)
- @Query("UPDATE rootlist SET picture = :picture WHERE uri = :uri AND CASE WHEN :overwrite THEN 1 ELSE picture <> '' END")
+ @Query("UPDATE rootlist SET picture = :picture WHERE uri = :uri AND CASE WHEN :overwrite THEN 1 ELSE picture == '' END")
suspend fun updateRootlistPicture(uri: String, picture: String, overwrite: Boolean)
@Query("SELECT * from lcTypes WHERE type = :of")
diff --git a/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/dac/DacRendererScreen.kt b/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/dac/DacRendererScreen.kt
index f697d0e..0080445 100644
--- a/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/dac/DacRendererScreen.kt
+++ b/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/dac/DacRendererScreen.kt
@@ -177,7 +177,7 @@ class DacViewModel @Inject constructor(
load(loader)
}
- // overwrite will update the image URI in db only if there isn't already one
+ // overwrite will update picture regardless if it already exists (e.g. in the case we get a bigger picture, store that instead)
override suspend fun updateRootlistImage(
uri: String,
image: String,
From 4c6a532034f18c2e552f34465be497bea01ca3fb Mon Sep 17 00:00:00 2001
From: Outlet7493 <122801553+Outlet7493@users.noreply.github.com>
Date: Wed, 17 Jan 2024 21:53:03 +0000
Subject: [PATCH 09/17] match library header to home screen header with profile
picture
---
.../ToolbarComponent2Binder.kt | 1 -
.../YourLibraryContainerScreen.kt | 46 +++++++++++++------
app/src/main/res/values/strings.xml | 2 +-
3 files changed, 34 insertions(+), 15 deletions(-)
diff --git a/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/dac/components_home/ToolbarComponent2Binder.kt b/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/dac/components_home/ToolbarComponent2Binder.kt
index 31136b9..83440e8 100644
--- a/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/dac/components_home/ToolbarComponent2Binder.kt
+++ b/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/dac/components_home/ToolbarComponent2Binder.kt
@@ -19,7 +19,6 @@ import androidx.compose.ui.unit.dp
import bruhcollective.itaysonlab.jetispot.ui.ext.dynamicUnpack
import bruhcollective.itaysonlab.jetispot.ui.navigation.LocalNavigationController
import bruhcollective.itaysonlab.jetispot.ui.shared.PreviewableAsyncImage
-import com.spotify.home.dac.component.v1.proto.ToolbarComponent
import com.spotify.home.dac.component.v1.proto.ToolbarItemFeedComponent
import com.spotify.home.dac.component.v1.proto.ToolbarItemListeningHistoryComponent
import com.spotify.home.dac.component.v1.proto.ToolbarItemSettingsComponent
diff --git a/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/yourlibrary2/YourLibraryContainerScreen.kt b/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/yourlibrary2/YourLibraryContainerScreen.kt
index 89dc3e2..dc250a9 100644
--- a/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/yourlibrary2/YourLibraryContainerScreen.kt
+++ b/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/yourlibrary2/YourLibraryContainerScreen.kt
@@ -9,14 +9,14 @@ import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState
+import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.rounded.AccountCircle
import androidx.compose.material.icons.rounded.Check
-import androidx.compose.material.icons.rounded.Search
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.FilterChip
import androidx.compose.material3.Icon
@@ -32,18 +32,22 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.ViewModel
import bruhcollective.itaysonlab.jetispot.R
import bruhcollective.itaysonlab.jetispot.core.SpMetadataRequester
+import bruhcollective.itaysonlab.jetispot.core.SpSessionManager
import bruhcollective.itaysonlab.jetispot.core.collection.db.LocalCollectionDao
import bruhcollective.itaysonlab.jetispot.core.collection.db.model2.CollectionEntry
import bruhcollective.itaysonlab.jetispot.core.collection.db.model2.PredefCeType
import bruhcollective.itaysonlab.jetispot.core.user
import bruhcollective.itaysonlab.jetispot.ui.navigation.LocalNavigationController
import bruhcollective.itaysonlab.jetispot.ui.shared.PagingLoadingPage
+import bruhcollective.itaysonlab.jetispot.ui.shared.PreviewableAsyncImage
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
@@ -69,17 +73,23 @@ fun YourLibraryContainerScreen(
Scaffold(topBar = {
Column {
- TopAppBar(title = {
- Text(stringResource(id = R.string.your_library))
- }, navigationIcon = {
- IconButton(onClick = { /* TODO */ }) {
- Icon(Icons.Rounded.AccountCircle, null)
- }
- }, actions = {
- IconButton(onClick = { /* TODO */ }) {
- Icon(Icons.Rounded.Search, null)
+ TopAppBar(
+ title = {
+ Text(stringResource(id = R.string.your_library), fontWeight = FontWeight.SemiBold)
+ },
+ navigationIcon = {
+ IconButton(
+ onClick = { navController.navigate("spotify:config") },
+ modifier = Modifier.padding(start = 8.dp, end = 8.dp)
+ ) {
+ PreviewableAsyncImage(imageUrl = viewModel.profilePicture, placeholderType = "user", modifier = Modifier.size(36.dp).clip(CircleShape))
+ }
+ }, actions = {
+// IconButton(onClick = { /* TODO */ }) {
+// Icon(Icons.Rounded.Search, null)
+// }
}
- })
+ )
AnimatedChipRow(
listOf(
@@ -163,16 +173,19 @@ fun AnimatedChipRow(
@HiltViewModel
class YourLibraryContainerViewModel @Inject constructor(
private val dao: LocalCollectionDao,
- private val spMetadataRequester: SpMetadataRequester
+ private val spMetadataRequester: SpMetadataRequester,
+ private val spSessionManager: SpSessionManager
) : ViewModel(), YlDelegate {
var selectedTabId: String by mutableStateOf("")
var content by mutableStateOf>(emptyList())
+ var profilePicture by mutableStateOf("")
override suspend fun getDisplayName(ownerUsername: String) = spMetadataRequester.request {
user("spotify:user:$ownerUsername")
}.userProfiles["spotify:user:$ownerUsername"]?.name?.value ?: ownerUsername
suspend fun load() {
+ getProfilePicture()
val type = when (selectedTabId) {
"playlists" -> FetchType.Playlists
"albums" -> FetchType.Albums
@@ -215,6 +228,13 @@ class YourLibraryContainerViewModel @Inject constructor(
})
}
+ private suspend fun getProfilePicture() {
+ val u = "spotify:user:${spSessionManager.session.username()}"
+ profilePicture = spMetadataRequester.request {
+ user(u)
+ }.userProfiles[u]?.imagesList?.firstOrNull()?.url ?: ""
+ }
+
enum class FetchType {
All,
Playlists,
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 7797eda..91781e5 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -158,7 +158,7 @@
Unknown Title
Unknown Artist
Unknown Album
- What would you like to listen?
+ What would you like to listen to?
"Song • "
Playlist • Personalized for you
Playlist • By Spotify
From 9d640421d827d017a10f0f6137cdea28d0afc535 Mon Sep 17 00:00:00 2001
From: Outlet7493 <122801553+Outlet7493@users.noreply.github.com>
Date: Fri, 19 Jan 2024 19:42:26 +0000
Subject: [PATCH 10/17] impl search feature for your library
---
.../YourLibraryContainerScreen.kt | 218 ++++++++++++++++--
app/src/main/res/values/strings.xml | 1 +
2 files changed, 198 insertions(+), 21 deletions(-)
diff --git a/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/yourlibrary2/YourLibraryContainerScreen.kt b/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/yourlibrary2/YourLibraryContainerScreen.kt
index dc250a9..7808d75 100644
--- a/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/yourlibrary2/YourLibraryContainerScreen.kt
+++ b/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/yourlibrary2/YourLibraryContainerScreen.kt
@@ -1,8 +1,17 @@
package bruhcollective.itaysonlab.jetispot.ui.screens.yourlibrary2
+import androidx.compose.animation.AnimatedContent
+import androidx.compose.animation.SizeTransform
+import androidx.compose.animation.fadeIn
+import androidx.compose.animation.fadeOut
+import androidx.compose.animation.slideInHorizontally
+import androidx.compose.animation.slideOutHorizontally
+import androidx.compose.animation.togetherWith
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.clickable
+import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.WindowInsets
@@ -15,41 +24,69 @@ import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.foundation.text.BasicTextField
+import androidx.compose.foundation.text.KeyboardActions
+import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.rounded.ArrowBack
import androidx.compose.material.icons.rounded.Check
+import androidx.compose.material.icons.rounded.Close
+import androidx.compose.material.icons.rounded.Search
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.FilterChip
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.OutlinedTextFieldDefaults
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
+import androidx.compose.material3.surfaceColorAtElevation
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
+import androidx.compose.ui.graphics.SolidColor
+import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.text.input.ImeAction
+import androidx.compose.ui.text.input.VisualTransformation
import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
import bruhcollective.itaysonlab.jetispot.R
import bruhcollective.itaysonlab.jetispot.core.SpMetadataRequester
import bruhcollective.itaysonlab.jetispot.core.SpSessionManager
import bruhcollective.itaysonlab.jetispot.core.collection.db.LocalCollectionDao
+import bruhcollective.itaysonlab.jetispot.core.collection.db.model2.CollectionAlbum
+import bruhcollective.itaysonlab.jetispot.core.collection.db.model2.CollectionArtist
import bruhcollective.itaysonlab.jetispot.core.collection.db.model2.CollectionEntry
+import bruhcollective.itaysonlab.jetispot.core.collection.db.model2.CollectionEpisode
+import bruhcollective.itaysonlab.jetispot.core.collection.db.model2.CollectionPinnedItem
+import bruhcollective.itaysonlab.jetispot.core.collection.db.model2.CollectionShow
import bruhcollective.itaysonlab.jetispot.core.collection.db.model2.PredefCeType
+import bruhcollective.itaysonlab.jetispot.core.collection.db.model2.rootlist.CollectionRootlistItem
import bruhcollective.itaysonlab.jetispot.core.user
import bruhcollective.itaysonlab.jetispot.ui.navigation.LocalNavigationController
import bruhcollective.itaysonlab.jetispot.ui.shared.PagingLoadingPage
import bruhcollective.itaysonlab.jetispot.ui.shared.PreviewableAsyncImage
import dagger.hilt.android.lifecycle.HiltViewModel
+import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.delay
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.collectLatest
+import kotlinx.coroutines.flow.debounce
import kotlinx.coroutines.launch
import javax.inject.Inject
@@ -62,9 +99,13 @@ fun YourLibraryContainerScreen(
viewModel: YourLibraryContainerViewModel = hiltViewModel()
) {
val navController = LocalNavigationController.current
+ val focusManager = LocalFocusManager.current
val scope = rememberCoroutineScope()
val state = rememberLazyListState()
+ var search by remember { mutableStateOf(false) }
+ var query by remember { mutableStateOf("") }
+
LaunchedEffect(Unit) {
launch {
viewModel.load()
@@ -75,19 +116,104 @@ fun YourLibraryContainerScreen(
Column {
TopAppBar(
title = {
- Text(stringResource(id = R.string.your_library), fontWeight = FontWeight.SemiBold)
+ val containerColor = MaterialTheme.colorScheme.surfaceColorAtElevation(8.dp)
+ AnimatedContent(
+ search,
+ transitionSpec = {
+ if (targetState) {
+ (slideInHorizontally { it } + fadeIn()).togetherWith(slideOutHorizontally { -it } + fadeOut())
+ } else {
+ (slideInHorizontally { -it } + fadeIn()).togetherWith(slideOutHorizontally { it } + fadeOut())
+ }.using(SizeTransform(clip = false))
+ },
+ label = "Your library search bar slide"
+ ) {
+ if (it) {
+ BasicTextField(
+ value = query,
+ onValueChange = {
+ query = it
+ viewModel.filter(it)
+ },
+ modifier = Modifier.fillMaxWidth(),
+ singleLine = true,
+ textStyle = TextStyle(
+ fontSize = 14.sp,
+ color = MaterialTheme.colorScheme.onSurface
+ ),
+ cursorBrush = SolidColor(MaterialTheme.colorScheme.onSurface),
+ keyboardOptions = KeyboardOptions(
+ imeAction = ImeAction.Search
+ ),
+ keyboardActions = KeyboardActions {
+ focusManager.clearFocus()
+ }
+ ) { inner ->
+ OutlinedTextFieldDefaults.DecorationBox(
+ value = query,
+ innerTextField = inner,
+ enabled = true,
+ singleLine = true,
+ visualTransformation = VisualTransformation.None,
+ interactionSource = remember { MutableInteractionSource() },
+ trailingIcon = {
+ if (query.isNotEmpty()) {
+ IconButton(onClick = { query = "" }) {
+ Icon(imageVector = Icons.Rounded.Close, contentDescription = null)
+ }
+ }
+ },
+ placeholder = {
+ Text(stringResource(R.string.search_your_library))
+ },
+ colors = OutlinedTextFieldDefaults.colors(
+ focusedContainerColor = containerColor,
+ unfocusedContainerColor = containerColor,
+ disabledContainerColor = containerColor,
+ unfocusedBorderColor = MaterialTheme.colorScheme.surfaceVariant,
+ ),
+ contentPadding = OutlinedTextFieldDefaults.contentPadding()
+ )
+ }
+ } else {
+ Text(stringResource(id = R.string.your_library), fontWeight = FontWeight.SemiBold)
+ }
+ }
},
navigationIcon = {
- IconButton(
- onClick = { navController.navigate("spotify:config") },
- modifier = Modifier.padding(start = 8.dp, end = 8.dp)
+ AnimatedContent(
+ search,
+ transitionSpec = {
+ if (targetState) {
+ (slideInHorizontally { it } + fadeIn()).togetherWith(slideOutHorizontally { -it } + fadeOut())
+ } else {
+ (slideInHorizontally { -it } + fadeIn()).togetherWith(slideOutHorizontally { it } + fadeOut())
+ }.using(SizeTransform(clip = false))
+ },
+ label = "Your library nav icon slide"
) {
- PreviewableAsyncImage(imageUrl = viewModel.profilePicture, placeholderType = "user", modifier = Modifier.size(36.dp).clip(CircleShape))
+ if (it) {
+ IconButton(onClick = { search = false }) {
+ Icon(Icons.Rounded.ArrowBack, null)
+ }
+ } else {
+ IconButton(
+ onClick = { navController.navigate("spotify:config") },
+ modifier = Modifier.padding(start = 8.dp, end = 8.dp)
+ ) {
+ PreviewableAsyncImage(imageUrl = viewModel.profilePicture, placeholderType = "user", modifier = Modifier
+ .size(36.dp)
+ .clip(CircleShape))
+ }
+ }
+ }
+ },
+ actions = {
+ if (!search) {
+ IconButton(onClick = { search = true }) {
+ Icon(Icons.Rounded.Search, null)
+ }
}
- }, actions = {
-// IconButton(onClick = { /* TODO */ }) {
-// Icon(Icons.Rounded.Search, null)
-// }
}
)
@@ -111,23 +237,37 @@ fun YourLibraryContainerScreen(
}
}
}, contentWindowInsets = WindowInsets(bottom = 0.dp)) { padding ->
- if (viewModel.content.isNotEmpty()) {
+ if (viewModel.loaded) {
LazyColumn(
state = state,
modifier = Modifier
.padding(padding)
.fillMaxSize()
) {
- items(
- viewModel.content,
- key = { it.javaClass.simpleName + "_" + it.ceId() },
- contentType = { it.javaClass.simpleName }) { item ->
- CompositionLocalProvider(LocalYlDelegate provides viewModel) {
- YlRenderer(item, modifier = Modifier
- .clickable { navController.navigate(item.ceUri()) }
- .fillMaxWidth()
- .padding(horizontal = 16.dp, vertical = 8.dp)
- .animateItemPlacement())
+ if (viewModel.filteredContent.isNotEmpty()) {
+ items(
+ viewModel.filteredContent,
+ key = { it.javaClass.simpleName + "_" + it.ceId() },
+ contentType = { it.javaClass.simpleName }) { item ->
+ CompositionLocalProvider(LocalYlDelegate provides viewModel) {
+ YlRenderer(item, modifier = Modifier
+ .clickable { navController.navigate(item.ceUri()) }
+ .fillMaxWidth()
+ .padding(horizontal = 16.dp, vertical = 8.dp)
+ .animateItemPlacement())
+ }
+ }
+ } else {
+ item("NoSearchResult") {
+ Box(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(top = 16.dp)
+ .animateItemPlacement(),
+ contentAlignment = Alignment.Center
+ ) {
+ Text(stringResource(R.string.search_no_results))
+ }
}
}
}
@@ -170,16 +310,46 @@ fun AnimatedChipRow(
}
}
+@OptIn(FlowPreview::class)
@HiltViewModel
class YourLibraryContainerViewModel @Inject constructor(
private val dao: LocalCollectionDao,
private val spMetadataRequester: SpMetadataRequester,
private val spSessionManager: SpSessionManager
) : ViewModel(), YlDelegate {
+ private var content by mutableStateOf>(emptyList())
+ private val debouncedSearch = MutableStateFlow("")
+ var loaded by mutableStateOf(false)
var selectedTabId: String by mutableStateOf("")
- var content by mutableStateOf>(emptyList())
+ var filteredContent by mutableStateOf>(emptyList())
var profilePicture by mutableStateOf("")
+ init {
+ viewModelScope.launch {
+ debouncedSearch
+ .debounce(200L)
+ .collectLatest { search ->
+ if (content.isNotEmpty()) {
+ filteredContent = if (search.isEmpty()) {
+ content
+ } else {
+ content.filter { ce ->
+ when (ce) {
+ is CollectionRootlistItem -> ce.name.contains(search, ignoreCase = true)
+ is CollectionArtist -> ce.name.contains(search, ignoreCase = true)
+ is CollectionPinnedItem -> ce.name.contains(search, ignoreCase = true)
+ is CollectionEpisode -> ce.name.contains(search, ignoreCase = true) || ce.showName.contains(search, ignoreCase = true)
+ is CollectionAlbum -> ce.name.contains(search, ignoreCase = true) || ce.rawArtistsData.contains(search, ignoreCase = true)
+ is CollectionShow -> ce.name.contains(search, ignoreCase = true) || ce.publisher.contains(search, ignoreCase = true)
+ else -> ce.ceId().contains(search, ignoreCase = true)
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
override suspend fun getDisplayName(ownerUsername: String) = spMetadataRequester.request {
user("spotify:user:$ownerUsername")
}.userProfiles["spotify:user:$ownerUsername"]?.name?.value ?: ownerUsername
@@ -226,6 +396,12 @@ class YourLibraryContainerViewModel @Inject constructor(
}
}
})
+ filteredContent = content
+ loaded = true
+ }
+
+ fun filter(search: String) {
+ debouncedSearch.value = search
}
private suspend fun getProfilePicture() {
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 91781e5..1e4ea3d 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -159,6 +159,7 @@
Unknown Artist
Unknown Album
What would you like to listen to?
+ Search your library
"Song • "
Playlist • Personalized for you
Playlist • By Spotify
From 6bfd0a3229d3b8fdca6dfaba84e772d9e6f10803 Mon Sep 17 00:00:00 2001
From: Outlet7493 <122801553+Outlet7493@users.noreply.github.com>
Date: Sat, 20 Jan 2024 14:52:25 +0000
Subject: [PATCH 11/17] add your episodes; add rootlist impl when pinned;
promocardbinder init
---
app/build.gradle.kts | 1 +
.../core/collection/SpCollectionManager.kt | 6 ++
.../collection/db/model2/CollectionEntry.kt | 2 +-
.../playback/service/SpPlaybackService.kt | 4 +-
.../playback/service/refl/SpReflect.kt | 1 -
.../itaysonlab/jetispot/ui/dac/DacRender.kt | 5 +-
.../components_home/MediumActionCardBinder.kt | 17 +++-
.../ui/dac/components_home/PromoCardBinder.kt | 89 +++++++++++++++++++
.../ui/hub/virt/PlaylistEntityView.kt | 16 ++--
.../ui/screens/dynamic/DynamicSpIdScreen.kt | 11 ++-
.../ui/screens/hub/YourEpisodesScreen.kt | 13 +++
.../ui/screens/yourlibrary2/YlDelegate.kt | 1 +
.../ui/screens/yourlibrary2/YlRenderer.kt | 68 ++++++++++----
.../YourLibraryContainerScreen.kt | 12 ++-
.../jetispot/ui/shared/NavController.kt | 4 +-
app/src/main/res/values/strings.xml | 2 +
16 files changed, 218 insertions(+), 34 deletions(-)
create mode 100644 app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/dac/components_home/PromoCardBinder.kt
create mode 100644 app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/hub/YourEpisodesScreen.kt
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index 1aafeba..c06e490 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -62,6 +62,7 @@ android {
}.toString()
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
+ testInstrumentationRunnerArguments["disableAnalytics"] = "true"
kapt {
correctErrorTypes = true
diff --git a/app/src/main/java/bruhcollective/itaysonlab/jetispot/core/collection/SpCollectionManager.kt b/app/src/main/java/bruhcollective/itaysonlab/jetispot/core/collection/SpCollectionManager.kt
index 348e1b0..f4ca9aa 100644
--- a/app/src/main/java/bruhcollective/itaysonlab/jetispot/core/collection/SpCollectionManager.kt
+++ b/app/src/main/java/bruhcollective/itaysonlab/jetispot/core/collection/SpCollectionManager.kt
@@ -95,6 +95,12 @@ class SpCollectionManager @Inject constructor(
uri: String
) = dao.getRootlistImage(uri)
+ suspend fun updateRootlistImage(
+ uri: String,
+ image: String,
+ overwrite: Boolean
+ ) = dao.updateRootlistPicture(uri, image, overwrite)
+
suspend fun clearRootlist() {
dao.deleteRootList()
dao.deleteCollectionCategory("rootlist")
diff --git a/app/src/main/java/bruhcollective/itaysonlab/jetispot/core/collection/db/model2/CollectionEntry.kt b/app/src/main/java/bruhcollective/itaysonlab/jetispot/core/collection/db/model2/CollectionEntry.kt
index f7c035e..0acbc97 100644
--- a/app/src/main/java/bruhcollective/itaysonlab/jetispot/core/collection/db/model2/CollectionEntry.kt
+++ b/app/src/main/java/bruhcollective/itaysonlab/jetispot/core/collection/db/model2/CollectionEntry.kt
@@ -8,5 +8,5 @@ interface CollectionEntry {
}
enum class PredefCeType {
- COLLECTION, EPISODES
+ COLLECTION, EPISODES, YOUR_EPISODES, ROOTLIST
}
\ No newline at end of file
diff --git a/app/src/main/java/bruhcollective/itaysonlab/jetispot/playback/service/SpPlaybackService.kt b/app/src/main/java/bruhcollective/itaysonlab/jetispot/playback/service/SpPlaybackService.kt
index 2b74f45..31d41e7 100644
--- a/app/src/main/java/bruhcollective/itaysonlab/jetispot/playback/service/SpPlaybackService.kt
+++ b/app/src/main/java/bruhcollective/itaysonlab/jetispot/playback/service/SpPlaybackService.kt
@@ -4,9 +4,7 @@ import android.annotation.SuppressLint
import android.app.PendingIntent
import android.content.Intent
import android.net.Uri
-import android.os.Build
import android.os.Bundle
-import androidx.media2.session.LibraryResult
import androidx.media2.session.MediaLibraryService
import androidx.media2.session.MediaSession
import androidx.media2.session.SessionResult
@@ -72,7 +70,7 @@ class SpPlaybackService : MediaLibraryService(), CoroutineScope by CoroutineScop
Intent(this@SpPlaybackService, MainActivity::class.java).apply {
putExtra("openPlayer", true)
},
- (if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) PendingIntent.FLAG_IMMUTABLE else 0) or PendingIntent.FLAG_UPDATE_CURRENT
+ (PendingIntent.FLAG_IMMUTABLE) or PendingIntent.FLAG_UPDATE_CURRENT
)
)
}.build()
diff --git a/app/src/main/java/bruhcollective/itaysonlab/jetispot/playback/service/refl/SpReflect.kt b/app/src/main/java/bruhcollective/itaysonlab/jetispot/playback/service/refl/SpReflect.kt
index 838691e..26b62b3 100644
--- a/app/src/main/java/bruhcollective/itaysonlab/jetispot/playback/service/refl/SpReflect.kt
+++ b/app/src/main/java/bruhcollective/itaysonlab/jetispot/playback/service/refl/SpReflect.kt
@@ -2,7 +2,6 @@ package bruhcollective.itaysonlab.jetispot.playback.service.refl
import androidx.media2.common.SessionPlayer
import com.google.gson.JsonParser
-import xyz.gianlu.librespot.player.PagesLoader
import xyz.gianlu.librespot.player.Player
class SpReflect(
diff --git a/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/dac/DacRender.kt b/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/dac/DacRender.kt
index fdad1bc..81455b3 100644
--- a/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/dac/DacRender.kt
+++ b/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/dac/DacRender.kt
@@ -10,6 +10,7 @@ import androidx.compose.ui.unit.dp
import bruhcollective.itaysonlab.jetispot.BuildConfig
import bruhcollective.itaysonlab.jetispot.proto.ErrorComponent
import bruhcollective.itaysonlab.jetispot.ui.dac.components_home.MediumActionCardBinder
+import bruhcollective.itaysonlab.jetispot.ui.dac.components_home.PromoCardBinder
import bruhcollective.itaysonlab.jetispot.ui.dac.components_home.RecentlyPlayedSectionComponentBinder
import bruhcollective.itaysonlab.jetispot.ui.dac.components_home.RecsplanationHeadingComponentBinder
import bruhcollective.itaysonlab.jetispot.ui.dac.components_home.RecsplanationHeadingSingleTextComponentBinder
@@ -35,6 +36,7 @@ import com.spotify.home.dac.component.v1.proto.ArtistCardActionsMediumComponent
import com.spotify.home.dac.component.v1.proto.ArtistCardActionsSmallComponent
import com.spotify.home.dac.component.v1.proto.PlaylistCardActionsMediumComponent
import com.spotify.home.dac.component.v1.proto.PlaylistCardActionsSmallComponent
+import com.spotify.home.dac.component.v1.proto.PromoCardHomeComponent
import com.spotify.home.dac.component.v1.proto.RecentlyPlayedSectionComponent
import com.spotify.home.dac.component.v1.proto.RecsplanationHeadingComponent
import com.spotify.home.dac.component.v1.proto.SectionComponent
@@ -55,7 +57,6 @@ fun DacRender(
item: Message
) {
when (item) {
-
// AllPlans / PlanOverview
is MultiUserMemberComponent -> MultiUserMemberComponentBinder(item)
is BenefitListComponent -> BenefitListComponentBinder(item)
@@ -85,6 +86,8 @@ fun DacRender(
is SectionComponent -> SectionComponentBinder(item)
is RecentlyPlayedSectionComponent -> RecentlyPlayedSectionComponentBinder()
+ is PromoCardHomeComponent -> PromoCardBinder(item)
+
//Podcasts
//EpisodeCardActionsMediumComponent ->
diff --git a/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/dac/components_home/MediumActionCardBinder.kt b/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/dac/components_home/MediumActionCardBinder.kt
index a68a007..7d55d7f 100644
--- a/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/dac/components_home/MediumActionCardBinder.kt
+++ b/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/dac/components_home/MediumActionCardBinder.kt
@@ -1,11 +1,24 @@
package bruhcollective.itaysonlab.jetispot.ui.dac.components_home
import androidx.compose.foundation.isSystemInDarkTheme
-import androidx.compose.foundation.layout.*
+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.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.width
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.MaterialTheme
-import androidx.compose.runtime.*
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
diff --git a/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/dac/components_home/PromoCardBinder.kt b/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/dac/components_home/PromoCardBinder.kt
new file mode 100644
index 0000000..d011ddd
--- /dev/null
+++ b/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/dac/components_home/PromoCardBinder.kt
@@ -0,0 +1,89 @@
+package bruhcollective.itaysonlab.jetispot.ui.dac.components_home
+
+import androidx.compose.foundation.isSystemInDarkTheme
+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.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.width
+import androidx.compose.material3.Card
+import androidx.compose.material3.CardDefaults
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.dp
+import bruhcollective.itaysonlab.jetispot.ui.ext.compositeSurfaceElevation
+import bruhcollective.itaysonlab.jetispot.ui.monet.ColorToScheme
+import bruhcollective.itaysonlab.jetispot.ui.shared.MediumText
+import bruhcollective.itaysonlab.jetispot.ui.shared.PreviewableAsyncImage
+import bruhcollective.itaysonlab.jetispot.ui.shared.Subtext
+import bruhcollective.itaysonlab.jetispot.ui.shared.dynamic_blocks.DynamicPlayButton
+import bruhcollective.itaysonlab.jetispot.ui.shared.navClickable
+import com.spotify.home.dac.component.v1.proto.PromoCardHomeComponent
+
+@Composable
+fun PromoCardBinder(
+ item: PromoCardHomeComponent
+) {
+ val imagePlaceholder = item.navigateUri.split(":").getOrNull(1) ?: "playlist"
+
+ val curScheme = MaterialTheme.colorScheme
+ val isDark = isSystemInDarkTheme()
+ var colorScheme by remember { mutableStateOf(curScheme) }
+
+ LaunchedEffect(item.gradientColor) {
+ val clr = android.graphics.Color.parseColor("#${item.gradientColor}")
+ colorScheme = ColorToScheme.convert(clr, isDark)
+ }
+
+ MaterialTheme(colorScheme = colorScheme) {
+ Card(colors = CardDefaults.cardColors(
+ containerColor = MaterialTheme.colorScheme.compositeSurfaceElevation(
+ 3.dp
+ )
+ ), modifier = Modifier
+ .padding(horizontal = 16.dp)
+ .fillMaxWidth()
+ .navClickable { navController ->
+ navController.navigate(item.navigateUri)
+ }) {
+ Column(Modifier.padding(16.dp)) {
+ Row {
+ PreviewableAsyncImage(
+ imageUrl = item.logoImageUri, placeholderType = imagePlaceholder, modifier = Modifier
+ .fillMaxHeight()
+ .size(140.dp)
+ )
+
+ Spacer(modifier = Modifier.width(8.dp))
+
+ Column {
+ MediumText(text = item.title, maxLines = 2)
+ Spacer(modifier = Modifier.height(8.dp))
+ Subtext(text = item.subtitle)
+ }
+ }
+
+ Spacer(modifier = Modifier.height(8.dp))
+
+ Row(verticalAlignment = Alignment.CenterVertically) {
+ Spacer(Modifier.weight(1f))
+ DynamicPlayButton(
+ command = item.playCommand,
+ Modifier.size(42.dp)
+ )
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/hub/virt/PlaylistEntityView.kt b/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/hub/virt/PlaylistEntityView.kt
index b7d1e80..599d7e7 100644
--- a/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/hub/virt/PlaylistEntityView.kt
+++ b/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/hub/virt/PlaylistEntityView.kt
@@ -49,6 +49,16 @@ object PlaylistEntityView {
tracks(playlistTracks.map { it.uri })
}
+ var imageUri = playlist.attributes.formatAttributesList.find { it.key == "image" }?.value
+ ?: playlist.attributes.formatAttributesList.find { it.key == "image_url" }?.value
+ ?: playlist.attributes.pictureSizeList.find { it.targetName == "default" }?.url
+ ?: if (playlist.attributes.hasPicture()) "https://i.scdn.co/image/${Utils.bytesToHex(playlist.attributes.picture).lowercase()}" else ""
+ if (imageUri.isEmpty()) {
+ imageUri = spCollectionManager.getRootlistImage("spotify:playlist:$id") ?: ""
+ } else {
+ spCollectionManager.updateRootlistImage("spotify:playlist:$id", imageUri, overwrite = true)
+ }
+
val mappedDuration = mappedMetadata.tracks.map { it.value.duration / 1000L }.sum()
val playlistOwner = mappedMetadata.userProfiles[playlistOwnerUsername]!!
val popCount = spInternalApi.getPlaylistPopCount(id)
@@ -88,11 +98,7 @@ object PlaylistEntityView {
),
images = HubImages(
HubImage(
- uri = playlist.attributes.formatAttributesList.find { it.key == "image" }?.value
- ?: playlist.attributes.formatAttributesList.find { it.key == "image_url" }?.value
- ?: playlist.attributes.pictureSizeList.find { it.targetName == "default" }?.url
- ?: if (playlist.attributes.hasPicture()) "https://i.scdn.co/image/${Utils.bytesToHex(playlist.attributes.picture).lowercase()}"
- else spCollectionManager.getRootlistImage("spotify:playlist:$id") ?: ""
+ uri = imageUri
)
)
)
diff --git a/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/dynamic/DynamicSpIdScreen.kt b/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/dynamic/DynamicSpIdScreen.kt
index 8c9644a..172c37d 100644
--- a/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/dynamic/DynamicSpIdScreen.kt
+++ b/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/dynamic/DynamicSpIdScreen.kt
@@ -10,7 +10,15 @@ import androidx.compose.ui.Modifier
import bruhcollective.itaysonlab.jetispot.ui.screens.blend.BlendCreateInvitationScreen
import bruhcollective.itaysonlab.jetispot.ui.screens.config.ConfigScreen
import bruhcollective.itaysonlab.jetispot.ui.screens.history.ListeningHistoryScreen
-import bruhcollective.itaysonlab.jetispot.ui.screens.hub.*
+import bruhcollective.itaysonlab.jetispot.ui.screens.hub.AlbumScreen
+import bruhcollective.itaysonlab.jetispot.ui.screens.hub.BrowseRadioScreen
+import bruhcollective.itaysonlab.jetispot.ui.screens.hub.BrowseScreen
+import bruhcollective.itaysonlab.jetispot.ui.screens.hub.CollectionScreen
+import bruhcollective.itaysonlab.jetispot.ui.screens.hub.HubScreen
+import bruhcollective.itaysonlab.jetispot.ui.screens.hub.LikedSongsScreen
+import bruhcollective.itaysonlab.jetispot.ui.screens.hub.PlaylistScreen
+import bruhcollective.itaysonlab.jetispot.ui.screens.hub.PodcastShowScreen
+import bruhcollective.itaysonlab.jetispot.ui.screens.hub.YourEpisodesScreen
@Composable
fun DynamicSpIdScreen(
@@ -47,6 +55,7 @@ fun DynamicSpIdScreen(
id = argument,
fullUri = fullUri
)
+ "your-episodes" -> YourEpisodesScreen()
"" -> CollectionScreen()
/* else -> { TODO } */
}
diff --git a/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/hub/YourEpisodesScreen.kt b/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/hub/YourEpisodesScreen.kt
new file mode 100644
index 0000000..3039ebc
--- /dev/null
+++ b/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/hub/YourEpisodesScreen.kt
@@ -0,0 +1,13 @@
+package bruhcollective.itaysonlab.jetispot.ui.screens.hub
+
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.res.stringResource
+import bruhcollective.itaysonlab.jetispot.R
+
+@Composable
+fun YourEpisodesScreen(
+
+) {
+ Text(stringResource(R.string.your_episodes))
+}
\ No newline at end of file
diff --git a/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/yourlibrary2/YlDelegate.kt b/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/yourlibrary2/YlDelegate.kt
index ef84634..eeab826 100644
--- a/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/yourlibrary2/YlDelegate.kt
+++ b/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/yourlibrary2/YlDelegate.kt
@@ -6,6 +6,7 @@ import androidx.compose.runtime.staticCompositionLocalOf
@Stable
interface YlDelegate {
suspend fun getDisplayName(ownerUsername: String): String
+ suspend fun getPinnedRootlistPicture(uri: String): String?
}
val LocalYlDelegate = staticCompositionLocalOf { error("YlDelegate should be initialized") }
\ No newline at end of file
diff --git a/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/yourlibrary2/YlRenderer.kt b/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/yourlibrary2/YlRenderer.kt
index c74e1c0..9702ed4 100644
--- a/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/yourlibrary2/YlRenderer.kt
+++ b/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/yourlibrary2/YlRenderer.kt
@@ -7,8 +7,10 @@ import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.rounded.Bookmark
import androidx.compose.material.icons.rounded.Favorite
import androidx.compose.material.icons.rounded.Photo
+import androidx.compose.material.icons.rounded.PlaylistPlay
import androidx.compose.material.icons.rounded.Podcasts
import androidx.compose.material.icons.rounded.PushPin
import androidx.compose.material3.Icon
@@ -58,12 +60,29 @@ fun YLRPinned(
item: CollectionPinnedItem,
modifier: Modifier
) {
+ if (item.predefType == PredefCeType.COLLECTION && item.predefDyn.toIntOrNull() == 0) return // don't display "liked songs" if the rootlist is empty
+
+ val ylDelegate = LocalYlDelegate.current
+ var ownerUsername by remember { mutableStateOf(item.subtitle) }
+
+ // if user has pinned a playlist, fetch display name instead of uuid
+ LaunchedEffect(Unit) {
+ if (item.predefType == PredefCeType.ROOTLIST) {
+ ownerUsername = ylDelegate.getDisplayName(item.subtitle)
+ }
+ }
+
Row(modifier) {
val isPredef = item.predefType != null
- if (isPredef) {
+ if (isPredef && item.predefType != PredefCeType.ROOTLIST) {
ImagePreview(
- if (item.predefType == PredefCeType.COLLECTION) Icons.Rounded.Favorite else Icons.Rounded.Podcasts,
+ when (item.predefType) {
+ PredefCeType.COLLECTION -> Icons.Rounded.Favorite
+ PredefCeType.EPISODES -> Icons.Rounded.Podcasts
+ PredefCeType.YOUR_EPISODES -> Icons.Rounded.Bookmark
+ PredefCeType.ROOTLIST, null -> Icons.Rounded.PlaylistPlay
+ },
true,
modifier = Modifier
.size(64.dp)
@@ -71,13 +90,29 @@ fun YLRPinned(
)
} else {
if (item.picture.isEmpty()) {
- ImagePreview(
- Icons.Rounded.Photo,
- false,
- modifier = Modifier
- .size(64.dp)
- .clip(RoundedCornerShape(8.dp))
- )
+ var pic by remember { mutableStateOf(null) }
+
+ LaunchedEffect(Unit) {
+ pic = ylDelegate.getPinnedRootlistPicture(item.ceUri())
+ }
+
+ if (pic == null) {
+ ImagePreview(
+ Icons.Rounded.Photo,
+ false,
+ modifier = Modifier
+ .size(64.dp)
+ .clip(RoundedCornerShape(8.dp))
+ )
+ } else {
+ AsyncImage(
+ model = pic,
+ contentDescription = null,
+ modifier = Modifier
+ .size(64.dp)
+ .clip(RoundedCornerShape(8.dp))
+ )
+ }
} else {
AsyncImage(
model = "https://i.scdn.co/image/${item.picture}",
@@ -94,9 +129,10 @@ fun YLRPinned(
.padding(start = 16.dp)
.align(Alignment.CenterVertically)) {
Text(text = when (item.predefType) {
- PredefCeType.COLLECTION -> stringResource(id = R.string.liked_songs)
- PredefCeType.EPISODES -> stringResource(id = R.string.new_episodes)
- null -> item.name
+ PredefCeType.COLLECTION -> stringResource(R.string.liked_songs)
+ PredefCeType.EPISODES -> stringResource(R.string.new_episodes)
+ PredefCeType.YOUR_EPISODES -> stringResource(R.string.your_episodes)
+ else -> item.name
}, maxLines = 1, overflow = TextOverflow.Ellipsis)
Row(Modifier.padding(top = 4.dp)) {
Icon(Icons.Rounded.PushPin, tint = MaterialTheme.colorScheme.primary, contentDescription = null, modifier = Modifier
@@ -104,9 +140,11 @@ fun YLRPinned(
.align(Alignment.CenterVertically))
Text(
text = when (item.predefType) {
- PredefCeType.COLLECTION -> stringResource(id = R.string.liked_songs_desc, item.predefDyn)
- PredefCeType.EPISODES -> stringResource(id = R.string.new_episodes_desc, item.predefDyn)
- null -> item.subtitle
+ PredefCeType.COLLECTION -> stringResource(R.string.liked_songs_desc, item.predefDyn)
+ PredefCeType.EPISODES -> stringResource(R.string.new_episodes_desc, item.predefDyn)
+ PredefCeType.YOUR_EPISODES -> stringResource(R.string.saved_episodes)
+ PredefCeType.ROOTLIST -> ownerUsername
+ else -> item.subtitle
},
color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.7f),
maxLines = 1,
diff --git a/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/yourlibrary2/YourLibraryContainerScreen.kt b/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/yourlibrary2/YourLibraryContainerScreen.kt
index 7808d75..8b62226 100644
--- a/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/yourlibrary2/YourLibraryContainerScreen.kt
+++ b/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/yourlibrary2/YourLibraryContainerScreen.kt
@@ -131,9 +131,9 @@ fun YourLibraryContainerScreen(
if (it) {
BasicTextField(
value = query,
- onValueChange = {
- query = it
- viewModel.filter(it)
+ onValueChange = { q ->
+ query = q
+ viewModel.filter(q)
},
modifier = Modifier.fillMaxWidth(),
singleLine = true,
@@ -354,6 +354,10 @@ class YourLibraryContainerViewModel @Inject constructor(
user("spotify:user:$ownerUsername")
}.userProfiles["spotify:user:$ownerUsername"]?.name?.value ?: ownerUsername
+ override suspend fun getPinnedRootlistPicture(uri: String): String? {
+ return dao.getRootlistImage(uri)
+ }
+
suspend fun load() {
getProfilePicture()
val type = when (selectedTabId) {
@@ -393,8 +397,10 @@ class YourLibraryContainerViewModel @Inject constructor(
when (pF.ceUri()) {
"spotify:collection" -> pF.ceModifyPredef(PredefCeType.COLLECTION, dao.trackCount().toString())
"spotify:collection:podcasts:episodes" -> pF.ceModifyPredef(PredefCeType.EPISODES, "")
+ "spotify:collection:your-episodes" -> pF.ceModifyPredef(PredefCeType.YOUR_EPISODES, "")
}
}
+ it.filter { p -> p.ceUri().startsWith("spotify:playlist:") }.map { pF -> pF.ceModifyPredef(PredefCeType.ROOTLIST, "") }
})
filteredContent = content
loaded = true
diff --git a/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/shared/NavController.kt b/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/shared/NavController.kt
index 4b796cf..c7c0b6f 100644
--- a/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/shared/NavController.kt
+++ b/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/shared/NavController.kt
@@ -24,7 +24,7 @@ fun Modifier.navClickable(
interactionSource = remember { MutableInteractionSource() },
) {
onClick(navController)
- }
+ }.then(this)
}
fun Modifier.navAndHubClickable(
@@ -41,5 +41,5 @@ fun Modifier.navAndHubClickable(
interactionSource = remember { MutableInteractionSource() },
) {
onClick(navController, hubScreenDelegate)
- }
+ }.then(this)
}
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 1e4ea3d..cf974a4 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -93,8 +93,10 @@
Liked Songs
New Episodes
+ Your Episodes
%s songs
Last updated on %s
+ Saved episodes
Sort by
Recently added
From f616f14875ca02d428d94deb575e6504824046dd Mon Sep 17 00:00:00 2001
From: Outlet7493 <122801553+Outlet7493@users.noreply.github.com>
Date: Sat, 20 Jan 2024 14:55:02 +0000
Subject: [PATCH 12/17] remove duplicates in playlist (e.g. daylist is pinned &
unpinned)
---
.../ui/screens/yourlibrary2/YourLibraryContainerScreen.kt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/yourlibrary2/YourLibraryContainerScreen.kt b/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/yourlibrary2/YourLibraryContainerScreen.kt
index 8b62226..0cb4a85 100644
--- a/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/yourlibrary2/YourLibraryContainerScreen.kt
+++ b/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/yourlibrary2/YourLibraryContainerScreen.kt
@@ -401,7 +401,7 @@ class YourLibraryContainerViewModel @Inject constructor(
}
}
it.filter { p -> p.ceUri().startsWith("spotify:playlist:") }.map { pF -> pF.ceModifyPredef(PredefCeType.ROOTLIST, "") }
- })
+ }).distinctBy { it.ceUri() }
filteredContent = content
loaded = true
}
From ad579d6481af1de0328224f55151ad0c77138613 Mon Sep 17 00:00:00 2001
From: Outlet7493 <122801553+Outlet7493@users.noreply.github.com>
Date: Sun, 21 Jan 2024 20:13:26 +0000
Subject: [PATCH 13/17] fix crash
---
.../jetispot/core/util/PlayCommandFactory.kt | 11 ++++++++---
1 file changed, 8 insertions(+), 3 deletions(-)
diff --git a/app/src/main/java/bruhcollective/itaysonlab/jetispot/core/util/PlayCommandFactory.kt b/app/src/main/java/bruhcollective/itaysonlab/jetispot/core/util/PlayCommandFactory.kt
index 9a21f6d..05ae4d2 100644
--- a/app/src/main/java/bruhcollective/itaysonlab/jetispot/core/util/PlayCommandFactory.kt
+++ b/app/src/main/java/bruhcollective/itaysonlab/jetispot/core/util/PlayCommandFactory.kt
@@ -1,9 +1,13 @@
package bruhcollective.itaysonlab.jetispot.core.util
-import bruhcollective.itaysonlab.jetispot.core.objs.player.*
+import bruhcollective.itaysonlab.jetispot.core.objs.player.PfcContextData
+import bruhcollective.itaysonlab.jetispot.core.objs.player.PfcOptSkipTo
+import bruhcollective.itaysonlab.jetispot.core.objs.player.PfcOptions
+import bruhcollective.itaysonlab.jetispot.core.objs.player.PfcStateOptions
+import bruhcollective.itaysonlab.jetispot.core.objs.player.PlayFromContextData
+import bruhcollective.itaysonlab.jetispot.core.objs.player.PlayFromContextPlayerData
import com.spotify.dac.player.v1.proto.PlayCommand
import com.squareup.moshi.Moshi
-import com.squareup.moshi.adapter
@DslMarker
@Target(AnnotationTarget.TYPE, AnnotationTarget.CLASS)
@@ -39,8 +43,9 @@ class PlayCommandBuilder (
}
fun PlayCommand.toApplicationPlayCommand(moshi: Moshi): PlayFromContextData {
+ // TODO needs investigation when coming from e.g. 'new release from' on DAC
val context = moshi.adapter(PfcContextData::class.java).fromJson(this.context.toStringUtf8())!!
- val options = moshi.adapter(PlayFromContextPlayerData::class.java).fromJson(this.options.toStringUtf8())!!.options!!
+ val options = moshi.adapter(PlayFromContextPlayerData::class.java).fromJson(this.options.toStringUtf8())?.options
return PlayFromContextData(
uri = context.uri,
From 453b6bfead65bbe4adc0ae1ecc2fba56a9a86e92 Mon Sep 17 00:00:00 2001
From: Outlet7493 <122801553+Outlet7493@users.noreply.github.com>
Date: Sun, 21 Jan 2024 20:54:58 +0000
Subject: [PATCH 14/17] fix BobbyESP/Jetispot#9
---
.../itaysonlab/jetispot/MainActivity.kt | 6 +++--
.../jetispot/core/SpPlayerManager.kt | 2 +-
.../jetispot/core/ext/ModifierExt.kt | 11 ++++++++++
.../jetispot/core/util/PlayCommandFactory.kt | 2 +-
.../jetispot/playback/sp/AndroidSinkOutput.kt | 1 -
.../ToolbarComponent2Binder.kt | 10 +++++++--
.../ui/screens/hub/LikedSongsScreen.kt | 15 +++++++++++--
.../nowplaying/NowPlayingMiniplayer.kt | 13 +++++++++--
.../ui/screens/nowplaying/NowPlayingScreen.kt | 3 +++
.../NowPlayingFullscreenComposition.kt | 22 ++++++++++++-------
.../nowplaying/fullscreen/NowPlayingHeader.kt | 20 +++++------------
.../YourLibraryContainerScreen.kt | 12 +++++++---
12 files changed, 81 insertions(+), 36 deletions(-)
create mode 100644 app/src/main/java/bruhcollective/itaysonlab/jetispot/core/ext/ModifierExt.kt
diff --git a/app/src/main/java/bruhcollective/itaysonlab/jetispot/MainActivity.kt b/app/src/main/java/bruhcollective/itaysonlab/jetispot/MainActivity.kt
index a7c3f94..e1b2ee8 100644
--- a/app/src/main/java/bruhcollective/itaysonlab/jetispot/MainActivity.kt
+++ b/app/src/main/java/bruhcollective/itaysonlab/jetispot/MainActivity.kt
@@ -18,6 +18,7 @@ import androidx.compose.material.rememberBottomSheetScaffoldState
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.dp
@@ -25,7 +26,6 @@ import androidx.core.view.WindowCompat
import androidx.navigation.NavController
import androidx.navigation.NavDestination.Companion.hierarchy
import androidx.navigation.compose.currentBackStackEntryAsState
-import androidx.navigation.compose.rememberNavController
import bruhcollective.itaysonlab.jetispot.core.SpAuthManager
import bruhcollective.itaysonlab.jetispot.core.SpPlayerServiceManager
import bruhcollective.itaysonlab.jetispot.core.SpSessionManager
@@ -178,6 +178,7 @@ class MainActivity : ComponentActivity() {
)
},
label = { Text(stringResource(screen.title)) },
+ alwaysShowLabel = false,
selected = currentDestination?.hierarchy?.any { it.route == screen.route } == true,
onClick = {
navController.navigate(screen.route) {
@@ -193,7 +194,7 @@ class MainActivity : ComponentActivity() {
}
}
}
- ) { innerPadding ->
+ ) { _ ->
BottomSheetScaffold(
sheetContent = {
NowPlayingScreen(
@@ -208,6 +209,7 @@ class MainActivity : ComponentActivity() {
scaffoldState = bsState,
sheetPeekHeight = bsPeek,
backgroundColor = MaterialTheme.colorScheme.surface,
+ sheetBackgroundColor = Color.Transparent,
sheetGesturesEnabled = !bsQueueOpened && !bsLyricsOpened
) { innerScaffoldPadding ->
AppNavigation(
diff --git a/app/src/main/java/bruhcollective/itaysonlab/jetispot/core/SpPlayerManager.kt b/app/src/main/java/bruhcollective/itaysonlab/jetispot/core/SpPlayerManager.kt
index f5d49d1..f3d8bb8 100644
--- a/app/src/main/java/bruhcollective/itaysonlab/jetispot/core/SpPlayerManager.kt
+++ b/app/src/main/java/bruhcollective/itaysonlab/jetispot/core/SpPlayerManager.kt
@@ -1,10 +1,10 @@
package bruhcollective.itaysonlab.jetispot.core
import android.os.Looper
-import bruhcollective.itaysonlab.jetispot.proto.AudioNormalization
import bruhcollective.itaysonlab.jetispot.playback.service.refl.SpReflect
import bruhcollective.itaysonlab.jetispot.playback.sp.AndroidSinkOutput
import bruhcollective.itaysonlab.jetispot.playback.sp.LowToHighQualityPicker
+import bruhcollective.itaysonlab.jetispot.proto.AudioNormalization
import xyz.gianlu.librespot.audio.decoders.AudioQuality
import xyz.gianlu.librespot.player.Player
import xyz.gianlu.librespot.player.PlayerConfiguration
diff --git a/app/src/main/java/bruhcollective/itaysonlab/jetispot/core/ext/ModifierExt.kt b/app/src/main/java/bruhcollective/itaysonlab/jetispot/core/ext/ModifierExt.kt
new file mode 100644
index 0000000..d0a968b
--- /dev/null
+++ b/app/src/main/java/bruhcollective/itaysonlab/jetispot/core/ext/ModifierExt.kt
@@ -0,0 +1,11 @@
+package bruhcollective.itaysonlab.jetispot.core.ext
+
+import androidx.compose.ui.Modifier
+
+fun Modifier.conditional(condition : Boolean, modifier : Modifier.() -> Modifier) : Modifier {
+ return if (condition) {
+ then(modifier(Modifier))
+ } else {
+ this
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/bruhcollective/itaysonlab/jetispot/core/util/PlayCommandFactory.kt b/app/src/main/java/bruhcollective/itaysonlab/jetispot/core/util/PlayCommandFactory.kt
index 05ae4d2..27c041a 100644
--- a/app/src/main/java/bruhcollective/itaysonlab/jetispot/core/util/PlayCommandFactory.kt
+++ b/app/src/main/java/bruhcollective/itaysonlab/jetispot/core/util/PlayCommandFactory.kt
@@ -43,7 +43,7 @@ class PlayCommandBuilder (
}
fun PlayCommand.toApplicationPlayCommand(moshi: Moshi): PlayFromContextData {
- // TODO needs investigation when coming from e.g. 'new release from' on DAC
+ // TODO playing needs investigation when coming from album i.e. 'new release from' on DAC
val context = moshi.adapter(PfcContextData::class.java).fromJson(this.context.toStringUtf8())!!
val options = moshi.adapter(PlayFromContextPlayerData::class.java).fromJson(this.options.toStringUtf8())?.options
diff --git a/app/src/main/java/bruhcollective/itaysonlab/jetispot/playback/sp/AndroidSinkOutput.kt b/app/src/main/java/bruhcollective/itaysonlab/jetispot/playback/sp/AndroidSinkOutput.kt
index 81bd7e8..7103499 100644
--- a/app/src/main/java/bruhcollective/itaysonlab/jetispot/playback/sp/AndroidSinkOutput.kt
+++ b/app/src/main/java/bruhcollective/itaysonlab/jetispot/playback/sp/AndroidSinkOutput.kt
@@ -12,7 +12,6 @@ import xyz.gianlu.librespot.player.mixing.output.OutputAudioFormat
import xyz.gianlu.librespot.player.mixing.output.SinkException
import xyz.gianlu.librespot.player.mixing.output.SinkOutput
-@RequiresApi(Build.VERSION_CODES.M)
class AndroidSinkOutput: SinkOutput {
private var track: AudioTrack? = null
private var lastVolume = -1F
diff --git a/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/dac/components_home/ToolbarComponent2Binder.kt b/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/dac/components_home/ToolbarComponent2Binder.kt
index 83440e8..0d09116 100644
--- a/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/dac/components_home/ToolbarComponent2Binder.kt
+++ b/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/dac/components_home/ToolbarComponent2Binder.kt
@@ -1,5 +1,6 @@
package bruhcollective.itaysonlab.jetispot.ui.dac.components_home
+import androidx.compose.foundation.border
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
@@ -9,7 +10,12 @@ import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.History
import androidx.compose.material.icons.rounded.Notifications
import androidx.compose.material.icons.rounded.Settings
-import androidx.compose.material3.*
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.Icon
+import androidx.compose.material3.IconButton
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
+import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
@@ -44,7 +50,7 @@ fun ToolbarComponent2Binder(
IconButton(onClick = {
navController.navigate("spotify:config") // TODO until we implement user pages
}, modifier = Modifier.padding(start = 8.dp, end = 6.dp)) {
- PreviewableAsyncImage(imageUrl = item.profileButton.imageUri, placeholderType = "user", modifier = Modifier.size(36.dp).clip(CircleShape))
+ PreviewableAsyncImage(imageUrl = item.profileButton.imageUri, placeholderType = "user", modifier = Modifier.size(36.dp).border(1.dp, MaterialTheme.colorScheme.onSurface, CircleShape).clip(CircleShape))
}
})
}
diff --git a/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/hub/LikedSongsScreen.kt b/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/hub/LikedSongsScreen.kt
index 2317395..6839b38 100644
--- a/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/hub/LikedSongsScreen.kt
+++ b/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/hub/LikedSongsScreen.kt
@@ -9,8 +9,19 @@ import androidx.hilt.navigation.compose.hiltViewModel
import bruhcollective.itaysonlab.jetispot.R
import bruhcollective.itaysonlab.jetispot.core.SpPlayerServiceManager
import bruhcollective.itaysonlab.jetispot.core.collection.SpCollectionManager
-import bruhcollective.itaysonlab.jetispot.core.objs.hub.*
-import bruhcollective.itaysonlab.jetispot.core.objs.player.*
+import bruhcollective.itaysonlab.jetispot.core.objs.hub.HubComponent
+import bruhcollective.itaysonlab.jetispot.core.objs.hub.HubEvent
+import bruhcollective.itaysonlab.jetispot.core.objs.hub.HubEvents
+import bruhcollective.itaysonlab.jetispot.core.objs.hub.HubImage
+import bruhcollective.itaysonlab.jetispot.core.objs.hub.HubImages
+import bruhcollective.itaysonlab.jetispot.core.objs.hub.HubItem
+import bruhcollective.itaysonlab.jetispot.core.objs.hub.HubResponse
+import bruhcollective.itaysonlab.jetispot.core.objs.hub.HubText
+import bruhcollective.itaysonlab.jetispot.core.objs.player.PfcContextData
+import bruhcollective.itaysonlab.jetispot.core.objs.player.PfcOptSkipTo
+import bruhcollective.itaysonlab.jetispot.core.objs.player.PfcOptions
+import bruhcollective.itaysonlab.jetispot.core.objs.player.PlayFromContextData
+import bruhcollective.itaysonlab.jetispot.core.objs.player.PlayFromContextPlayerData
import dagger.hilt.android.lifecycle.HiltViewModel
import xyz.gianlu.librespot.metadata.ArtistId
import javax.inject.Inject
diff --git a/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/nowplaying/NowPlayingMiniplayer.kt b/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/nowplaying/NowPlayingMiniplayer.kt
index b794a41..b3642e2 100644
--- a/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/nowplaying/NowPlayingMiniplayer.kt
+++ b/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/nowplaying/NowPlayingMiniplayer.kt
@@ -4,13 +4,22 @@ import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.foundation.clickable
-import androidx.compose.foundation.layout.*
+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.fillMaxHeight
+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.size
+import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.LinearProgressIndicator
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
-import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
diff --git a/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/nowplaying/NowPlayingScreen.kt b/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/nowplaying/NowPlayingScreen.kt
index 7faa250..ba4f50b 100644
--- a/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/nowplaying/NowPlayingScreen.kt
+++ b/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/nowplaying/NowPlayingScreen.kt
@@ -6,6 +6,7 @@ import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.BottomSheetState
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.runtime.Composable
@@ -13,6 +14,7 @@ import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
+import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
@@ -50,6 +52,7 @@ fun NowPlayingScreen(
NowPlayingMiniplayer(
viewModel,
Modifier
+ .clip(RoundedCornerShape(16.dp, 16.dp, 0.dp, 0.dp))
.alpha(1f - bsOffset())
.clickable { scope.launch { bottomSheetState.expand() } }
.fillMaxWidth()
diff --git a/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/nowplaying/fullscreen/NowPlayingFullscreenComposition.kt b/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/nowplaying/fullscreen/NowPlayingFullscreenComposition.kt
index c67bd74..2911629 100644
--- a/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/nowplaying/fullscreen/NowPlayingFullscreenComposition.kt
+++ b/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/nowplaying/fullscreen/NowPlayingFullscreenComposition.kt
@@ -6,7 +6,15 @@ import androidx.compose.animation.core.spring
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.foundation.background
-import androidx.compose.foundation.layout.*
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxHeight
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.navigationBarsPadding
+import androidx.compose.foundation.layout.offset
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.statusBarsPadding
+import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.BottomSheetState
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material3.MaterialTheme
@@ -16,6 +24,7 @@ import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
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
import androidx.compose.ui.unit.IntOffset
@@ -23,7 +32,6 @@ import androidx.compose.ui.unit.dp
import bruhcollective.itaysonlab.jetispot.ui.ext.compositeSurfaceElevation
import bruhcollective.itaysonlab.jetispot.ui.ext.disableTouch
import bruhcollective.itaysonlab.jetispot.ui.screens.nowplaying.NowPlayingViewModel
-import bruhcollective.itaysonlab.jetispot.ui.theme.ApplicationTheme
import kotlinx.coroutines.launch
@OptIn(ExperimentalMaterialApi::class)
@@ -60,12 +68,13 @@ fun NowPlayingFullscreenComposition(
}
Box(modifier = Modifier
.fillMaxSize()
+ .clip(RoundedCornerShape(16.dp, 16.dp, 0.dp, 0.dp))
.background(
MaterialTheme.colorScheme.compositeSurfaceElevation(
3.dp
)
- )) {
-
+ )
+ ) {
NowPlayingBackground(
viewModel = viewModel,
modifier = Modifier
@@ -92,10 +101,7 @@ fun NowPlayingFullscreenComposition(
.statusBarsPadding()
.align(Alignment.TopCenter)
.fillMaxWidth()
- .padding(horizontal = 16.dp),
- viewModel = viewModel,
- bottomSheetState = bottomSheetState,
- scope = scope,
+ .padding(horizontal = 16.dp)
)
}
// composite
diff --git a/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/nowplaying/fullscreen/NowPlayingHeader.kt b/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/nowplaying/fullscreen/NowPlayingHeader.kt
index 9cfc20d..cfecb7d 100644
--- a/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/nowplaying/fullscreen/NowPlayingHeader.kt
+++ b/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/nowplaying/fullscreen/NowPlayingHeader.kt
@@ -1,13 +1,14 @@
package bruhcollective.itaysonlab.jetispot.ui.screens.nowplaying.fullscreen
-import androidx.compose.foundation.layout.*
-import androidx.compose.foundation.shape.CircleShape
-import androidx.compose.material.BottomSheetState
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.Close
import androidx.compose.material.icons.rounded.KeyboardArrowDown
-import androidx.compose.material.icons.rounded.MoreVert
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.Text
@@ -15,18 +16,12 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
-import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.scale
-import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
-import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
-import bruhcollective.itaysonlab.jetispot.ui.screens.nowplaying.NowPlayingViewModel
-import bruhcollective.itaysonlab.jetispot.ui.shared.navClickable
-import kotlinx.coroutines.CoroutineScope
@OptIn(ExperimentalMaterialApi::class)
@Composable
@@ -35,10 +30,7 @@ fun NowPlayingHeader(
state: String,
queueStateProgress: Float,
onCloseClick: () -> Unit,
- modifier: Modifier,
- viewModel: NowPlayingViewModel,
- bottomSheetState: BottomSheetState,
- scope: CoroutineScope
+ modifier: Modifier
) {
Row(modifier, verticalAlignment = Alignment.CenterVertically) {
IconButton(onClick = onCloseClick, Modifier.size(32.dp)) {
diff --git a/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/yourlibrary2/YourLibraryContainerScreen.kt b/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/yourlibrary2/YourLibraryContainerScreen.kt
index 0cb4a85..1779482 100644
--- a/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/yourlibrary2/YourLibraryContainerScreen.kt
+++ b/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/yourlibrary2/YourLibraryContainerScreen.kt
@@ -8,6 +8,7 @@ import androidx.compose.animation.slideInHorizontally
import androidx.compose.animation.slideOutHorizontally
import androidx.compose.animation.togetherWith
import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Arrangement
@@ -201,9 +202,14 @@ fun YourLibraryContainerScreen(
onClick = { navController.navigate("spotify:config") },
modifier = Modifier.padding(start = 8.dp, end = 8.dp)
) {
- PreviewableAsyncImage(imageUrl = viewModel.profilePicture, placeholderType = "user", modifier = Modifier
- .size(36.dp)
- .clip(CircleShape))
+ PreviewableAsyncImage(
+ imageUrl = viewModel.profilePicture,
+ placeholderType = "user",
+ modifier = Modifier
+ .size(36.dp)
+ .clip(CircleShape)
+ .border(1.dp, MaterialTheme.colorScheme.onSurface, CircleShape)
+ )
}
}
}
From c5f58859ec8803845476de017aabeb4633585db5 Mon Sep 17 00:00:00 2001
From: Outlet7493 <122801553+Outlet7493@users.noreply.github.com>
Date: Sun, 21 Jan 2024 21:17:51 +0000
Subject: [PATCH 15/17] update deprecated calls
---
.../jetispot/core/ext/ModifierExt.kt | 11 --------
.../library/SessionControllerVerifier.kt | 1 +
.../ui/screens/config/QualityConfigScreen.kt | 3 +--
.../jetispot/ui/screens/hub/HubExt.kt | 2 +-
.../ui/screens/search/SearchScreen.kt | 26 ++++++++++++++-----
5 files changed, 23 insertions(+), 20 deletions(-)
delete mode 100644 app/src/main/java/bruhcollective/itaysonlab/jetispot/core/ext/ModifierExt.kt
diff --git a/app/src/main/java/bruhcollective/itaysonlab/jetispot/core/ext/ModifierExt.kt b/app/src/main/java/bruhcollective/itaysonlab/jetispot/core/ext/ModifierExt.kt
deleted file mode 100644
index d0a968b..0000000
--- a/app/src/main/java/bruhcollective/itaysonlab/jetispot/core/ext/ModifierExt.kt
+++ /dev/null
@@ -1,11 +0,0 @@
-package bruhcollective.itaysonlab.jetispot.core.ext
-
-import androidx.compose.ui.Modifier
-
-fun Modifier.conditional(condition : Boolean, modifier : Modifier.() -> Modifier) : Modifier {
- return if (condition) {
- then(modifier(Modifier))
- } else {
- this
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/bruhcollective/itaysonlab/jetispot/playback/service/library/SessionControllerVerifier.kt b/app/src/main/java/bruhcollective/itaysonlab/jetispot/playback/service/library/SessionControllerVerifier.kt
index b4eee9a..9c828e2 100644
--- a/app/src/main/java/bruhcollective/itaysonlab/jetispot/playback/service/library/SessionControllerVerifier.kt
+++ b/app/src/main/java/bruhcollective/itaysonlab/jetispot/playback/service/library/SessionControllerVerifier.kt
@@ -149,6 +149,7 @@ class SessionControllerVerifier @Inject constructor(
private fun pmNewSignaturesSupported() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.P
@SuppressLint("PackageManagerGetSignatures")
+ @Suppress("DEPRECATION")
private fun pmFlags() = if (pmNewSignaturesSupported()) PackageManager.GET_SIGNING_CERTIFICATES else PackageManager.GET_SIGNATURES
@RequiresApi(Build.VERSION_CODES.P)
diff --git a/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/config/QualityConfigScreen.kt b/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/config/QualityConfigScreen.kt
index de01adc..5ee5474 100644
--- a/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/config/QualityConfigScreen.kt
+++ b/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/config/QualityConfigScreen.kt
@@ -8,7 +8,6 @@ import bruhcollective.itaysonlab.jetispot.core.SpConfigurationManager
import bruhcollective.itaysonlab.jetispot.core.SpSessionManager
import bruhcollective.itaysonlab.jetispot.proto.AppConfig
import bruhcollective.itaysonlab.jetispot.proto.AudioQuality
-import com.spotify.pamviewservice.v1.proto.PremiumPlanRow
import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject
@@ -53,7 +52,7 @@ class QualityConfigScreenViewModel @Inject constructor(
}))
//if the user doesn't have premium, don't show the option to select very high quality
- if (spSessionManager.session?.getUserAttribute("name") != "Spotify Free") {
+ if (spSessionManager.session.getUserAttribute("name") != "Spotify Free") {
add(ConfigItem.Radio(R.string.quality_very_high, R.string.quality_very_high_desc, {
it.playerConfig.preferredQuality == AudioQuality.VERY_HIGH
}, { true }, {
diff --git a/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/hub/HubExt.kt b/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/hub/HubExt.kt
index d5864e2..ba4b8c4 100644
--- a/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/hub/HubExt.kt
+++ b/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/hub/HubExt.kt
@@ -84,7 +84,7 @@ fun HubScaffold(
Icon(Icons.Rounded.ArrowBack, null)
}
},
- colors = if (toolbarOptions.alwaysVisible) TopAppBarDefaults.smallTopAppBarColors() else TopAppBarDefaults.smallTopAppBarColors(
+ colors = if (toolbarOptions.alwaysVisible) TopAppBarDefaults.topAppBarColors() else TopAppBarDefaults.topAppBarColors(
containerColor = Color.Transparent,
scrolledContainerColor = MaterialTheme.colorScheme.compositeSurfaceElevation(
3.dp
diff --git a/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/search/SearchScreen.kt b/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/search/SearchScreen.kt
index 881df05..1c0ebac 100644
--- a/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/search/SearchScreen.kt
+++ b/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/search/SearchScreen.kt
@@ -2,7 +2,11 @@ package bruhcollective.itaysonlab.jetispot.ui.screens.search
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.clickable
-import androidx.compose.foundation.layout.*
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.statusBarsPadding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.pager.HorizontalPager
@@ -12,7 +16,15 @@ import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.Close
import androidx.compose.material.icons.rounded.Search
-import androidx.compose.material3.*
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.Icon
+import androidx.compose.material3.IconButton
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.OutlinedTextField
+import androidx.compose.material3.OutlinedTextFieldDefaults
+import androidx.compose.material3.Scaffold
+import androidx.compose.material3.Text
+import androidx.compose.material3.surfaceColorAtElevation
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
@@ -62,6 +74,7 @@ fun SearchScreen(
Scaffold(
topBar = {
Column(Modifier.statusBarsPadding()) {
+ val containerColor = MaterialTheme.colorScheme.surfaceColorAtElevation(8.dp)
OutlinedTextField(
value = viewModel.searchQuery,
onValueChange = { viewModel.searchQuery = it },
@@ -84,10 +97,11 @@ fun SearchScreen(
.padding(16.dp)
.fillMaxWidth()
.focusTarget(),
- colors = TextFieldDefaults.outlinedTextFieldColors(
- containerColor = MaterialTheme.colorScheme.surfaceColorAtElevation(
- 8.dp
- ), unfocusedBorderColor = MaterialTheme.colorScheme.surfaceVariant
+ colors = OutlinedTextFieldDefaults.colors(
+ focusedContainerColor = containerColor,
+ unfocusedContainerColor = containerColor,
+ disabledContainerColor = containerColor,
+ unfocusedBorderColor = MaterialTheme.colorScheme.surfaceVariant,
),
singleLine = true,
keyboardOptions = KeyboardOptions(
From a7a5a5fd92f72edfeae21cca4d42ecc3e08ba644 Mon Sep 17 00:00:00 2001
From: Outlet7493 <122801553+Outlet7493@users.noreply.github.com>
Date: Mon, 22 Jan 2024 01:50:08 +0000
Subject: [PATCH 16/17] manually place album/playlist image on findcard as
sometimes background picture is only color
---
.../jetispot/ui/hub/components/FindCard.kt | 42 ++++++++++++++-----
1 file changed, 32 insertions(+), 10 deletions(-)
diff --git a/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/hub/components/FindCard.kt b/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/hub/components/FindCard.kt
index e2ffc3f..4451040 100644
--- a/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/hub/components/FindCard.kt
+++ b/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/hub/components/FindCard.kt
@@ -1,11 +1,21 @@
package bruhcollective.itaysonlab.jetispot.ui.hub.components
-import androidx.compose.foundation.layout.*
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.offset
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Card
+import androidx.compose.material3.CardDefaults
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.rotate
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
@@ -15,19 +25,31 @@ import bruhcollective.itaysonlab.jetispot.core.objs.hub.HubItem
import bruhcollective.itaysonlab.jetispot.ui.hub.clickableHub
import bruhcollective.itaysonlab.jetispot.ui.shared.PreviewableAsyncImage
+private val fallbackColors = listOf("#148a08", "#e1118c", "#27856a", "#283ea3", "#0d73ec", "#e8115b")
+
@Composable
fun FindCard(
item: HubItem
) {
- Card(modifier = Modifier
- .height(100.dp)
- .fillMaxWidth()
- .clickableHub(item)) {
- Box {
+ Card(
+ modifier = Modifier
+ .height(100.dp)
+ .fillMaxWidth()
+ .clickableHub(item),
+ colors = CardDefaults.cardColors(
+ containerColor = Color(android.graphics.Color.parseColor((item.custom?.get("backgroundColor") as? String) ?: fallbackColors.random()))
+ )
+ ) {
+ Box(Modifier.fillMaxSize()) {
PreviewableAsyncImage(
- imageUrl = item.images?.background?.uri,
+ imageUrl = item.images?.main?.uri,
placeholderType = item.images?.background?.placeholder,
- modifier = Modifier.fillMaxSize()
+ modifier = Modifier
+ .size(84.dp)
+ .offset(x = 24.dp, y = 12.dp)
+ .rotate(20f)
+ .clip(RoundedCornerShape(8.dp))
+ .align(Alignment.BottomEnd)
)
Text(
item.text!!.title!!,
@@ -37,8 +59,8 @@ fun FindCard(
maxLines = 2,
overflow = TextOverflow.Ellipsis,
modifier = Modifier
- .align(Alignment.TopStart)
- .padding(12.dp)
+ .align(Alignment.TopStart)
+ .padding(12.dp)
)
}
}
From e2b447c422d641c7e9ed26a70e5b5425955c24a0 Mon Sep 17 00:00:00 2001
From: Outlet7493 <122801553+Outlet7493@users.noreply.github.com>
Date: Mon, 22 Jan 2024 01:53:12 +0000
Subject: [PATCH 17/17] neaten config screens
---
.../ui/screens/config/ConfigScreen.kt | 6 +-
.../ui/screens/config/SharedConfigUi.kt | 64 +++++++++++++++++--
.../ui/screens/config/StorageScreen.kt | 15 +++--
app/src/main/res/values/strings.xml | 2 +-
4 files changed, 72 insertions(+), 15 deletions(-)
diff --git a/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/config/ConfigScreen.kt b/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/config/ConfigScreen.kt
index 972540c..00da7e0 100644
--- a/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/config/ConfigScreen.kt
+++ b/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/config/ConfigScreen.kt
@@ -32,7 +32,7 @@ class ConfigScreenViewModel @Inject constructor(
) : ViewModel(), ConfigViewModel {
private val configList = buildList {
- add(ConfigItem.Hint())
+ add(ConfigItem.Hint)
add(ConfigItem.Category(R.string.config_playback))
@@ -109,11 +109,11 @@ class ConfigScreenViewModel @Inject constructor(
add(ConfigItem.Category(R.string.config_about))
- add(ConfigItem.Preference(R.string.app_name, { ctx, _ ->
+ add(ConfigItem.InfoItem(R.string.app_name) { ctx ->
ctx.getString(
R.string.about_version, BuildConfig.VERSION_NAME
)
- }, {}))
+ })
add(ConfigItem.Preference(R.string.about_sources, { ctx, _ -> "" }, {
it.openInBrowser("https://github.com/iTaysonLab/jetispot")
diff --git a/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/config/SharedConfigUi.kt b/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/config/SharedConfigUi.kt
index e8e9c27..1970fd1 100644
--- a/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/config/SharedConfigUi.kt
+++ b/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/config/SharedConfigUi.kt
@@ -1,5 +1,6 @@
package bruhcollective.itaysonlab.jetispot.ui.screens.config
+import android.annotation.SuppressLint
import android.content.Context
import android.content.Intent
import android.net.Uri
@@ -13,7 +14,14 @@ import androidx.compose.animation.fadeOut
import androidx.compose.animation.shrinkVertically
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
-import androidx.compose.foundation.layout.*
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.WindowInsets
+import androidx.compose.foundation.layout.fillMaxHeight
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.RoundedCornerShape
@@ -22,8 +30,27 @@ import androidx.compose.material.icons.outlined.Translate
import androidx.compose.material.icons.rounded.ArrowBack
import androidx.compose.material.icons.rounded.EnergySavingsLeaf
import androidx.compose.material.icons.rounded.Info
-import androidx.compose.material3.*
-import androidx.compose.runtime.*
+import androidx.compose.material3.Card
+import androidx.compose.material3.CardDefaults
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.Icon
+import androidx.compose.material3.IconButton
+import androidx.compose.material3.LargeTopAppBar
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.RadioButton
+import androidx.compose.material3.Scaffold
+import androidx.compose.material3.Slider
+import androidx.compose.material3.Switch
+import androidx.compose.material3.SwitchDefaults
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.Stable
+import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
@@ -53,6 +80,7 @@ interface ConfigViewModel {
fun isRoot(): Boolean = false
}
+@SuppressLint("BatteryLife")
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun BaseConfigScreen(
@@ -118,6 +146,10 @@ fun BaseConfigScreen(
ConfigInfo(stringResource(item.text))
}
+ is ConfigItem.InfoItem -> {
+ ConfigInfoItem(stringResource(item.title), item.subtitle(context))
+ }
+
is ConfigItem.Preference -> {
ConfigPreference(
stringResource(item.title),
@@ -288,6 +320,24 @@ fun ConfigRadio(
}
}
+@Composable
+fun ConfigInfoItem(
+ title: String,
+ subtitle: String
+) {
+ Column(modifier = Modifier
+ .fillMaxWidth()
+ .padding(16.dp)) {
+ Text(text = title, color = MaterialTheme.colorScheme.onBackground, fontSize = 18.sp)
+ if (subtitle.isNotEmpty()) Text(
+ text = subtitle,
+ color = MaterialTheme.colorScheme.onSurfaceVariant,
+ fontSize = 14.sp,
+ modifier = Modifier.padding(top = 4.dp)
+ )
+ }
+}
+
@Composable
fun ConfigPreference(
title: String,
@@ -415,6 +465,11 @@ sealed class ConfigItem {
class Category(@StringRes val title: Int) : ConfigItem()
class Info(@StringRes val text: Int) : ConfigItem()
+ class InfoItem(
+ @StringRes val title: Int,
+ val subtitle: (Context) -> String
+ ): ConfigItem()
+
class Preference(
@StringRes val title: Int,
val subtitle: (Context, AppConfig) -> String,
@@ -451,6 +506,5 @@ sealed class ConfigItem {
val modify: AppConfig.Builder.(Int) -> Unit
) : ConfigItem()
- class Hint(
- ) : ConfigItem()
+ data object Hint : ConfigItem()
}
\ No newline at end of file
diff --git a/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/config/StorageScreen.kt b/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/config/StorageScreen.kt
index 60b5e15..a159692 100644
--- a/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/config/StorageScreen.kt
+++ b/app/src/main/java/bruhcollective/itaysonlab/jetispot/ui/screens/config/StorageScreen.kt
@@ -115,6 +115,7 @@ fun StorageScreen(
// 2-3. Header
item("storageact") {
+ Spacer(Modifier.size(8.dp))
ConfigCategory(text = stringResource(id = R.string.storage_actions))
}
@@ -131,7 +132,7 @@ fun StorageScreen(
}
@Composable
-fun StorageComponentDetail(
+private fun StorageComponentDetail(
type: StorageViewModel.StorageFileKind,
size: String,
) {
@@ -139,7 +140,9 @@ fun StorageComponentDetail(
Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp)
- .padding(top = 16.dp)) {
+ .padding(top = 16.dp),
+ verticalAlignment = Alignment.CenterVertically
+ ) {
Icon(imageVector = type.icon, contentDescription = null, modifier = Modifier.size(28.dp))
Column(Modifier.padding(start = 16.dp)) {
@@ -151,7 +154,7 @@ fun StorageComponentDetail(
}
@Composable
-fun StorageHeader(
+private fun StorageHeader(
state: StorageViewModel.UiState.Ready
) {
Column(Modifier.padding(horizontal = 16.dp)) {
@@ -191,7 +194,7 @@ fun StorageHeader(
.height(16.dp), state.takenTotal, state.others, state.internalStorage.total
)
- Row(Modifier.padding(top = 4.dp, bottom = 8.dp)) {
+ Row(Modifier.padding(top = 8.dp, bottom = 8.dp)) {
ProgressIndicatorLegend(
modifier = Modifier.align(Alignment.CenterVertically),
color = MaterialTheme.colorScheme.primary,
@@ -211,7 +214,7 @@ fun StorageHeader(
}
@Composable
-fun ProgressIndicatorLegend(
+private fun ProgressIndicatorLegend(
modifier: Modifier = Modifier,
color: Color,
text: String
@@ -233,7 +236,7 @@ fun ProgressIndicatorLegend(
}
@Composable
-fun MultiStateProgressIndicator(
+private fun MultiStateProgressIndicator(
modifier: Modifier,
application: Long,
others: Long,
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index cf974a4..dfa8a0f 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -68,7 +68,7 @@
Plan includes
See other plans
- version %s
+ Version %s
Show source code
Open Telegram channel