Attempt fix #401
This commit is contained in:
parent
f4a55ff2ad
commit
ed8eb7cf8a
64 changed files with 373 additions and 944 deletions
|
@ -70,6 +70,7 @@ kapt {
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
implementation(projects.composePersist)
|
||||||
implementation(projects.composeRouting)
|
implementation(projects.composeRouting)
|
||||||
implementation(projects.composeReordering)
|
implementation(projects.composeReordering)
|
||||||
|
|
||||||
|
|
|
@ -58,6 +58,8 @@ import androidx.media3.common.MediaItem
|
||||||
import androidx.media3.common.Player
|
import androidx.media3.common.Player
|
||||||
import com.valentinilk.shimmer.LocalShimmerTheme
|
import com.valentinilk.shimmer.LocalShimmerTheme
|
||||||
import com.valentinilk.shimmer.defaultShimmerTheme
|
import com.valentinilk.shimmer.defaultShimmerTheme
|
||||||
|
import it.vfsfitvnm.compose.persist.PersistMap
|
||||||
|
import it.vfsfitvnm.compose.persist.PersistMapOwner
|
||||||
import it.vfsfitvnm.innertube.Innertube
|
import it.vfsfitvnm.innertube.Innertube
|
||||||
import it.vfsfitvnm.innertube.models.bodies.BrowseBody
|
import it.vfsfitvnm.innertube.models.bodies.BrowseBody
|
||||||
import it.vfsfitvnm.innertube.requests.playlistPage
|
import it.vfsfitvnm.innertube.requests.playlistPage
|
||||||
|
@ -99,7 +101,7 @@ import kotlinx.coroutines.flow.first
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
class MainActivity : ComponentActivity() {
|
class MainActivity : ComponentActivity(), PersistMapOwner {
|
||||||
private val serviceConnection = object : ServiceConnection {
|
private val serviceConnection = object : ServiceConnection {
|
||||||
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
|
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
|
||||||
if (service is PlayerService.Binder) {
|
if (service is PlayerService.Binder) {
|
||||||
|
@ -114,20 +116,20 @@ class MainActivity : ComponentActivity() {
|
||||||
|
|
||||||
private var binder by mutableStateOf<PlayerService.Binder?>(null)
|
private var binder by mutableStateOf<PlayerService.Binder?>(null)
|
||||||
|
|
||||||
|
override lateinit var persistMap: PersistMap
|
||||||
|
|
||||||
override fun onStart() {
|
override fun onStart() {
|
||||||
super.onStart()
|
super.onStart()
|
||||||
bindService(intent<PlayerService>(), serviceConnection, Context.BIND_AUTO_CREATE)
|
bindService(intent<PlayerService>(), serviceConnection, Context.BIND_AUTO_CREATE)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onStop() {
|
|
||||||
unbindService(serviceConnection)
|
|
||||||
super.onStop()
|
|
||||||
}
|
|
||||||
|
|
||||||
@OptIn(ExperimentalFoundationApi::class, ExperimentalAnimationApi::class)
|
@OptIn(ExperimentalFoundationApi::class, ExperimentalAnimationApi::class)
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
|
@Suppress("DEPRECATION", "UNCHECKED_CAST")
|
||||||
|
persistMap = lastCustomNonConfigurationInstance as? PersistMap ?: PersistMap()
|
||||||
|
|
||||||
WindowCompat.setDecorFitsSystemWindows(window, false)
|
WindowCompat.setDecorFitsSystemWindows(window, false)
|
||||||
|
|
||||||
val launchedFromNotification = intent?.extras?.getBoolean("expandPlayerBottomSheet") == true
|
val launchedFromNotification = intent?.extras?.getBoolean("expandPlayerBottomSheet") == true
|
||||||
|
@ -451,6 +453,21 @@ class MainActivity : ComponentActivity() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onRetainCustomNonConfigurationInstance() = persistMap
|
||||||
|
|
||||||
|
override fun onStop() {
|
||||||
|
unbindService(serviceConnection)
|
||||||
|
super.onStop()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroy() {
|
||||||
|
if (!isChangingConfigurations) {
|
||||||
|
persistMap.clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
super.onDestroy()
|
||||||
|
}
|
||||||
|
|
||||||
private fun setSystemBarAppearance(isDark: Boolean) {
|
private fun setSystemBarAppearance(isDark: Boolean) {
|
||||||
with(WindowCompat.getInsetsController(window, window.decorView.rootView)) {
|
with(WindowCompat.getInsetsController(window, window.decorView.rootView)) {
|
||||||
isAppearanceLightStatusBars = !isDark
|
isAppearanceLightStatusBars = !isDark
|
||||||
|
|
|
@ -1,31 +0,0 @@
|
||||||
package it.vfsfitvnm.vimusic.savers
|
|
||||||
|
|
||||||
import androidx.compose.runtime.saveable.Saver
|
|
||||||
import androidx.compose.runtime.saveable.SaverScope
|
|
||||||
import it.vfsfitvnm.vimusic.models.Album
|
|
||||||
|
|
||||||
object AlbumSaver : Saver<Album, List<Any?>> {
|
|
||||||
override fun SaverScope.save(value: Album): List<Any?> = listOf(
|
|
||||||
value.id,
|
|
||||||
value.title,
|
|
||||||
value.thumbnailUrl,
|
|
||||||
value.year,
|
|
||||||
value.authorsText,
|
|
||||||
value.shareUrl,
|
|
||||||
value.timestamp,
|
|
||||||
value.bookmarkedAt,
|
|
||||||
)
|
|
||||||
|
|
||||||
override fun restore(value: List<Any?>): Album = Album(
|
|
||||||
id = value[0] as String,
|
|
||||||
title = value[1] as String,
|
|
||||||
thumbnailUrl = value[2] as String?,
|
|
||||||
year = value[3] as String?,
|
|
||||||
authorsText = value[4] as String?,
|
|
||||||
shareUrl = value[5] as String?,
|
|
||||||
timestamp = value[6] as Long?,
|
|
||||||
bookmarkedAt = value[7] as Long?,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
val AlbumListSaver = listSaver(AlbumSaver)
|
|
|
@ -1,25 +0,0 @@
|
||||||
package it.vfsfitvnm.vimusic.savers
|
|
||||||
|
|
||||||
import androidx.compose.runtime.saveable.Saver
|
|
||||||
import androidx.compose.runtime.saveable.SaverScope
|
|
||||||
import it.vfsfitvnm.vimusic.models.Artist
|
|
||||||
|
|
||||||
object ArtistSaver : Saver<Artist, List<Any?>> {
|
|
||||||
override fun SaverScope.save(value: Artist): List<Any?> = listOf(
|
|
||||||
value.id,
|
|
||||||
value.name,
|
|
||||||
value.thumbnailUrl,
|
|
||||||
value.timestamp,
|
|
||||||
value.bookmarkedAt,
|
|
||||||
)
|
|
||||||
|
|
||||||
override fun restore(value: List<Any?>): Artist = Artist(
|
|
||||||
id = value[0] as String,
|
|
||||||
name = value[1] as String?,
|
|
||||||
thumbnailUrl = value[2] as String?,
|
|
||||||
timestamp = value[3] as Long?,
|
|
||||||
bookmarkedAt = value[4] as Long?,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
val ArtistListSaver = listSaver(ArtistSaver)
|
|
|
@ -1,33 +0,0 @@
|
||||||
package it.vfsfitvnm.vimusic.savers
|
|
||||||
|
|
||||||
import androidx.compose.runtime.saveable.Saver
|
|
||||||
import androidx.compose.runtime.saveable.SaverScope
|
|
||||||
import it.vfsfitvnm.vimusic.models.DetailedSong
|
|
||||||
|
|
||||||
object DetailedSongSaver : Saver<DetailedSong, List<Any?>> {
|
|
||||||
override fun SaverScope.save(value: DetailedSong) =
|
|
||||||
listOf(
|
|
||||||
value.id,
|
|
||||||
value.title,
|
|
||||||
value.artistsText,
|
|
||||||
value.durationText,
|
|
||||||
value.thumbnailUrl,
|
|
||||||
value.totalPlayTimeMs,
|
|
||||||
value.albumId,
|
|
||||||
value.artists?.let { with(InfoListSaver) { save(it) } }
|
|
||||||
)
|
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
|
||||||
override fun restore(value: List<Any?>) = DetailedSong(
|
|
||||||
id = value[0] as String,
|
|
||||||
title = value[1] as String,
|
|
||||||
artistsText = value[2] as String?,
|
|
||||||
durationText = value[3] as String?,
|
|
||||||
thumbnailUrl = value[4] as String?,
|
|
||||||
totalPlayTimeMs = value[5] as Long,
|
|
||||||
albumId = value[6] as String?,
|
|
||||||
artists = (value[7] as List<List<String>>?)?.let(InfoListSaver::restore)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
val DetailedSongListSaver = listSaver(DetailedSongSaver)
|
|
|
@ -1,13 +0,0 @@
|
||||||
package it.vfsfitvnm.vimusic.savers
|
|
||||||
|
|
||||||
import androidx.compose.runtime.saveable.Saver
|
|
||||||
import androidx.compose.runtime.saveable.SaverScope
|
|
||||||
import it.vfsfitvnm.vimusic.models.Info
|
|
||||||
|
|
||||||
object InfoSaver : Saver<Info, List<String?>> {
|
|
||||||
override fun SaverScope.save(value: Info) = listOf(value.id, value.name)
|
|
||||||
|
|
||||||
override fun restore(value: List<String?>) = Info(id = value[0] as String, name = value[1])
|
|
||||||
}
|
|
||||||
|
|
||||||
val InfoListSaver = listSaver(InfoSaver)
|
|
|
@ -1,24 +0,0 @@
|
||||||
package it.vfsfitvnm.vimusic.savers
|
|
||||||
|
|
||||||
import androidx.compose.runtime.saveable.Saver
|
|
||||||
import androidx.compose.runtime.saveable.SaverScope
|
|
||||||
import it.vfsfitvnm.innertube.Innertube
|
|
||||||
|
|
||||||
object InnertubeAlbumItemSaver : Saver<Innertube.AlbumItem, List<Any?>> {
|
|
||||||
override fun SaverScope.save(value: Innertube.AlbumItem): List<Any?> = listOf(
|
|
||||||
value.info?.let { with(InnertubeBrowseInfoSaver) { save(it) } },
|
|
||||||
value.authors?.let { with(InnertubeBrowseInfoListSaver) { save(it) } },
|
|
||||||
value.year,
|
|
||||||
value.thumbnail?.let { with(InnertubeThumbnailSaver) { save(it) } }
|
|
||||||
)
|
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
|
||||||
override fun restore(value: List<Any?>) = Innertube.AlbumItem(
|
|
||||||
info = (value[0] as List<Any?>?)?.let(InnertubeBrowseInfoSaver::restore),
|
|
||||||
authors = (value[1] as List<List<Any?>>?)?.let(InnertubeBrowseInfoListSaver::restore),
|
|
||||||
year = value[2] as String?,
|
|
||||||
thumbnail = (value[3] as List<Any?>?)?.let(InnertubeThumbnailSaver::restore)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
val InnertubeAlbumItemListSaver = listSaver(InnertubeAlbumItemSaver)
|
|
|
@ -1,21 +0,0 @@
|
||||||
package it.vfsfitvnm.vimusic.savers
|
|
||||||
|
|
||||||
import androidx.compose.runtime.saveable.Saver
|
|
||||||
import androidx.compose.runtime.saveable.SaverScope
|
|
||||||
import it.vfsfitvnm.innertube.Innertube
|
|
||||||
|
|
||||||
object InnertubeArtistItemSaver : Saver<Innertube.ArtistItem, List<Any?>> {
|
|
||||||
override fun SaverScope.save(value: Innertube.ArtistItem): List<Any?> = listOf(
|
|
||||||
value.info?.let { with(InnertubeBrowseInfoSaver) { save(it) } },
|
|
||||||
value.subscribersCountText,
|
|
||||||
value.thumbnail?.let { with(InnertubeThumbnailSaver) { save(it) } }
|
|
||||||
)
|
|
||||||
|
|
||||||
override fun restore(value: List<Any?>) = Innertube.ArtistItem(
|
|
||||||
info = (value[0] as List<Any?>?)?.let(InnertubeBrowseInfoSaver::restore),
|
|
||||||
subscribersCountText = value[1] as String?,
|
|
||||||
thumbnail = (value[2] as List<Any?>?)?.let(InnertubeThumbnailSaver::restore)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
val InnertubeArtistItemListSaver = listSaver(InnertubeArtistItemSaver)
|
|
|
@ -1,36 +0,0 @@
|
||||||
package it.vfsfitvnm.vimusic.savers
|
|
||||||
|
|
||||||
import androidx.compose.runtime.saveable.Saver
|
|
||||||
import androidx.compose.runtime.saveable.SaverScope
|
|
||||||
import it.vfsfitvnm.innertube.Innertube
|
|
||||||
|
|
||||||
object InnertubeArtistPageSaver : Saver<Innertube.ArtistPage, List<Any?>> {
|
|
||||||
override fun SaverScope.save(value: Innertube.ArtistPage) = listOf(
|
|
||||||
value.name,
|
|
||||||
value.description,
|
|
||||||
value.thumbnail?.let { with(InnertubeThumbnailSaver) { save(it) } },
|
|
||||||
value.shuffleEndpoint?.let { with(InnertubeWatchEndpointSaver) { save(it) } },
|
|
||||||
value.radioEndpoint?.let { with(InnertubeWatchEndpointSaver) { save(it) } },
|
|
||||||
value.songs?.let { with(InnertubeSongItemListSaver) { save(it) } },
|
|
||||||
value.songsEndpoint?.let { with(InnertubeBrowseEndpointSaver) { save(it) } },
|
|
||||||
value.albums?.let { with(InnertubeAlbumItemListSaver) { save(it) } },
|
|
||||||
value.albumsEndpoint?.let { with(InnertubeBrowseEndpointSaver) { save(it) } },
|
|
||||||
value.singles?.let { with(InnertubeAlbumItemListSaver) { save(it) } },
|
|
||||||
value.singlesEndpoint?.let { with(InnertubeBrowseEndpointSaver) { save(it) } },
|
|
||||||
)
|
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
|
||||||
override fun restore(value: List<Any?>) = Innertube.ArtistPage(
|
|
||||||
name = value[0] as String?,
|
|
||||||
description = value[1] as String?,
|
|
||||||
thumbnail = (value[2] as List<Any?>?)?.let(InnertubeThumbnailSaver::restore),
|
|
||||||
shuffleEndpoint = (value[3] as List<Any?>?)?.let(InnertubeWatchEndpointSaver::restore),
|
|
||||||
radioEndpoint = (value[4] as List<Any?>?)?.let(InnertubeWatchEndpointSaver::restore),
|
|
||||||
songs = (value[5] as List<List<Any?>>?)?.let(InnertubeSongItemListSaver::restore),
|
|
||||||
songsEndpoint = (value[6] as List<Any?>?)?.let(InnertubeBrowseEndpointSaver::restore),
|
|
||||||
albums = (value[7] as List<List<Any?>>?)?.let(InnertubeAlbumItemListSaver::restore),
|
|
||||||
albumsEndpoint = (value[8] as List<Any?>?)?.let(InnertubeBrowseEndpointSaver::restore),
|
|
||||||
singles = (value[9] as List<List<Any?>>?)?.let(InnertubeAlbumItemListSaver::restore),
|
|
||||||
singlesEndpoint = (value[10] as List<Any?>?)?.let(InnertubeBrowseEndpointSaver::restore),
|
|
||||||
)
|
|
||||||
}
|
|
|
@ -1,18 +0,0 @@
|
||||||
package it.vfsfitvnm.vimusic.savers
|
|
||||||
|
|
||||||
import androidx.compose.runtime.saveable.Saver
|
|
||||||
import androidx.compose.runtime.saveable.SaverScope
|
|
||||||
import it.vfsfitvnm.innertube.models.NavigationEndpoint
|
|
||||||
|
|
||||||
object InnertubeBrowseEndpointSaver : Saver<NavigationEndpoint.Endpoint.Browse, List<Any?>> {
|
|
||||||
override fun SaverScope.save(value: NavigationEndpoint.Endpoint.Browse) = listOf(
|
|
||||||
value.browseId,
|
|
||||||
value.params
|
|
||||||
)
|
|
||||||
|
|
||||||
override fun restore(value: List<Any?>) = NavigationEndpoint.Endpoint.Browse(
|
|
||||||
browseId = value[0] as String,
|
|
||||||
params = value[1] as String?,
|
|
||||||
browseEndpointContextSupportedConfigs = null
|
|
||||||
)
|
|
||||||
}
|
|
|
@ -1,20 +0,0 @@
|
||||||
package it.vfsfitvnm.vimusic.savers
|
|
||||||
|
|
||||||
import androidx.compose.runtime.saveable.Saver
|
|
||||||
import androidx.compose.runtime.saveable.SaverScope
|
|
||||||
import it.vfsfitvnm.innertube.Innertube
|
|
||||||
import it.vfsfitvnm.innertube.models.NavigationEndpoint
|
|
||||||
|
|
||||||
object InnertubeBrowseInfoSaver : Saver<Innertube.Info<NavigationEndpoint.Endpoint.Browse>, List<Any?>> {
|
|
||||||
override fun SaverScope.save(value: Innertube.Info<NavigationEndpoint.Endpoint.Browse>) = listOf(
|
|
||||||
value.name,
|
|
||||||
value.endpoint?.let { with(InnertubeBrowseEndpointSaver) { save(it) } }
|
|
||||||
)
|
|
||||||
|
|
||||||
override fun restore(value: List<Any?>) = Innertube.Info(
|
|
||||||
name = value[0] as String?,
|
|
||||||
endpoint = (value[1] as List<Any?>?)?.let(InnertubeBrowseEndpointSaver::restore)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
val InnertubeBrowseInfoListSaver = listSaver(InnertubeBrowseInfoSaver)
|
|
|
@ -1,4 +0,0 @@
|
||||||
package it.vfsfitvnm.vimusic.savers
|
|
||||||
|
|
||||||
val InnertubeSongsPageSaver = innertubeItemsPageSaver(InnertubeSongItemListSaver)
|
|
||||||
val InnertubeAlbumsPageSaver = innertubeItemsPageSaver(InnertubeAlbumItemListSaver)
|
|
|
@ -1,23 +0,0 @@
|
||||||
package it.vfsfitvnm.vimusic.savers
|
|
||||||
|
|
||||||
import androidx.compose.runtime.saveable.Saver
|
|
||||||
import androidx.compose.runtime.saveable.SaverScope
|
|
||||||
import it.vfsfitvnm.innertube.Innertube
|
|
||||||
|
|
||||||
object InnertubePlaylistItemSaver : Saver<Innertube.PlaylistItem, List<Any?>> {
|
|
||||||
override fun SaverScope.save(value: Innertube.PlaylistItem): List<Any?> = listOf(
|
|
||||||
value.info?.let { with(InnertubeBrowseInfoSaver) { save(it) } },
|
|
||||||
value.channel?.let { with(InnertubeBrowseInfoSaver) { save(it) } },
|
|
||||||
value.songCount,
|
|
||||||
value.thumbnail?.let { with(InnertubeThumbnailSaver) { save(it) } }
|
|
||||||
)
|
|
||||||
|
|
||||||
override fun restore(value: List<Any?>) = Innertube.PlaylistItem(
|
|
||||||
info = (value[0] as List<Any?>?)?.let(InnertubeBrowseInfoSaver::restore),
|
|
||||||
channel = (value[1] as List<Any?>?)?.let(InnertubeBrowseInfoSaver::restore),
|
|
||||||
songCount = value[2] as Int?,
|
|
||||||
thumbnail = (value[3] as List<Any?>?)?.let(InnertubeThumbnailSaver::restore)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
val InnertubePlaylistItemListSaver = listSaver(InnertubePlaylistItemSaver)
|
|
|
@ -1,28 +0,0 @@
|
||||||
package it.vfsfitvnm.vimusic.savers
|
|
||||||
|
|
||||||
import androidx.compose.runtime.saveable.Saver
|
|
||||||
import androidx.compose.runtime.saveable.SaverScope
|
|
||||||
import it.vfsfitvnm.innertube.Innertube
|
|
||||||
|
|
||||||
object InnertubePlaylistOrAlbumPageSaver : Saver<Innertube.PlaylistOrAlbumPage, List<Any?>> {
|
|
||||||
override fun SaverScope.save(value: Innertube.PlaylistOrAlbumPage): List<Any?> = listOf(
|
|
||||||
value.title,
|
|
||||||
value.authors?.let { with(InnertubeBrowseInfoListSaver) { save(it) } },
|
|
||||||
value.year,
|
|
||||||
value.thumbnail?.let { with(InnertubeThumbnailSaver) { save(it) } } ,
|
|
||||||
value.url,
|
|
||||||
value.songsPage?.let { with(InnertubeSongsPageSaver) { save(it) } },
|
|
||||||
value.otherVersions?.let { with(InnertubeAlbumItemListSaver) { save(it) } },
|
|
||||||
)
|
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
|
||||||
override fun restore(value: List<Any?>) = Innertube.PlaylistOrAlbumPage(
|
|
||||||
title = value[0] as String?,
|
|
||||||
authors = (value[1] as List<List<Any?>>?)?.let(InnertubeBrowseInfoListSaver::restore),
|
|
||||||
year = value[2] as String?,
|
|
||||||
thumbnail = (value[3] as List<Any?>?)?.let(InnertubeThumbnailSaver::restore),
|
|
||||||
url = value[4] as String?,
|
|
||||||
songsPage = (value[5] as List<Any?>?)?.let(InnertubeSongsPageSaver::restore),
|
|
||||||
otherVersions = (value[6] as List<List<Any?>>?)?.let(InnertubeAlbumItemListSaver::restore),
|
|
||||||
)
|
|
||||||
}
|
|
|
@ -1,22 +0,0 @@
|
||||||
package it.vfsfitvnm.vimusic.savers
|
|
||||||
|
|
||||||
import androidx.compose.runtime.saveable.Saver
|
|
||||||
import androidx.compose.runtime.saveable.SaverScope
|
|
||||||
import it.vfsfitvnm.innertube.Innertube
|
|
||||||
|
|
||||||
object InnertubeRelatedPageSaver : Saver<Innertube.RelatedPage, List<Any?>> {
|
|
||||||
override fun SaverScope.save(value: Innertube.RelatedPage): List<Any?> = listOf(
|
|
||||||
value.songs?.let { with(InnertubeSongItemListSaver) { save(it) } },
|
|
||||||
value.playlists?.let { with(InnertubePlaylistItemListSaver) { save(it) } },
|
|
||||||
value.albums?.let { with(InnertubeAlbumItemListSaver) { save(it) } },
|
|
||||||
value.artists?.let { with(InnertubeArtistItemListSaver) { save(it) } },
|
|
||||||
)
|
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
|
||||||
override fun restore(value: List<Any?>) = Innertube.RelatedPage(
|
|
||||||
songs = (value[0] as List<List<Any?>>?)?.let(InnertubeSongItemListSaver::restore),
|
|
||||||
playlists = (value[1] as List<List<Any?>>?)?.let(InnertubePlaylistItemListSaver::restore),
|
|
||||||
albums = (value[2] as List<List<Any?>>?)?.let(InnertubeAlbumItemListSaver::restore),
|
|
||||||
artists = (value[3] as List<List<Any?>>?)?.let(InnertubeArtistItemListSaver::restore),
|
|
||||||
)
|
|
||||||
}
|
|
|
@ -1,26 +0,0 @@
|
||||||
package it.vfsfitvnm.vimusic.savers
|
|
||||||
|
|
||||||
import androidx.compose.runtime.saveable.Saver
|
|
||||||
import androidx.compose.runtime.saveable.SaverScope
|
|
||||||
import it.vfsfitvnm.innertube.Innertube
|
|
||||||
|
|
||||||
object InnertubeSongItemSaver : Saver<Innertube.SongItem, List<Any?>> {
|
|
||||||
override fun SaverScope.save(value: Innertube.SongItem): List<Any?> = listOf(
|
|
||||||
value.info?.let { with(InnertubeWatchInfoSaver) { save(it) } },
|
|
||||||
value.authors?.let { with(InnertubeBrowseInfoListSaver) { save(it) } },
|
|
||||||
value.album?.let { with(InnertubeBrowseInfoSaver) { save(it) } },
|
|
||||||
value.durationText,
|
|
||||||
value.thumbnail?.let { with(InnertubeThumbnailSaver) { save(it) } }
|
|
||||||
)
|
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
|
||||||
override fun restore(value: List<Any?>) = Innertube.SongItem(
|
|
||||||
info = (value[0] as List<Any?>?)?.let(InnertubeWatchInfoSaver::restore),
|
|
||||||
authors = (value[1] as List<List<Any?>>?)?.let(InnertubeBrowseInfoListSaver::restore),
|
|
||||||
album = (value[2] as List<Any?>?)?.let(InnertubeBrowseInfoSaver::restore),
|
|
||||||
durationText = value[3] as String?,
|
|
||||||
thumbnail = (value[4] as List<Any?>?)?.let(InnertubeThumbnailSaver::restore)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
val InnertubeSongItemListSaver = listSaver(InnertubeSongItemSaver)
|
|
|
@ -1,19 +0,0 @@
|
||||||
package it.vfsfitvnm.vimusic.savers
|
|
||||||
|
|
||||||
import androidx.compose.runtime.saveable.Saver
|
|
||||||
import androidx.compose.runtime.saveable.SaverScope
|
|
||||||
import it.vfsfitvnm.innertube.models.Thumbnail
|
|
||||||
|
|
||||||
object InnertubeThumbnailSaver : Saver<Thumbnail, List<Any?>> {
|
|
||||||
override fun SaverScope.save(value: Thumbnail) = listOf(
|
|
||||||
value.url,
|
|
||||||
value.width,
|
|
||||||
value.height
|
|
||||||
)
|
|
||||||
|
|
||||||
override fun restore(value: List<Any?>) = Thumbnail(
|
|
||||||
url = value[0] as String,
|
|
||||||
width = value[1] as Int,
|
|
||||||
height = value[2] as Int?,
|
|
||||||
)
|
|
||||||
}
|
|
|
@ -1,26 +0,0 @@
|
||||||
package it.vfsfitvnm.vimusic.savers
|
|
||||||
|
|
||||||
import androidx.compose.runtime.saveable.Saver
|
|
||||||
import androidx.compose.runtime.saveable.SaverScope
|
|
||||||
import it.vfsfitvnm.innertube.Innertube
|
|
||||||
|
|
||||||
object InnertubeVideoItemSaver : Saver<Innertube.VideoItem, List<Any?>> {
|
|
||||||
override fun SaverScope.save(value: Innertube.VideoItem): List<Any?> = listOf(
|
|
||||||
value.info?.let { with(InnertubeWatchInfoSaver) { save(it) } },
|
|
||||||
value.authors?.let { with(InnertubeBrowseInfoListSaver) { save(it) } },
|
|
||||||
value.viewsText,
|
|
||||||
value.durationText,
|
|
||||||
value.thumbnail?.let { with(InnertubeThumbnailSaver) { save(it) } }
|
|
||||||
)
|
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
|
||||||
override fun restore(value: List<Any?>) = Innertube.VideoItem(
|
|
||||||
info = (value[0] as List<Any?>?)?.let(InnertubeWatchInfoSaver::restore),
|
|
||||||
authors = (value[1] as List<List<Any?>>?)?.let(InnertubeBrowseInfoListSaver::restore),
|
|
||||||
viewsText = value[2] as String?,
|
|
||||||
durationText = value[3] as String?,
|
|
||||||
thumbnail = (value[4] as List<Any?>?)?.let(InnertubeThumbnailSaver::restore)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
val InnertubeVideoItemListSaver = listSaver(InnertubeVideoItemSaver)
|
|
|
@ -1,24 +0,0 @@
|
||||||
package it.vfsfitvnm.vimusic.savers
|
|
||||||
|
|
||||||
import androidx.compose.runtime.saveable.Saver
|
|
||||||
import androidx.compose.runtime.saveable.SaverScope
|
|
||||||
import it.vfsfitvnm.innertube.models.NavigationEndpoint
|
|
||||||
|
|
||||||
object InnertubeWatchEndpointSaver : Saver<NavigationEndpoint.Endpoint.Watch, List<Any?>> {
|
|
||||||
override fun SaverScope.save(value: NavigationEndpoint.Endpoint.Watch) = listOf(
|
|
||||||
value.params,
|
|
||||||
value.playlistId,
|
|
||||||
value.videoId,
|
|
||||||
value.index,
|
|
||||||
value.playlistSetVideoId,
|
|
||||||
)
|
|
||||||
|
|
||||||
override fun restore(value: List<Any?>) = NavigationEndpoint.Endpoint.Watch(
|
|
||||||
params = value[0] as String?,
|
|
||||||
playlistId = value[1] as String?,
|
|
||||||
videoId = value[2] as String?,
|
|
||||||
index = value[3] as Int?,
|
|
||||||
playlistSetVideoId = value[4] as String?,
|
|
||||||
watchEndpointMusicSupportedConfigs = null
|
|
||||||
)
|
|
||||||
}
|
|
|
@ -1,18 +0,0 @@
|
||||||
package it.vfsfitvnm.vimusic.savers
|
|
||||||
|
|
||||||
import androidx.compose.runtime.saveable.Saver
|
|
||||||
import androidx.compose.runtime.saveable.SaverScope
|
|
||||||
import it.vfsfitvnm.innertube.Innertube
|
|
||||||
import it.vfsfitvnm.innertube.models.NavigationEndpoint
|
|
||||||
|
|
||||||
object InnertubeWatchInfoSaver : Saver<Innertube.Info<NavigationEndpoint.Endpoint.Watch>, List<Any?>> {
|
|
||||||
override fun SaverScope.save(value: Innertube.Info<NavigationEndpoint.Endpoint.Watch>) = listOf(
|
|
||||||
value.name,
|
|
||||||
value.endpoint?.let { with(InnertubeWatchEndpointSaver) { save(it) } },
|
|
||||||
)
|
|
||||||
|
|
||||||
override fun restore(value: List<Any?>) = Innertube.Info(
|
|
||||||
name = value[0] as String?,
|
|
||||||
endpoint = (value[1] as List<Any?>?)?.let(InnertubeWatchEndpointSaver::restore)
|
|
||||||
)
|
|
||||||
}
|
|
|
@ -1,19 +0,0 @@
|
||||||
package it.vfsfitvnm.vimusic.savers
|
|
||||||
|
|
||||||
import androidx.compose.runtime.saveable.Saver
|
|
||||||
import androidx.compose.runtime.saveable.SaverScope
|
|
||||||
import it.vfsfitvnm.vimusic.models.PlaylistPreview
|
|
||||||
|
|
||||||
object PlaylistPreviewSaver : Saver<PlaylistPreview, List<Any>> {
|
|
||||||
override fun SaverScope.save(value: PlaylistPreview) = listOf(
|
|
||||||
with(PlaylistSaver) { save(value.playlist) },
|
|
||||||
value.songCount,
|
|
||||||
)
|
|
||||||
|
|
||||||
override fun restore(value: List<Any>) = PlaylistPreview(
|
|
||||||
playlist = PlaylistSaver.restore(value[0] as List<Any?>),
|
|
||||||
songCount = value[1] as Int,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
val PlaylistPreviewListSaver = listSaver(PlaylistPreviewSaver)
|
|
|
@ -1,19 +0,0 @@
|
||||||
package it.vfsfitvnm.vimusic.savers
|
|
||||||
|
|
||||||
import androidx.compose.runtime.saveable.Saver
|
|
||||||
import androidx.compose.runtime.saveable.SaverScope
|
|
||||||
import it.vfsfitvnm.vimusic.models.Playlist
|
|
||||||
|
|
||||||
object PlaylistSaver : Saver<Playlist, List<Any?>> {
|
|
||||||
override fun SaverScope.save(value: Playlist): List<Any?> = listOf(
|
|
||||||
value.id,
|
|
||||||
value.name,
|
|
||||||
value.browseId,
|
|
||||||
)
|
|
||||||
|
|
||||||
override fun restore(value: List<Any?>): Playlist = Playlist(
|
|
||||||
id = value[0] as Long,
|
|
||||||
name = value[1] as String,
|
|
||||||
browseId = value[2] as String?,
|
|
||||||
)
|
|
||||||
}
|
|
|
@ -1,18 +0,0 @@
|
||||||
package it.vfsfitvnm.vimusic.savers
|
|
||||||
|
|
||||||
import androidx.compose.runtime.saveable.Saver
|
|
||||||
import androidx.compose.runtime.saveable.SaverScope
|
|
||||||
import it.vfsfitvnm.vimusic.models.PlaylistWithSongs
|
|
||||||
|
|
||||||
object PlaylistWithSongsSaver : Saver<PlaylistWithSongs, List<Any>> {
|
|
||||||
override fun SaverScope.save(value: PlaylistWithSongs) = listOf(
|
|
||||||
with(PlaylistSaver) { save(value.playlist) },
|
|
||||||
with(DetailedSongListSaver) { save(value.songs) },
|
|
||||||
)
|
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
|
||||||
override fun restore(value: List<Any>): PlaylistWithSongs = PlaylistWithSongs(
|
|
||||||
playlist = PlaylistSaver.restore(value[0] as List<Any?>),
|
|
||||||
songs = DetailedSongListSaver.restore(value[1] as List<List<Any?>>)
|
|
||||||
)
|
|
||||||
}
|
|
|
@ -1,52 +0,0 @@
|
||||||
package it.vfsfitvnm.vimusic.savers
|
|
||||||
|
|
||||||
import androidx.compose.runtime.saveable.Saver
|
|
||||||
import androidx.compose.runtime.saveable.SaverScope
|
|
||||||
import it.vfsfitvnm.innertube.Innertube
|
|
||||||
|
|
||||||
interface ListSaver<Original, Saveable : Any> : Saver<List<Original>, List<Saveable>> {
|
|
||||||
override fun SaverScope.save(value: List<Original>): List<Saveable>
|
|
||||||
override fun restore(value: List<Saveable>): List<Original>
|
|
||||||
}
|
|
||||||
|
|
||||||
fun <Original, Saveable : Any> resultSaver(saver: Saver<Original, Saveable>) =
|
|
||||||
object : Saver<Result<Original>?, Pair<Saveable?, Throwable?>> {
|
|
||||||
override fun restore(value: Pair<Saveable?, Throwable?>) =
|
|
||||||
value.first?.let(saver::restore)?.let(Result.Companion::success)
|
|
||||||
?: value.second?.let(Result.Companion::failure)
|
|
||||||
|
|
||||||
override fun SaverScope.save(value: Result<Original>?) =
|
|
||||||
with(saver) { value?.getOrNull()?.let { save(it) } } to value?.exceptionOrNull()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun <Original, Saveable : Any> listSaver(saver: Saver<Original, Saveable>) =
|
|
||||||
object : ListSaver<Original, Saveable> {
|
|
||||||
override fun restore(value: List<Saveable>) =
|
|
||||||
value.mapNotNull(saver::restore)
|
|
||||||
|
|
||||||
override fun SaverScope.save(value: List<Original>) =
|
|
||||||
with(saver) { value.mapNotNull { save(it) } }
|
|
||||||
}
|
|
||||||
|
|
||||||
fun <Original, Saveable : Any> nullableSaver(saver: Saver<Original, Saveable>) =
|
|
||||||
object : Saver<Original?, Saveable> {
|
|
||||||
override fun SaverScope.save(value: Original?): Saveable? =
|
|
||||||
value?.let { with(saver) { save(it) } }
|
|
||||||
|
|
||||||
override fun restore(value: Saveable): Original? =
|
|
||||||
saver.restore(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun <Original : Innertube.Item> innertubeItemsPageSaver(saver: ListSaver<Original, List<Any?>>) =
|
|
||||||
object : Saver<Innertube.ItemsPage<Original>, List<Any?>> {
|
|
||||||
override fun SaverScope.save(value: Innertube.ItemsPage<Original>) = listOf(
|
|
||||||
value.items?.let { with(saver) { save(it) } },
|
|
||||||
value.continuation
|
|
||||||
)
|
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
|
||||||
override fun restore(value: List<Any?>) = Innertube.ItemsPage(
|
|
||||||
items = (value[0] as List<List<Any?>>?)?.let(saver::restore),
|
|
||||||
continuation = value[1] as String?
|
|
||||||
)
|
|
||||||
}
|
|
|
@ -1,17 +0,0 @@
|
||||||
package it.vfsfitvnm.vimusic.savers
|
|
||||||
|
|
||||||
import androidx.compose.runtime.saveable.Saver
|
|
||||||
import androidx.compose.runtime.saveable.SaverScope
|
|
||||||
import it.vfsfitvnm.vimusic.models.SearchQuery
|
|
||||||
|
|
||||||
object SearchQuerySaver : Saver<SearchQuery, List<Any?>> {
|
|
||||||
override fun SaverScope.save(value: SearchQuery): List<Any?> = listOf(
|
|
||||||
value.id,
|
|
||||||
value.query,
|
|
||||||
)
|
|
||||||
|
|
||||||
override fun restore(value: List<Any?>) = SearchQuery(
|
|
||||||
id = value[0] as Long,
|
|
||||||
query = value[1] as String
|
|
||||||
)
|
|
||||||
}
|
|
|
@ -963,7 +963,6 @@ class PlayerService : InvincibleService(), Player.Listener, PlaybackStatsListene
|
||||||
|
|
||||||
private class NotificationActionReceiver(private val player: Player) : BroadcastReceiver() {
|
private class NotificationActionReceiver(private val player: Player) : BroadcastReceiver() {
|
||||||
override fun onReceive(context: Context, intent: Intent) {
|
override fun onReceive(context: Context, intent: Intent) {
|
||||||
println(intent.action)
|
|
||||||
when (intent.action) {
|
when (intent.action) {
|
||||||
Action.pause.value -> player.pause()
|
Action.pause.value -> player.pause()
|
||||||
Action.play.value -> player.play()
|
Action.play.value -> player.play()
|
||||||
|
|
|
@ -17,17 +17,17 @@ import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import com.valentinilk.shimmer.shimmer
|
import com.valentinilk.shimmer.shimmer
|
||||||
|
import it.vfsfitvnm.compose.persist.PersistMapCleanup
|
||||||
|
import it.vfsfitvnm.compose.persist.persist
|
||||||
|
import it.vfsfitvnm.innertube.Innertube
|
||||||
|
import it.vfsfitvnm.innertube.models.bodies.BrowseBody
|
||||||
|
import it.vfsfitvnm.innertube.requests.albumPage
|
||||||
import it.vfsfitvnm.route.RouteHandler
|
import it.vfsfitvnm.route.RouteHandler
|
||||||
import it.vfsfitvnm.vimusic.Database
|
import it.vfsfitvnm.vimusic.Database
|
||||||
import it.vfsfitvnm.vimusic.R
|
import it.vfsfitvnm.vimusic.R
|
||||||
import it.vfsfitvnm.vimusic.models.Album
|
import it.vfsfitvnm.vimusic.models.Album
|
||||||
import it.vfsfitvnm.vimusic.models.SongAlbumMap
|
import it.vfsfitvnm.vimusic.models.SongAlbumMap
|
||||||
import it.vfsfitvnm.vimusic.query
|
import it.vfsfitvnm.vimusic.query
|
||||||
import it.vfsfitvnm.vimusic.savers.AlbumSaver
|
|
||||||
import it.vfsfitvnm.vimusic.savers.InnertubeAlbumItemListSaver
|
|
||||||
import it.vfsfitvnm.vimusic.savers.InnertubePlaylistOrAlbumPageSaver
|
|
||||||
import it.vfsfitvnm.vimusic.savers.innertubeItemsPageSaver
|
|
||||||
import it.vfsfitvnm.vimusic.savers.nullableSaver
|
|
||||||
import it.vfsfitvnm.vimusic.ui.components.themed.Header
|
import it.vfsfitvnm.vimusic.ui.components.themed.Header
|
||||||
import it.vfsfitvnm.vimusic.ui.components.themed.HeaderIconButton
|
import it.vfsfitvnm.vimusic.ui.components.themed.HeaderIconButton
|
||||||
import it.vfsfitvnm.vimusic.ui.components.themed.HeaderPlaceholder
|
import it.vfsfitvnm.vimusic.ui.components.themed.HeaderPlaceholder
|
||||||
|
@ -41,9 +41,6 @@ import it.vfsfitvnm.vimusic.ui.screens.searchresult.ItemsPage
|
||||||
import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance
|
import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance
|
||||||
import it.vfsfitvnm.vimusic.ui.styling.px
|
import it.vfsfitvnm.vimusic.ui.styling.px
|
||||||
import it.vfsfitvnm.vimusic.utils.asMediaItem
|
import it.vfsfitvnm.vimusic.utils.asMediaItem
|
||||||
import it.vfsfitvnm.innertube.Innertube
|
|
||||||
import it.vfsfitvnm.innertube.models.bodies.BrowseBody
|
|
||||||
import it.vfsfitvnm.innertube.requests.albumPage
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.flow.combine
|
import kotlinx.coroutines.flow.combine
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
@ -58,13 +55,10 @@ fun AlbumScreen(browseId: String) {
|
||||||
mutableStateOf(0)
|
mutableStateOf(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
var album by rememberSaveable(stateSaver = nullableSaver(AlbumSaver)) {
|
var album by persist<Album?>("album/$browseId/album")
|
||||||
mutableStateOf(null)
|
var albumPage by persist<Innertube.PlaylistOrAlbumPage?>("album/$browseId/albumPage")
|
||||||
}
|
|
||||||
|
|
||||||
var albumPage by rememberSaveable(stateSaver = nullableSaver(InnertubePlaylistOrAlbumPageSaver)) {
|
PersistMapCleanup(tagPrefix = "album/$browseId/")
|
||||||
mutableStateOf(null)
|
|
||||||
}
|
|
||||||
|
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
Database
|
Database
|
||||||
|
@ -205,7 +199,7 @@ fun AlbumScreen(browseId: String) {
|
||||||
val thumbnailSizePx = thumbnailSizeDp.px
|
val thumbnailSizePx = thumbnailSizeDp.px
|
||||||
|
|
||||||
ItemsPage(
|
ItemsPage(
|
||||||
stateSaver = innertubeItemsPageSaver(InnertubeAlbumItemListSaver),
|
tag = "album/$browseId/alternatives",
|
||||||
headerContent = headerContent,
|
headerContent = headerContent,
|
||||||
initialPlaceholderCount = 1,
|
initialPlaceholderCount = 1,
|
||||||
continuationPlaceholderCount = 1,
|
continuationPlaceholderCount = 1,
|
||||||
|
|
|
@ -6,26 +6,28 @@ import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.combinedClickable
|
import androidx.compose.foundation.combinedClickable
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.WindowInsetsSides
|
||||||
|
import androidx.compose.foundation.layout.asPaddingValues
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.only
|
||||||
import androidx.compose.foundation.layout.width
|
import androidx.compose.foundation.layout.width
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.lazy.itemsIndexed
|
import androidx.compose.foundation.lazy.itemsIndexed
|
||||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||||
import androidx.compose.foundation.text.BasicText
|
import androidx.compose.foundation.text.BasicText
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
|
import it.vfsfitvnm.compose.persist.persistList
|
||||||
import it.vfsfitvnm.vimusic.Database
|
import it.vfsfitvnm.vimusic.Database
|
||||||
import it.vfsfitvnm.vimusic.LocalPlayerAwareWindowInsets
|
import it.vfsfitvnm.vimusic.LocalPlayerAwareWindowInsets
|
||||||
import androidx.compose.foundation.layout.WindowInsetsSides
|
|
||||||
import androidx.compose.foundation.layout.asPaddingValues
|
|
||||||
import androidx.compose.foundation.layout.only
|
|
||||||
import it.vfsfitvnm.vimusic.LocalPlayerServiceBinder
|
import it.vfsfitvnm.vimusic.LocalPlayerServiceBinder
|
||||||
import it.vfsfitvnm.vimusic.R
|
import it.vfsfitvnm.vimusic.R
|
||||||
import it.vfsfitvnm.vimusic.models.DetailedSong
|
import it.vfsfitvnm.vimusic.models.DetailedSong
|
||||||
import it.vfsfitvnm.vimusic.savers.DetailedSongListSaver
|
|
||||||
import it.vfsfitvnm.vimusic.ui.components.LocalMenuState
|
import it.vfsfitvnm.vimusic.ui.components.LocalMenuState
|
||||||
import it.vfsfitvnm.vimusic.ui.components.ShimmerHost
|
import it.vfsfitvnm.vimusic.ui.components.ShimmerHost
|
||||||
import it.vfsfitvnm.vimusic.ui.components.themed.FloatingActionsContainerWithScrollToTop
|
import it.vfsfitvnm.vimusic.ui.components.themed.FloatingActionsContainerWithScrollToTop
|
||||||
|
@ -43,10 +45,7 @@ import it.vfsfitvnm.vimusic.utils.enqueue
|
||||||
import it.vfsfitvnm.vimusic.utils.forcePlayAtIndex
|
import it.vfsfitvnm.vimusic.utils.forcePlayAtIndex
|
||||||
import it.vfsfitvnm.vimusic.utils.forcePlayFromBeginning
|
import it.vfsfitvnm.vimusic.utils.forcePlayFromBeginning
|
||||||
import it.vfsfitvnm.vimusic.utils.isLandscape
|
import it.vfsfitvnm.vimusic.utils.isLandscape
|
||||||
import it.vfsfitvnm.vimusic.utils.produceSaveableState
|
|
||||||
import it.vfsfitvnm.vimusic.utils.semiBold
|
import it.vfsfitvnm.vimusic.utils.semiBold
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.flow.flowOn
|
|
||||||
|
|
||||||
@ExperimentalAnimationApi
|
@ExperimentalAnimationApi
|
||||||
@ExperimentalFoundationApi
|
@ExperimentalFoundationApi
|
||||||
|
@ -60,14 +59,10 @@ fun AlbumSongs(
|
||||||
val binder = LocalPlayerServiceBinder.current
|
val binder = LocalPlayerServiceBinder.current
|
||||||
val menuState = LocalMenuState.current
|
val menuState = LocalMenuState.current
|
||||||
|
|
||||||
val songs by produceSaveableState(
|
var songs by persistList<DetailedSong>("album/$browseId/songs")
|
||||||
initialValue = emptyList(),
|
|
||||||
stateSaver = DetailedSongListSaver
|
LaunchedEffect(Unit) {
|
||||||
) {
|
Database.albumSongs(browseId).collect { songs = it }
|
||||||
Database
|
|
||||||
.albumSongs(browseId)
|
|
||||||
.flowOn(Dispatchers.IO)
|
|
||||||
.collect { value = it }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val thumbnailSizeDp = Dimensions.thumbnails.song
|
val thumbnailSizeDp = Dimensions.thumbnails.song
|
||||||
|
|
|
@ -6,24 +6,25 @@ import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.combinedClickable
|
import androidx.compose.foundation.combinedClickable
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.WindowInsetsSides
|
||||||
|
import androidx.compose.foundation.layout.asPaddingValues
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.only
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.lazy.itemsIndexed
|
import androidx.compose.foundation.lazy.itemsIndexed
|
||||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import it.vfsfitvnm.compose.persist.persist
|
||||||
import it.vfsfitvnm.vimusic.Database
|
import it.vfsfitvnm.vimusic.Database
|
||||||
import it.vfsfitvnm.vimusic.LocalPlayerAwareWindowInsets
|
import it.vfsfitvnm.vimusic.LocalPlayerAwareWindowInsets
|
||||||
import androidx.compose.foundation.layout.WindowInsetsSides
|
|
||||||
import androidx.compose.foundation.layout.asPaddingValues
|
|
||||||
import androidx.compose.foundation.layout.only
|
|
||||||
import androidx.compose.ui.Alignment
|
|
||||||
import it.vfsfitvnm.vimusic.LocalPlayerServiceBinder
|
import it.vfsfitvnm.vimusic.LocalPlayerServiceBinder
|
||||||
import it.vfsfitvnm.vimusic.R
|
import it.vfsfitvnm.vimusic.R
|
||||||
import it.vfsfitvnm.vimusic.models.DetailedSong
|
import it.vfsfitvnm.vimusic.models.DetailedSong
|
||||||
import it.vfsfitvnm.vimusic.savers.DetailedSongListSaver
|
|
||||||
import it.vfsfitvnm.vimusic.savers.nullableSaver
|
|
||||||
import it.vfsfitvnm.vimusic.ui.components.LocalMenuState
|
import it.vfsfitvnm.vimusic.ui.components.LocalMenuState
|
||||||
import it.vfsfitvnm.vimusic.ui.components.ShimmerHost
|
import it.vfsfitvnm.vimusic.ui.components.ShimmerHost
|
||||||
import it.vfsfitvnm.vimusic.ui.components.themed.FloatingActionsContainerWithScrollToTop
|
import it.vfsfitvnm.vimusic.ui.components.themed.FloatingActionsContainerWithScrollToTop
|
||||||
|
@ -39,9 +40,6 @@ import it.vfsfitvnm.vimusic.utils.asMediaItem
|
||||||
import it.vfsfitvnm.vimusic.utils.enqueue
|
import it.vfsfitvnm.vimusic.utils.enqueue
|
||||||
import it.vfsfitvnm.vimusic.utils.forcePlayAtIndex
|
import it.vfsfitvnm.vimusic.utils.forcePlayAtIndex
|
||||||
import it.vfsfitvnm.vimusic.utils.forcePlayFromBeginning
|
import it.vfsfitvnm.vimusic.utils.forcePlayFromBeginning
|
||||||
import it.vfsfitvnm.vimusic.utils.produceSaveableState
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.flow.flowOn
|
|
||||||
|
|
||||||
@ExperimentalFoundationApi
|
@ExperimentalFoundationApi
|
||||||
@ExperimentalAnimationApi
|
@ExperimentalAnimationApi
|
||||||
|
@ -55,14 +53,10 @@ fun ArtistLocalSongs(
|
||||||
val (colorPalette) = LocalAppearance.current
|
val (colorPalette) = LocalAppearance.current
|
||||||
val menuState = LocalMenuState.current
|
val menuState = LocalMenuState.current
|
||||||
|
|
||||||
val songs by produceSaveableState(
|
var songs by persist<List<DetailedSong>?>("artist/$browseId/localSongs")
|
||||||
initialValue = null,
|
|
||||||
stateSaver = nullableSaver(DetailedSongListSaver)
|
LaunchedEffect(Unit) {
|
||||||
) {
|
Database.artistSongs(browseId).collect { songs = it }
|
||||||
Database
|
|
||||||
.artistSongs(browseId)
|
|
||||||
.flowOn(Dispatchers.IO)
|
|
||||||
.collect { value = it }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val songThumbnailSizeDp = Dimensions.thumbnails.song
|
val songThumbnailSizeDp = Dimensions.thumbnails.song
|
||||||
|
|
|
@ -26,6 +26,8 @@ import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
import it.vfsfitvnm.innertube.Innertube
|
||||||
|
import it.vfsfitvnm.innertube.models.NavigationEndpoint
|
||||||
import it.vfsfitvnm.vimusic.LocalPlayerAwareWindowInsets
|
import it.vfsfitvnm.vimusic.LocalPlayerAwareWindowInsets
|
||||||
import it.vfsfitvnm.vimusic.LocalPlayerServiceBinder
|
import it.vfsfitvnm.vimusic.LocalPlayerServiceBinder
|
||||||
import it.vfsfitvnm.vimusic.R
|
import it.vfsfitvnm.vimusic.R
|
||||||
|
@ -49,8 +51,6 @@ import it.vfsfitvnm.vimusic.utils.color
|
||||||
import it.vfsfitvnm.vimusic.utils.forcePlay
|
import it.vfsfitvnm.vimusic.utils.forcePlay
|
||||||
import it.vfsfitvnm.vimusic.utils.secondary
|
import it.vfsfitvnm.vimusic.utils.secondary
|
||||||
import it.vfsfitvnm.vimusic.utils.semiBold
|
import it.vfsfitvnm.vimusic.utils.semiBold
|
||||||
import it.vfsfitvnm.innertube.Innertube
|
|
||||||
import it.vfsfitvnm.innertube.models.NavigationEndpoint
|
|
||||||
|
|
||||||
@ExperimentalFoundationApi
|
@ExperimentalFoundationApi
|
||||||
@ExperimentalAnimationApi
|
@ExperimentalAnimationApi
|
||||||
|
|
|
@ -10,8 +10,6 @@ import androidx.compose.foundation.shape.CircleShape
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
|
||||||
import androidx.compose.runtime.saveable.rememberSaveable
|
|
||||||
import androidx.compose.runtime.saveable.rememberSaveableStateHolder
|
import androidx.compose.runtime.saveable.rememberSaveableStateHolder
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.runtime.snapshotFlow
|
import androidx.compose.runtime.snapshotFlow
|
||||||
|
@ -19,17 +17,20 @@ import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import com.valentinilk.shimmer.shimmer
|
import com.valentinilk.shimmer.shimmer
|
||||||
|
import it.vfsfitvnm.compose.persist.PersistMapCleanup
|
||||||
|
import it.vfsfitvnm.compose.persist.persist
|
||||||
|
import it.vfsfitvnm.innertube.Innertube
|
||||||
|
import it.vfsfitvnm.innertube.models.bodies.BrowseBody
|
||||||
|
import it.vfsfitvnm.innertube.models.bodies.ContinuationBody
|
||||||
|
import it.vfsfitvnm.innertube.requests.artistPage
|
||||||
|
import it.vfsfitvnm.innertube.requests.itemsPage
|
||||||
|
import it.vfsfitvnm.innertube.utils.from
|
||||||
import it.vfsfitvnm.route.RouteHandler
|
import it.vfsfitvnm.route.RouteHandler
|
||||||
import it.vfsfitvnm.vimusic.Database
|
import it.vfsfitvnm.vimusic.Database
|
||||||
import it.vfsfitvnm.vimusic.LocalPlayerServiceBinder
|
import it.vfsfitvnm.vimusic.LocalPlayerServiceBinder
|
||||||
import it.vfsfitvnm.vimusic.R
|
import it.vfsfitvnm.vimusic.R
|
||||||
import it.vfsfitvnm.vimusic.models.Artist
|
import it.vfsfitvnm.vimusic.models.Artist
|
||||||
import it.vfsfitvnm.vimusic.query
|
import it.vfsfitvnm.vimusic.query
|
||||||
import it.vfsfitvnm.vimusic.savers.ArtistSaver
|
|
||||||
import it.vfsfitvnm.vimusic.savers.InnertubeAlbumsPageSaver
|
|
||||||
import it.vfsfitvnm.vimusic.savers.InnertubeArtistPageSaver
|
|
||||||
import it.vfsfitvnm.vimusic.savers.InnertubeSongsPageSaver
|
|
||||||
import it.vfsfitvnm.vimusic.savers.nullableSaver
|
|
||||||
import it.vfsfitvnm.vimusic.ui.components.LocalMenuState
|
import it.vfsfitvnm.vimusic.ui.components.LocalMenuState
|
||||||
import it.vfsfitvnm.vimusic.ui.components.themed.Header
|
import it.vfsfitvnm.vimusic.ui.components.themed.Header
|
||||||
import it.vfsfitvnm.vimusic.ui.components.themed.HeaderIconButton
|
import it.vfsfitvnm.vimusic.ui.components.themed.HeaderIconButton
|
||||||
|
@ -51,12 +52,6 @@ import it.vfsfitvnm.vimusic.utils.artistScreenTabIndexKey
|
||||||
import it.vfsfitvnm.vimusic.utils.asMediaItem
|
import it.vfsfitvnm.vimusic.utils.asMediaItem
|
||||||
import it.vfsfitvnm.vimusic.utils.forcePlay
|
import it.vfsfitvnm.vimusic.utils.forcePlay
|
||||||
import it.vfsfitvnm.vimusic.utils.rememberPreference
|
import it.vfsfitvnm.vimusic.utils.rememberPreference
|
||||||
import it.vfsfitvnm.innertube.Innertube
|
|
||||||
import it.vfsfitvnm.innertube.models.bodies.BrowseBody
|
|
||||||
import it.vfsfitvnm.innertube.models.bodies.ContinuationBody
|
|
||||||
import it.vfsfitvnm.innertube.requests.artistPage
|
|
||||||
import it.vfsfitvnm.innertube.requests.itemsPage
|
|
||||||
import it.vfsfitvnm.innertube.utils.from
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.flow.combine
|
import kotlinx.coroutines.flow.combine
|
||||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||||
|
@ -71,13 +66,11 @@ fun ArtistScreen(browseId: String) {
|
||||||
|
|
||||||
var tabIndex by rememberPreference(artistScreenTabIndexKey, defaultValue = 0)
|
var tabIndex by rememberPreference(artistScreenTabIndexKey, defaultValue = 0)
|
||||||
|
|
||||||
var artist by rememberSaveable(stateSaver = nullableSaver(ArtistSaver)) {
|
PersistMapCleanup(tagPrefix = "artist/$browseId/")
|
||||||
mutableStateOf(null)
|
|
||||||
}
|
|
||||||
|
|
||||||
var artistPage by rememberSaveable(stateSaver = nullableSaver(InnertubeArtistPageSaver)) {
|
var artist by persist<Artist?>("artist/$browseId/artist")
|
||||||
mutableStateOf(null)
|
|
||||||
}
|
var artistPage by persist<Innertube.ArtistPage?>("artist/$browseId/artistPage")
|
||||||
|
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
Database
|
Database
|
||||||
|
@ -209,7 +202,7 @@ fun ArtistScreen(browseId: String) {
|
||||||
val thumbnailSizePx = thumbnailSizeDp.px
|
val thumbnailSizePx = thumbnailSizeDp.px
|
||||||
|
|
||||||
ItemsPage(
|
ItemsPage(
|
||||||
stateSaver = InnertubeSongsPageSaver,
|
tag = "artist/$browseId/songs",
|
||||||
headerContent = headerContent,
|
headerContent = headerContent,
|
||||||
itemsPageProvider = artistPage?.let {
|
itemsPageProvider = artistPage?.let {
|
||||||
({ continuation ->
|
({ continuation ->
|
||||||
|
@ -272,7 +265,7 @@ fun ArtistScreen(browseId: String) {
|
||||||
val thumbnailSizePx = thumbnailSizeDp.px
|
val thumbnailSizePx = thumbnailSizeDp.px
|
||||||
|
|
||||||
ItemsPage(
|
ItemsPage(
|
||||||
stateSaver = InnertubeAlbumsPageSaver,
|
tag = "artist/$browseId/albums",
|
||||||
headerContent = headerContent,
|
headerContent = headerContent,
|
||||||
emptyItemsText = "This artist didn't release any album",
|
emptyItemsText = "This artist didn't release any album",
|
||||||
itemsPageProvider = artistPage?.let {
|
itemsPageProvider = artistPage?.let {
|
||||||
|
@ -322,7 +315,7 @@ fun ArtistScreen(browseId: String) {
|
||||||
val thumbnailSizePx = thumbnailSizeDp.px
|
val thumbnailSizePx = thumbnailSizeDp.px
|
||||||
|
|
||||||
ItemsPage(
|
ItemsPage(
|
||||||
stateSaver = InnertubeAlbumsPageSaver,
|
tag = "artist/$browseId/singles",
|
||||||
headerContent = headerContent,
|
headerContent = headerContent,
|
||||||
emptyItemsText = "This artist didn't release any single",
|
emptyItemsText = "This artist didn't release any single",
|
||||||
itemsPageProvider = artistPage?.let {
|
itemsPageProvider = artistPage?.let {
|
||||||
|
|
|
@ -6,6 +6,7 @@ import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.saveable.rememberSaveable
|
import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
import androidx.compose.runtime.saveable.rememberSaveableStateHolder
|
import androidx.compose.runtime.saveable.rememberSaveableStateHolder
|
||||||
|
import it.vfsfitvnm.compose.persist.PersistMapCleanup
|
||||||
import it.vfsfitvnm.route.RouteHandler
|
import it.vfsfitvnm.route.RouteHandler
|
||||||
import it.vfsfitvnm.vimusic.R
|
import it.vfsfitvnm.vimusic.R
|
||||||
import it.vfsfitvnm.vimusic.enums.BuiltInPlaylist
|
import it.vfsfitvnm.vimusic.enums.BuiltInPlaylist
|
||||||
|
@ -25,6 +26,8 @@ fun BuiltInPlaylistScreen(builtInPlaylist: BuiltInPlaylist) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PersistMapCleanup(tagPrefix = "${builtInPlaylist.name}/")
|
||||||
|
|
||||||
RouteHandler(listenToGlobalEmitter = true) {
|
RouteHandler(listenToGlobalEmitter = true) {
|
||||||
globalRoutes()
|
globalRoutes()
|
||||||
|
|
||||||
|
|
|
@ -15,16 +15,18 @@ import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.lazy.itemsIndexed
|
import androidx.compose.foundation.lazy.itemsIndexed
|
||||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
import it.vfsfitvnm.compose.persist.persistList
|
||||||
import it.vfsfitvnm.vimusic.Database
|
import it.vfsfitvnm.vimusic.Database
|
||||||
import it.vfsfitvnm.vimusic.LocalPlayerAwareWindowInsets
|
import it.vfsfitvnm.vimusic.LocalPlayerAwareWindowInsets
|
||||||
import it.vfsfitvnm.vimusic.LocalPlayerServiceBinder
|
import it.vfsfitvnm.vimusic.LocalPlayerServiceBinder
|
||||||
import it.vfsfitvnm.vimusic.R
|
import it.vfsfitvnm.vimusic.R
|
||||||
import it.vfsfitvnm.vimusic.enums.BuiltInPlaylist
|
import it.vfsfitvnm.vimusic.enums.BuiltInPlaylist
|
||||||
import it.vfsfitvnm.vimusic.models.DetailedSong
|
import it.vfsfitvnm.vimusic.models.DetailedSong
|
||||||
import it.vfsfitvnm.vimusic.savers.DetailedSongListSaver
|
|
||||||
import it.vfsfitvnm.vimusic.ui.components.LocalMenuState
|
import it.vfsfitvnm.vimusic.ui.components.LocalMenuState
|
||||||
import it.vfsfitvnm.vimusic.ui.components.themed.FloatingActionsContainerWithScrollToTop
|
import it.vfsfitvnm.vimusic.ui.components.themed.FloatingActionsContainerWithScrollToTop
|
||||||
import it.vfsfitvnm.vimusic.ui.components.themed.Header
|
import it.vfsfitvnm.vimusic.ui.components.themed.Header
|
||||||
|
@ -39,7 +41,6 @@ import it.vfsfitvnm.vimusic.utils.asMediaItem
|
||||||
import it.vfsfitvnm.vimusic.utils.enqueue
|
import it.vfsfitvnm.vimusic.utils.enqueue
|
||||||
import it.vfsfitvnm.vimusic.utils.forcePlayAtIndex
|
import it.vfsfitvnm.vimusic.utils.forcePlayAtIndex
|
||||||
import it.vfsfitvnm.vimusic.utils.forcePlayFromBeginning
|
import it.vfsfitvnm.vimusic.utils.forcePlayFromBeginning
|
||||||
import it.vfsfitvnm.vimusic.utils.produceSaveableState
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.flow.flowOn
|
import kotlinx.coroutines.flow.flowOn
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
|
@ -52,25 +53,24 @@ fun BuiltInPlaylistSongs(builtInPlaylist: BuiltInPlaylist) {
|
||||||
val binder = LocalPlayerServiceBinder.current
|
val binder = LocalPlayerServiceBinder.current
|
||||||
val menuState = LocalMenuState.current
|
val menuState = LocalMenuState.current
|
||||||
|
|
||||||
val songs by produceSaveableState(
|
var songs by persistList<DetailedSong>("${builtInPlaylist.name}/songs")
|
||||||
initialValue = emptyList(),
|
|
||||||
stateSaver = DetailedSongListSaver
|
LaunchedEffect(Unit) {
|
||||||
) {
|
|
||||||
when (builtInPlaylist) {
|
when (builtInPlaylist) {
|
||||||
BuiltInPlaylist.Favorites -> Database
|
BuiltInPlaylist.Favorites -> Database
|
||||||
.favorites()
|
.favorites()
|
||||||
.flowOn(Dispatchers.IO)
|
|
||||||
BuiltInPlaylist.Offline -> Database
|
BuiltInPlaylist.Offline -> Database
|
||||||
.songsWithContentLength()
|
.songsWithContentLength()
|
||||||
.flowOn(Dispatchers.IO)
|
.flowOn(Dispatchers.IO)
|
||||||
.map { songs ->
|
.map { songs ->
|
||||||
songs.filter { song ->
|
songs.filter { song ->
|
||||||
song.contentLength?.let {
|
song.contentLength?.let {
|
||||||
binder?.cache?.isCached(song.id, 0, song.contentLength)
|
binder?.cache?.isCached(song.id, 0, song.contentLength)
|
||||||
} ?: false
|
} ?: false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}.collect { songs = it }
|
||||||
}.collect { value = it }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val thumbnailSizeDp = Dimensions.thumbnails.song
|
val thumbnailSizeDp = Dimensions.thumbnails.song
|
||||||
|
|
|
@ -15,21 +15,24 @@ import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.only
|
import androidx.compose.foundation.layout.only
|
||||||
import androidx.compose.foundation.layout.width
|
import androidx.compose.foundation.layout.width
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
|
import androidx.compose.foundation.lazy.LazyListState
|
||||||
import androidx.compose.foundation.lazy.items
|
import androidx.compose.foundation.lazy.items
|
||||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.graphicsLayer
|
import androidx.compose.ui.graphics.graphicsLayer
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
import it.vfsfitvnm.compose.persist.persist
|
||||||
import it.vfsfitvnm.vimusic.Database
|
import it.vfsfitvnm.vimusic.Database
|
||||||
import it.vfsfitvnm.vimusic.LocalPlayerAwareWindowInsets
|
import it.vfsfitvnm.vimusic.LocalPlayerAwareWindowInsets
|
||||||
import it.vfsfitvnm.vimusic.R
|
import it.vfsfitvnm.vimusic.R
|
||||||
import it.vfsfitvnm.vimusic.enums.AlbumSortBy
|
import it.vfsfitvnm.vimusic.enums.AlbumSortBy
|
||||||
import it.vfsfitvnm.vimusic.enums.SortOrder
|
import it.vfsfitvnm.vimusic.enums.SortOrder
|
||||||
import it.vfsfitvnm.vimusic.models.Album
|
import it.vfsfitvnm.vimusic.models.Album
|
||||||
import it.vfsfitvnm.vimusic.savers.AlbumListSaver
|
|
||||||
import it.vfsfitvnm.vimusic.ui.components.themed.FloatingActionsContainerWithScrollToTop
|
import it.vfsfitvnm.vimusic.ui.components.themed.FloatingActionsContainerWithScrollToTop
|
||||||
import it.vfsfitvnm.vimusic.ui.components.themed.Header
|
import it.vfsfitvnm.vimusic.ui.components.themed.Header
|
||||||
import it.vfsfitvnm.vimusic.ui.components.themed.HeaderIconButton
|
import it.vfsfitvnm.vimusic.ui.components.themed.HeaderIconButton
|
||||||
|
@ -39,10 +42,7 @@ import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance
|
||||||
import it.vfsfitvnm.vimusic.ui.styling.px
|
import it.vfsfitvnm.vimusic.ui.styling.px
|
||||||
import it.vfsfitvnm.vimusic.utils.albumSortByKey
|
import it.vfsfitvnm.vimusic.utils.albumSortByKey
|
||||||
import it.vfsfitvnm.vimusic.utils.albumSortOrderKey
|
import it.vfsfitvnm.vimusic.utils.albumSortOrderKey
|
||||||
import it.vfsfitvnm.vimusic.utils.produceSaveableState
|
|
||||||
import it.vfsfitvnm.vimusic.utils.rememberPreference
|
import it.vfsfitvnm.vimusic.utils.rememberPreference
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.flow.flowOn
|
|
||||||
|
|
||||||
@ExperimentalFoundationApi
|
@ExperimentalFoundationApi
|
||||||
@ExperimentalAnimationApi
|
@ExperimentalAnimationApi
|
||||||
|
@ -56,15 +56,10 @@ fun HomeAlbums(
|
||||||
var sortBy by rememberPreference(albumSortByKey, AlbumSortBy.DateAdded)
|
var sortBy by rememberPreference(albumSortByKey, AlbumSortBy.DateAdded)
|
||||||
var sortOrder by rememberPreference(albumSortOrderKey, SortOrder.Descending)
|
var sortOrder by rememberPreference(albumSortOrderKey, SortOrder.Descending)
|
||||||
|
|
||||||
val items by produceSaveableState(
|
var items by persist<List<Album>>(tag = "home/albums", emptyList())
|
||||||
initialValue = emptyList(),
|
|
||||||
stateSaver = AlbumListSaver,
|
LaunchedEffect(sortBy, sortOrder) {
|
||||||
sortBy, sortOrder,
|
Database.albums(sortBy, sortOrder).collect { items = it }
|
||||||
) {
|
|
||||||
Database
|
|
||||||
.albums(sortBy, sortOrder)
|
|
||||||
.flowOn(Dispatchers.IO)
|
|
||||||
.collect { value = it }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val thumbnailSizeDp = Dimensions.thumbnails.song * 2
|
val thumbnailSizeDp = Dimensions.thumbnails.song * 2
|
||||||
|
|
|
@ -10,7 +10,10 @@ import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.WindowInsetsSides
|
||||||
|
import androidx.compose.foundation.layout.asPaddingValues
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.only
|
||||||
import androidx.compose.foundation.layout.width
|
import androidx.compose.foundation.layout.width
|
||||||
import androidx.compose.foundation.lazy.grid.GridCells
|
import androidx.compose.foundation.lazy.grid.GridCells
|
||||||
import androidx.compose.foundation.lazy.grid.GridItemSpan
|
import androidx.compose.foundation.lazy.grid.GridItemSpan
|
||||||
|
@ -18,22 +21,20 @@ import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
|
||||||
import androidx.compose.foundation.lazy.grid.items
|
import androidx.compose.foundation.lazy.grid.items
|
||||||
import androidx.compose.foundation.lazy.grid.rememberLazyGridState
|
import androidx.compose.foundation.lazy.grid.rememberLazyGridState
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.graphicsLayer
|
import androidx.compose.ui.graphics.graphicsLayer
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
import it.vfsfitvnm.compose.persist.persistList
|
||||||
import it.vfsfitvnm.vimusic.Database
|
import it.vfsfitvnm.vimusic.Database
|
||||||
import it.vfsfitvnm.vimusic.LocalPlayerAwareWindowInsets
|
import it.vfsfitvnm.vimusic.LocalPlayerAwareWindowInsets
|
||||||
import androidx.compose.foundation.layout.WindowInsetsSides
|
|
||||||
import androidx.compose.foundation.layout.asPaddingValues
|
|
||||||
import androidx.compose.foundation.layout.only
|
|
||||||
import it.vfsfitvnm.vimusic.R
|
import it.vfsfitvnm.vimusic.R
|
||||||
import it.vfsfitvnm.vimusic.enums.ArtistSortBy
|
import it.vfsfitvnm.vimusic.enums.ArtistSortBy
|
||||||
import it.vfsfitvnm.vimusic.enums.SortOrder
|
import it.vfsfitvnm.vimusic.enums.SortOrder
|
||||||
import it.vfsfitvnm.vimusic.models.Artist
|
import it.vfsfitvnm.vimusic.models.Artist
|
||||||
import it.vfsfitvnm.vimusic.savers.ArtistListSaver
|
|
||||||
import it.vfsfitvnm.vimusic.ui.components.themed.FloatingActionsContainerWithScrollToTop
|
import it.vfsfitvnm.vimusic.ui.components.themed.FloatingActionsContainerWithScrollToTop
|
||||||
import it.vfsfitvnm.vimusic.ui.components.themed.Header
|
import it.vfsfitvnm.vimusic.ui.components.themed.Header
|
||||||
import it.vfsfitvnm.vimusic.ui.components.themed.HeaderIconButton
|
import it.vfsfitvnm.vimusic.ui.components.themed.HeaderIconButton
|
||||||
|
@ -43,10 +44,7 @@ import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance
|
||||||
import it.vfsfitvnm.vimusic.ui.styling.px
|
import it.vfsfitvnm.vimusic.ui.styling.px
|
||||||
import it.vfsfitvnm.vimusic.utils.artistSortByKey
|
import it.vfsfitvnm.vimusic.utils.artistSortByKey
|
||||||
import it.vfsfitvnm.vimusic.utils.artistSortOrderKey
|
import it.vfsfitvnm.vimusic.utils.artistSortOrderKey
|
||||||
import it.vfsfitvnm.vimusic.utils.produceSaveableState
|
|
||||||
import it.vfsfitvnm.vimusic.utils.rememberPreference
|
import it.vfsfitvnm.vimusic.utils.rememberPreference
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.flow.flowOn
|
|
||||||
|
|
||||||
@ExperimentalFoundationApi
|
@ExperimentalFoundationApi
|
||||||
@ExperimentalAnimationApi
|
@ExperimentalAnimationApi
|
||||||
|
@ -60,15 +58,10 @@ fun HomeArtistList(
|
||||||
var sortBy by rememberPreference(artistSortByKey, ArtistSortBy.DateAdded)
|
var sortBy by rememberPreference(artistSortByKey, ArtistSortBy.DateAdded)
|
||||||
var sortOrder by rememberPreference(artistSortOrderKey, SortOrder.Descending)
|
var sortOrder by rememberPreference(artistSortOrderKey, SortOrder.Descending)
|
||||||
|
|
||||||
val items by produceSaveableState(
|
var items by persistList<Artist>("home/artists")
|
||||||
initialValue = emptyList(),
|
|
||||||
stateSaver = ArtistListSaver,
|
LaunchedEffect(sortBy, sortOrder) {
|
||||||
sortBy, sortOrder,
|
Database.artists(sortBy, sortOrder).collect { items = it }
|
||||||
) {
|
|
||||||
Database
|
|
||||||
.artists(sortBy, sortOrder)
|
|
||||||
.flowOn(Dispatchers.IO)
|
|
||||||
.collect { value = it }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val thumbnailSizeDp = Dimensions.thumbnails.song * 2
|
val thumbnailSizeDp = Dimensions.thumbnails.song * 2
|
||||||
|
|
|
@ -21,6 +21,7 @@ import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
|
||||||
import androidx.compose.foundation.lazy.grid.items
|
import androidx.compose.foundation.lazy.grid.items
|
||||||
import androidx.compose.foundation.lazy.grid.rememberLazyGridState
|
import androidx.compose.foundation.lazy.grid.rememberLazyGridState
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.saveable.rememberSaveable
|
import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
|
@ -29,6 +30,7 @@ import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.graphicsLayer
|
import androidx.compose.ui.graphics.graphicsLayer
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
import it.vfsfitvnm.compose.persist.persistList
|
||||||
import it.vfsfitvnm.vimusic.Database
|
import it.vfsfitvnm.vimusic.Database
|
||||||
import it.vfsfitvnm.vimusic.LocalPlayerAwareWindowInsets
|
import it.vfsfitvnm.vimusic.LocalPlayerAwareWindowInsets
|
||||||
import it.vfsfitvnm.vimusic.R
|
import it.vfsfitvnm.vimusic.R
|
||||||
|
@ -36,8 +38,8 @@ import it.vfsfitvnm.vimusic.enums.BuiltInPlaylist
|
||||||
import it.vfsfitvnm.vimusic.enums.PlaylistSortBy
|
import it.vfsfitvnm.vimusic.enums.PlaylistSortBy
|
||||||
import it.vfsfitvnm.vimusic.enums.SortOrder
|
import it.vfsfitvnm.vimusic.enums.SortOrder
|
||||||
import it.vfsfitvnm.vimusic.models.Playlist
|
import it.vfsfitvnm.vimusic.models.Playlist
|
||||||
|
import it.vfsfitvnm.vimusic.models.PlaylistPreview
|
||||||
import it.vfsfitvnm.vimusic.query
|
import it.vfsfitvnm.vimusic.query
|
||||||
import it.vfsfitvnm.vimusic.savers.PlaylistPreviewListSaver
|
|
||||||
import it.vfsfitvnm.vimusic.ui.components.themed.FloatingActionsContainerWithScrollToTop
|
import it.vfsfitvnm.vimusic.ui.components.themed.FloatingActionsContainerWithScrollToTop
|
||||||
import it.vfsfitvnm.vimusic.ui.components.themed.Header
|
import it.vfsfitvnm.vimusic.ui.components.themed.Header
|
||||||
import it.vfsfitvnm.vimusic.ui.components.themed.HeaderIconButton
|
import it.vfsfitvnm.vimusic.ui.components.themed.HeaderIconButton
|
||||||
|
@ -49,10 +51,7 @@ import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance
|
||||||
import it.vfsfitvnm.vimusic.ui.styling.px
|
import it.vfsfitvnm.vimusic.ui.styling.px
|
||||||
import it.vfsfitvnm.vimusic.utils.playlistSortByKey
|
import it.vfsfitvnm.vimusic.utils.playlistSortByKey
|
||||||
import it.vfsfitvnm.vimusic.utils.playlistSortOrderKey
|
import it.vfsfitvnm.vimusic.utils.playlistSortOrderKey
|
||||||
import it.vfsfitvnm.vimusic.utils.produceSaveableState
|
|
||||||
import it.vfsfitvnm.vimusic.utils.rememberPreference
|
import it.vfsfitvnm.vimusic.utils.rememberPreference
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.flow.flowOn
|
|
||||||
|
|
||||||
@ExperimentalAnimationApi
|
@ExperimentalAnimationApi
|
||||||
@ExperimentalFoundationApi
|
@ExperimentalFoundationApi
|
||||||
|
@ -85,15 +84,10 @@ fun HomePlaylists(
|
||||||
var sortBy by rememberPreference(playlistSortByKey, PlaylistSortBy.DateAdded)
|
var sortBy by rememberPreference(playlistSortByKey, PlaylistSortBy.DateAdded)
|
||||||
var sortOrder by rememberPreference(playlistSortOrderKey, SortOrder.Descending)
|
var sortOrder by rememberPreference(playlistSortOrderKey, SortOrder.Descending)
|
||||||
|
|
||||||
val items by produceSaveableState(
|
var items by persistList<PlaylistPreview>("home/playlists")
|
||||||
initialValue = emptyList(),
|
|
||||||
stateSaver = PlaylistPreviewListSaver,
|
LaunchedEffect(sortBy, sortOrder) {
|
||||||
sortBy, sortOrder,
|
Database.playlistPreviews(sortBy, sortOrder).collect { items = it }
|
||||||
) {
|
|
||||||
Database
|
|
||||||
.playlistPreviews(sortBy, sortOrder)
|
|
||||||
.flowOn(Dispatchers.IO)
|
|
||||||
.collect { value = it }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val sortOrderIconRotation by animateFloatAsState(
|
val sortOrderIconRotation by animateFloatAsState(
|
||||||
|
|
|
@ -5,6 +5,7 @@ import androidx.compose.foundation.ExperimentalFoundationApi
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.saveable.rememberSaveableStateHolder
|
import androidx.compose.runtime.saveable.rememberSaveableStateHolder
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import it.vfsfitvnm.compose.persist.PersistMapCleanup
|
||||||
import it.vfsfitvnm.route.RouteHandler
|
import it.vfsfitvnm.route.RouteHandler
|
||||||
import it.vfsfitvnm.route.defaultStacking
|
import it.vfsfitvnm.route.defaultStacking
|
||||||
import it.vfsfitvnm.route.defaultStill
|
import it.vfsfitvnm.route.defaultStill
|
||||||
|
@ -42,6 +43,8 @@ import it.vfsfitvnm.vimusic.utils.rememberPreference
|
||||||
fun HomeScreen(onPlaylistUrl: (String) -> Unit) {
|
fun HomeScreen(onPlaylistUrl: (String) -> Unit) {
|
||||||
val saveableStateHolder = rememberSaveableStateHolder()
|
val saveableStateHolder = rememberSaveableStateHolder()
|
||||||
|
|
||||||
|
PersistMapCleanup("home/")
|
||||||
|
|
||||||
RouteHandler(
|
RouteHandler(
|
||||||
listenToGlobalEmitter = true,
|
listenToGlobalEmitter = true,
|
||||||
transitionSpec = {
|
transitionSpec = {
|
||||||
|
|
|
@ -21,6 +21,7 @@ import androidx.compose.foundation.lazy.itemsIndexed
|
||||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||||
import androidx.compose.foundation.text.BasicText
|
import androidx.compose.foundation.text.BasicText
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
|
@ -30,6 +31,7 @@ import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.graphics.graphicsLayer
|
import androidx.compose.ui.graphics.graphicsLayer
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
import it.vfsfitvnm.compose.persist.persistList
|
||||||
import it.vfsfitvnm.vimusic.Database
|
import it.vfsfitvnm.vimusic.Database
|
||||||
import it.vfsfitvnm.vimusic.LocalPlayerAwareWindowInsets
|
import it.vfsfitvnm.vimusic.LocalPlayerAwareWindowInsets
|
||||||
import it.vfsfitvnm.vimusic.LocalPlayerServiceBinder
|
import it.vfsfitvnm.vimusic.LocalPlayerServiceBinder
|
||||||
|
@ -37,7 +39,6 @@ import it.vfsfitvnm.vimusic.R
|
||||||
import it.vfsfitvnm.vimusic.enums.SongSortBy
|
import it.vfsfitvnm.vimusic.enums.SongSortBy
|
||||||
import it.vfsfitvnm.vimusic.enums.SortOrder
|
import it.vfsfitvnm.vimusic.enums.SortOrder
|
||||||
import it.vfsfitvnm.vimusic.models.DetailedSong
|
import it.vfsfitvnm.vimusic.models.DetailedSong
|
||||||
import it.vfsfitvnm.vimusic.savers.DetailedSongListSaver
|
|
||||||
import it.vfsfitvnm.vimusic.ui.components.LocalMenuState
|
import it.vfsfitvnm.vimusic.ui.components.LocalMenuState
|
||||||
import it.vfsfitvnm.vimusic.ui.components.themed.FloatingActionsContainerWithScrollToTop
|
import it.vfsfitvnm.vimusic.ui.components.themed.FloatingActionsContainerWithScrollToTop
|
||||||
import it.vfsfitvnm.vimusic.ui.components.themed.Header
|
import it.vfsfitvnm.vimusic.ui.components.themed.Header
|
||||||
|
@ -53,13 +54,10 @@ import it.vfsfitvnm.vimusic.utils.asMediaItem
|
||||||
import it.vfsfitvnm.vimusic.utils.center
|
import it.vfsfitvnm.vimusic.utils.center
|
||||||
import it.vfsfitvnm.vimusic.utils.color
|
import it.vfsfitvnm.vimusic.utils.color
|
||||||
import it.vfsfitvnm.vimusic.utils.forcePlayAtIndex
|
import it.vfsfitvnm.vimusic.utils.forcePlayAtIndex
|
||||||
import it.vfsfitvnm.vimusic.utils.produceSaveableState
|
|
||||||
import it.vfsfitvnm.vimusic.utils.rememberPreference
|
import it.vfsfitvnm.vimusic.utils.rememberPreference
|
||||||
import it.vfsfitvnm.vimusic.utils.semiBold
|
import it.vfsfitvnm.vimusic.utils.semiBold
|
||||||
import it.vfsfitvnm.vimusic.utils.songSortByKey
|
import it.vfsfitvnm.vimusic.utils.songSortByKey
|
||||||
import it.vfsfitvnm.vimusic.utils.songSortOrderKey
|
import it.vfsfitvnm.vimusic.utils.songSortOrderKey
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.flow.flowOn
|
|
||||||
|
|
||||||
@ExperimentalFoundationApi
|
@ExperimentalFoundationApi
|
||||||
@ExperimentalAnimationApi
|
@ExperimentalAnimationApi
|
||||||
|
@ -77,15 +75,10 @@ fun HomeSongs(
|
||||||
var sortBy by rememberPreference(songSortByKey, SongSortBy.DateAdded)
|
var sortBy by rememberPreference(songSortByKey, SongSortBy.DateAdded)
|
||||||
var sortOrder by rememberPreference(songSortOrderKey, SortOrder.Descending)
|
var sortOrder by rememberPreference(songSortOrderKey, SortOrder.Descending)
|
||||||
|
|
||||||
val items by produceSaveableState(
|
var items by persistList<DetailedSong>("home/songs")
|
||||||
initialValue = emptyList(),
|
|
||||||
stateSaver = DetailedSongListSaver,
|
LaunchedEffect(sortBy, sortOrder) {
|
||||||
sortBy, sortOrder,
|
Database.songs(sortBy, sortOrder).collect { items = it }
|
||||||
) {
|
|
||||||
Database
|
|
||||||
.songs(sortBy, sortOrder)
|
|
||||||
.flowOn(Dispatchers.IO)
|
|
||||||
.collect { value = it }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val sortOrderIconRotation by animateFloatAsState(
|
val sortOrderIconRotation by animateFloatAsState(
|
||||||
|
|
|
@ -31,24 +31,24 @@ import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.saveable.rememberSaveable
|
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.ColorFilter
|
import androidx.compose.ui.graphics.ColorFilter
|
||||||
import androidx.compose.ui.res.painterResource
|
import androidx.compose.ui.res.painterResource
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
import it.vfsfitvnm.compose.persist.persist
|
||||||
|
import it.vfsfitvnm.innertube.Innertube
|
||||||
|
import it.vfsfitvnm.innertube.models.NavigationEndpoint
|
||||||
|
import it.vfsfitvnm.innertube.models.bodies.NextBody
|
||||||
|
import it.vfsfitvnm.innertube.requests.relatedPage
|
||||||
import it.vfsfitvnm.vimusic.Database
|
import it.vfsfitvnm.vimusic.Database
|
||||||
import it.vfsfitvnm.vimusic.LocalPlayerAwareWindowInsets
|
import it.vfsfitvnm.vimusic.LocalPlayerAwareWindowInsets
|
||||||
import it.vfsfitvnm.vimusic.LocalPlayerServiceBinder
|
import it.vfsfitvnm.vimusic.LocalPlayerServiceBinder
|
||||||
import it.vfsfitvnm.vimusic.R
|
import it.vfsfitvnm.vimusic.R
|
||||||
|
import it.vfsfitvnm.vimusic.models.DetailedSong
|
||||||
import it.vfsfitvnm.vimusic.query
|
import it.vfsfitvnm.vimusic.query
|
||||||
import it.vfsfitvnm.vimusic.savers.DetailedSongSaver
|
|
||||||
import it.vfsfitvnm.vimusic.savers.InnertubeRelatedPageSaver
|
|
||||||
import it.vfsfitvnm.vimusic.savers.nullableSaver
|
|
||||||
import it.vfsfitvnm.vimusic.savers.resultSaver
|
|
||||||
import it.vfsfitvnm.vimusic.ui.components.LocalMenuState
|
import it.vfsfitvnm.vimusic.ui.components.LocalMenuState
|
||||||
import it.vfsfitvnm.vimusic.ui.components.ShimmerHost
|
import it.vfsfitvnm.vimusic.ui.components.ShimmerHost
|
||||||
import it.vfsfitvnm.vimusic.ui.components.themed.FloatingActionsContainerWithScrollToTop
|
import it.vfsfitvnm.vimusic.ui.components.themed.FloatingActionsContainerWithScrollToTop
|
||||||
|
@ -70,16 +70,10 @@ import it.vfsfitvnm.vimusic.utils.SnapLayoutInfoProvider
|
||||||
import it.vfsfitvnm.vimusic.utils.asMediaItem
|
import it.vfsfitvnm.vimusic.utils.asMediaItem
|
||||||
import it.vfsfitvnm.vimusic.utils.center
|
import it.vfsfitvnm.vimusic.utils.center
|
||||||
import it.vfsfitvnm.vimusic.utils.forcePlay
|
import it.vfsfitvnm.vimusic.utils.forcePlay
|
||||||
|
import it.vfsfitvnm.vimusic.utils.isLandscape
|
||||||
import it.vfsfitvnm.vimusic.utils.secondary
|
import it.vfsfitvnm.vimusic.utils.secondary
|
||||||
import it.vfsfitvnm.vimusic.utils.semiBold
|
import it.vfsfitvnm.vimusic.utils.semiBold
|
||||||
import it.vfsfitvnm.innertube.Innertube
|
|
||||||
import it.vfsfitvnm.innertube.models.NavigationEndpoint
|
|
||||||
import it.vfsfitvnm.innertube.models.bodies.NextBody
|
|
||||||
import it.vfsfitvnm.innertube.requests.relatedPage
|
|
||||||
import it.vfsfitvnm.vimusic.utils.isLandscape
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||||
import kotlinx.coroutines.flow.flowOn
|
|
||||||
|
|
||||||
@ExperimentalFoundationApi
|
@ExperimentalFoundationApi
|
||||||
@ExperimentalAnimationApi
|
@ExperimentalAnimationApi
|
||||||
|
@ -95,25 +89,18 @@ fun QuickPicks(
|
||||||
val menuState = LocalMenuState.current
|
val menuState = LocalMenuState.current
|
||||||
val windowInsets = LocalPlayerAwareWindowInsets.current
|
val windowInsets = LocalPlayerAwareWindowInsets.current
|
||||||
|
|
||||||
var trending by rememberSaveable(stateSaver = nullableSaver(DetailedSongSaver)) {
|
var trending by persist<DetailedSong?>("home/trending")
|
||||||
mutableStateOf(null)
|
|
||||||
}
|
|
||||||
|
|
||||||
var relatedPageResult by rememberSaveable(stateSaver = resultSaver(nullableSaver(InnertubeRelatedPageSaver))) {
|
var relatedPageResult by persist<Result<Innertube.RelatedPage?>?>(tag = "home/relatedPageResult")
|
||||||
mutableStateOf(null)
|
|
||||||
}
|
|
||||||
|
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
Database.trending()
|
Database.trending().distinctUntilChanged().collect { song ->
|
||||||
.flowOn(Dispatchers.IO)
|
if ((song == null && relatedPageResult == null) || trending?.id != song?.id) {
|
||||||
.distinctUntilChanged()
|
relatedPageResult =
|
||||||
.collect { song ->
|
Innertube.relatedPage(NextBody(videoId = (song?.id ?: "J7p4bzqLvCw")))
|
||||||
if ((song == null && relatedPageResult == null) || trending?.id != song?.id) {
|
|
||||||
relatedPageResult =
|
|
||||||
Innertube.relatedPage(NextBody(videoId = (song?.id ?: "J7p4bzqLvCw")))
|
|
||||||
}
|
|
||||||
trending = song
|
|
||||||
}
|
}
|
||||||
|
trending = song
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val songThumbnailSizeDp = Dimensions.thumbnails.song
|
val songThumbnailSizeDp = Dimensions.thumbnails.song
|
||||||
|
|
|
@ -4,6 +4,7 @@ import androidx.compose.animation.ExperimentalAnimationApi
|
||||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.saveable.rememberSaveableStateHolder
|
import androidx.compose.runtime.saveable.rememberSaveableStateHolder
|
||||||
|
import it.vfsfitvnm.compose.persist.PersistMapCleanup
|
||||||
import it.vfsfitvnm.route.RouteHandler
|
import it.vfsfitvnm.route.RouteHandler
|
||||||
import it.vfsfitvnm.vimusic.R
|
import it.vfsfitvnm.vimusic.R
|
||||||
import it.vfsfitvnm.vimusic.ui.components.themed.Scaffold
|
import it.vfsfitvnm.vimusic.ui.components.themed.Scaffold
|
||||||
|
@ -15,6 +16,8 @@ import it.vfsfitvnm.vimusic.ui.screens.globalRoutes
|
||||||
fun LocalPlaylistScreen(playlistId: Long) {
|
fun LocalPlaylistScreen(playlistId: Long) {
|
||||||
val saveableStateHolder = rememberSaveableStateHolder()
|
val saveableStateHolder = rememberSaveableStateHolder()
|
||||||
|
|
||||||
|
PersistMapCleanup(tagPrefix = "localPlaylist/$playlistId/")
|
||||||
|
|
||||||
RouteHandler(listenToGlobalEmitter = true) {
|
RouteHandler(listenToGlobalEmitter = true) {
|
||||||
globalRoutes()
|
globalRoutes()
|
||||||
|
|
||||||
|
@ -28,11 +31,13 @@ fun LocalPlaylistScreen(playlistId: Long) {
|
||||||
Item(0, "Songs", R.drawable.musical_notes)
|
Item(0, "Songs", R.drawable.musical_notes)
|
||||||
}
|
}
|
||||||
) { currentTabIndex ->
|
) { currentTabIndex ->
|
||||||
saveableStateHolder.SaveableStateProvider(key = currentTabIndex) {
|
saveableStateHolder.SaveableStateProvider(currentTabIndex) {
|
||||||
LocalPlaylistSongs(
|
when (currentTabIndex) {
|
||||||
playlistId = playlistId,
|
0 -> LocalPlaylistSongs(
|
||||||
onDelete = pop
|
playlistId = playlistId,
|
||||||
)
|
onDelete = pop
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,12 +16,17 @@ import androidx.compose.foundation.lazy.itemsIndexed
|
||||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||||
import androidx.compose.material.ripple.rememberRipple
|
import androidx.compose.material.ripple.rememberRipple
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.saveable.rememberSaveable
|
import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
import it.vfsfitvnm.compose.persist.persist
|
||||||
|
import it.vfsfitvnm.innertube.Innertube
|
||||||
|
import it.vfsfitvnm.innertube.models.bodies.BrowseBody
|
||||||
|
import it.vfsfitvnm.innertube.requests.playlistPage
|
||||||
import it.vfsfitvnm.reordering.ReorderingLazyColumn
|
import it.vfsfitvnm.reordering.ReorderingLazyColumn
|
||||||
import it.vfsfitvnm.reordering.animateItemPlacement
|
import it.vfsfitvnm.reordering.animateItemPlacement
|
||||||
import it.vfsfitvnm.reordering.draggedItem
|
import it.vfsfitvnm.reordering.draggedItem
|
||||||
|
@ -32,10 +37,9 @@ import it.vfsfitvnm.vimusic.LocalPlayerAwareWindowInsets
|
||||||
import it.vfsfitvnm.vimusic.LocalPlayerServiceBinder
|
import it.vfsfitvnm.vimusic.LocalPlayerServiceBinder
|
||||||
import it.vfsfitvnm.vimusic.R
|
import it.vfsfitvnm.vimusic.R
|
||||||
import it.vfsfitvnm.vimusic.models.DetailedSong
|
import it.vfsfitvnm.vimusic.models.DetailedSong
|
||||||
|
import it.vfsfitvnm.vimusic.models.PlaylistWithSongs
|
||||||
import it.vfsfitvnm.vimusic.models.SongPlaylistMap
|
import it.vfsfitvnm.vimusic.models.SongPlaylistMap
|
||||||
import it.vfsfitvnm.vimusic.query
|
import it.vfsfitvnm.vimusic.query
|
||||||
import it.vfsfitvnm.vimusic.savers.PlaylistWithSongsSaver
|
|
||||||
import it.vfsfitvnm.vimusic.savers.nullableSaver
|
|
||||||
import it.vfsfitvnm.vimusic.transaction
|
import it.vfsfitvnm.vimusic.transaction
|
||||||
import it.vfsfitvnm.vimusic.ui.components.LocalMenuState
|
import it.vfsfitvnm.vimusic.ui.components.LocalMenuState
|
||||||
import it.vfsfitvnm.vimusic.ui.components.themed.ConfirmationDialog
|
import it.vfsfitvnm.vimusic.ui.components.themed.ConfirmationDialog
|
||||||
|
@ -57,10 +61,6 @@ import it.vfsfitvnm.vimusic.utils.completed
|
||||||
import it.vfsfitvnm.vimusic.utils.enqueue
|
import it.vfsfitvnm.vimusic.utils.enqueue
|
||||||
import it.vfsfitvnm.vimusic.utils.forcePlayAtIndex
|
import it.vfsfitvnm.vimusic.utils.forcePlayAtIndex
|
||||||
import it.vfsfitvnm.vimusic.utils.forcePlayFromBeginning
|
import it.vfsfitvnm.vimusic.utils.forcePlayFromBeginning
|
||||||
import it.vfsfitvnm.vimusic.utils.produceSaveableState
|
|
||||||
import it.vfsfitvnm.innertube.Innertube
|
|
||||||
import it.vfsfitvnm.innertube.models.bodies.BrowseBody
|
|
||||||
import it.vfsfitvnm.innertube.requests.playlistPage
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.flow.filterNotNull
|
import kotlinx.coroutines.flow.filterNotNull
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
|
@ -77,14 +77,10 @@ fun LocalPlaylistSongs(
|
||||||
val binder = LocalPlayerServiceBinder.current
|
val binder = LocalPlayerServiceBinder.current
|
||||||
val menuState = LocalMenuState.current
|
val menuState = LocalMenuState.current
|
||||||
|
|
||||||
val playlistWithSongs by produceSaveableState(
|
var playlistWithSongs by persist<PlaylistWithSongs?>("localPlaylist/$playlistId/playlistWithSongs")
|
||||||
initialValue = null,
|
|
||||||
stateSaver = nullableSaver(PlaylistWithSongsSaver)
|
LaunchedEffect(Unit) {
|
||||||
) {
|
Database.playlistWithSongs(playlistId).filterNotNull().collect { playlistWithSongs = it }
|
||||||
Database
|
|
||||||
.playlistWithSongs(playlistId)
|
|
||||||
.filterNotNull()
|
|
||||||
.collect { value = it }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val lazyListState = rememberLazyListState()
|
val lazyListState = rememberLazyListState()
|
||||||
|
|
|
@ -52,9 +52,7 @@ import it.vfsfitvnm.vimusic.utils.rememberPreference
|
||||||
import it.vfsfitvnm.vimusic.utils.secondary
|
import it.vfsfitvnm.vimusic.utils.secondary
|
||||||
import it.vfsfitvnm.vimusic.utils.semiBold
|
import it.vfsfitvnm.vimusic.utils.semiBold
|
||||||
import it.vfsfitvnm.vimusic.utils.trackLoopEnabledKey
|
import it.vfsfitvnm.vimusic.utils.trackLoopEnabledKey
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||||
import kotlinx.coroutines.flow.flowOn
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun Controls(
|
fun Controls(
|
||||||
|
@ -82,11 +80,7 @@ fun Controls(
|
||||||
}
|
}
|
||||||
|
|
||||||
LaunchedEffect(mediaId) {
|
LaunchedEffect(mediaId) {
|
||||||
Database
|
Database.likedAt(mediaId).distinctUntilChanged().collect { likedAt = it }
|
||||||
.likedAt(mediaId)
|
|
||||||
.flowOn(Dispatchers.IO)
|
|
||||||
.distinctUntilChanged()
|
|
||||||
.collect { likedAt = it }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val shouldBePlayingTransition = updateTransition(shouldBePlaying, label = "shouldBePlaying")
|
val shouldBePlayingTransition = updateTransition(shouldBePlaying, label = "shouldBePlaying")
|
||||||
|
|
|
@ -32,7 +32,7 @@ import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.saveable.autoSaver
|
import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
@ -70,14 +70,12 @@ import it.vfsfitvnm.vimusic.utils.center
|
||||||
import it.vfsfitvnm.vimusic.utils.color
|
import it.vfsfitvnm.vimusic.utils.color
|
||||||
import it.vfsfitvnm.vimusic.utils.isShowingSynchronizedLyricsKey
|
import it.vfsfitvnm.vimusic.utils.isShowingSynchronizedLyricsKey
|
||||||
import it.vfsfitvnm.vimusic.utils.medium
|
import it.vfsfitvnm.vimusic.utils.medium
|
||||||
import it.vfsfitvnm.vimusic.utils.produceSaveableState
|
|
||||||
import it.vfsfitvnm.vimusic.utils.rememberPreference
|
import it.vfsfitvnm.vimusic.utils.rememberPreference
|
||||||
import it.vfsfitvnm.vimusic.utils.toast
|
import it.vfsfitvnm.vimusic.utils.toast
|
||||||
import it.vfsfitvnm.vimusic.utils.verticalFadingEdge
|
import it.vfsfitvnm.vimusic.utils.verticalFadingEdge
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||||
import kotlinx.coroutines.flow.flowOn
|
|
||||||
import kotlinx.coroutines.isActive
|
import kotlinx.coroutines.isActive
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
|
@ -108,19 +106,16 @@ fun Lyrics(
|
||||||
mutableStateOf(false)
|
mutableStateOf(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
val lyrics by produceSaveableState(
|
var lyrics by rememberSaveable {
|
||||||
initialValue = ".",
|
mutableStateOf<String?>(".")
|
||||||
stateSaver = autoSaver<String?>(),
|
}
|
||||||
mediaId, isShowingSynchronizedLyrics
|
|
||||||
) {
|
LaunchedEffect(mediaId, isShowingSynchronizedLyrics) {
|
||||||
if (isShowingSynchronizedLyrics) {
|
if (isShowingSynchronizedLyrics) {
|
||||||
Database.synchronizedLyrics(mediaId)
|
Database.synchronizedLyrics(mediaId)
|
||||||
} else {
|
} else {
|
||||||
Database.lyrics(mediaId)
|
Database.lyrics(mediaId)
|
||||||
}
|
}.distinctUntilChanged().collect { lyrics = it }
|
||||||
.flowOn(Dispatchers.IO)
|
|
||||||
.distinctUntilChanged()
|
|
||||||
.collect { value = it }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var isError by remember(lyrics) {
|
var isError by remember(lyrics) {
|
||||||
|
|
|
@ -4,6 +4,7 @@ import androidx.compose.animation.ExperimentalAnimationApi
|
||||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.saveable.rememberSaveableStateHolder
|
import androidx.compose.runtime.saveable.rememberSaveableStateHolder
|
||||||
|
import it.vfsfitvnm.compose.persist.PersistMapCleanup
|
||||||
import it.vfsfitvnm.route.RouteHandler
|
import it.vfsfitvnm.route.RouteHandler
|
||||||
import it.vfsfitvnm.vimusic.R
|
import it.vfsfitvnm.vimusic.R
|
||||||
import it.vfsfitvnm.vimusic.ui.components.themed.Scaffold
|
import it.vfsfitvnm.vimusic.ui.components.themed.Scaffold
|
||||||
|
@ -14,6 +15,7 @@ import it.vfsfitvnm.vimusic.ui.screens.globalRoutes
|
||||||
@Composable
|
@Composable
|
||||||
fun PlaylistScreen(browseId: String) {
|
fun PlaylistScreen(browseId: String) {
|
||||||
val saveableStateHolder = rememberSaveableStateHolder()
|
val saveableStateHolder = rememberSaveableStateHolder()
|
||||||
|
PersistMapCleanup(tagPrefix = "playlist/$browseId")
|
||||||
|
|
||||||
RouteHandler(listenToGlobalEmitter = true) {
|
RouteHandler(listenToGlobalEmitter = true) {
|
||||||
globalRoutes()
|
globalRoutes()
|
||||||
|
|
|
@ -8,11 +8,15 @@ import androidx.compose.foundation.combinedClickable
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.WindowInsetsSides
|
||||||
|
import androidx.compose.foundation.layout.asPaddingValues
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.only
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.lazy.itemsIndexed
|
import androidx.compose.foundation.lazy.itemsIndexed
|
||||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.saveable.rememberSaveable
|
import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
|
@ -21,18 +25,17 @@ import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import com.valentinilk.shimmer.shimmer
|
import com.valentinilk.shimmer.shimmer
|
||||||
|
import it.vfsfitvnm.compose.persist.persist
|
||||||
|
import it.vfsfitvnm.innertube.Innertube
|
||||||
|
import it.vfsfitvnm.innertube.models.bodies.BrowseBody
|
||||||
|
import it.vfsfitvnm.innertube.requests.playlistPage
|
||||||
import it.vfsfitvnm.vimusic.Database
|
import it.vfsfitvnm.vimusic.Database
|
||||||
import it.vfsfitvnm.vimusic.LocalPlayerAwareWindowInsets
|
import it.vfsfitvnm.vimusic.LocalPlayerAwareWindowInsets
|
||||||
import androidx.compose.foundation.layout.WindowInsetsSides
|
|
||||||
import androidx.compose.foundation.layout.asPaddingValues
|
|
||||||
import androidx.compose.foundation.layout.only
|
|
||||||
import it.vfsfitvnm.vimusic.LocalPlayerServiceBinder
|
import it.vfsfitvnm.vimusic.LocalPlayerServiceBinder
|
||||||
import it.vfsfitvnm.vimusic.R
|
import it.vfsfitvnm.vimusic.R
|
||||||
import it.vfsfitvnm.vimusic.models.Playlist
|
import it.vfsfitvnm.vimusic.models.Playlist
|
||||||
import it.vfsfitvnm.vimusic.models.SongPlaylistMap
|
import it.vfsfitvnm.vimusic.models.SongPlaylistMap
|
||||||
import it.vfsfitvnm.vimusic.query
|
import it.vfsfitvnm.vimusic.query
|
||||||
import it.vfsfitvnm.vimusic.savers.InnertubePlaylistOrAlbumPageSaver
|
|
||||||
import it.vfsfitvnm.vimusic.savers.nullableSaver
|
|
||||||
import it.vfsfitvnm.vimusic.transaction
|
import it.vfsfitvnm.vimusic.transaction
|
||||||
import it.vfsfitvnm.vimusic.ui.components.LocalMenuState
|
import it.vfsfitvnm.vimusic.ui.components.LocalMenuState
|
||||||
import it.vfsfitvnm.vimusic.ui.components.ShimmerHost
|
import it.vfsfitvnm.vimusic.ui.components.ShimmerHost
|
||||||
|
@ -56,10 +59,6 @@ import it.vfsfitvnm.vimusic.utils.enqueue
|
||||||
import it.vfsfitvnm.vimusic.utils.forcePlayAtIndex
|
import it.vfsfitvnm.vimusic.utils.forcePlayAtIndex
|
||||||
import it.vfsfitvnm.vimusic.utils.forcePlayFromBeginning
|
import it.vfsfitvnm.vimusic.utils.forcePlayFromBeginning
|
||||||
import it.vfsfitvnm.vimusic.utils.isLandscape
|
import it.vfsfitvnm.vimusic.utils.isLandscape
|
||||||
import it.vfsfitvnm.vimusic.utils.produceSaveableState
|
|
||||||
import it.vfsfitvnm.innertube.Innertube
|
|
||||||
import it.vfsfitvnm.innertube.models.bodies.BrowseBody
|
|
||||||
import it.vfsfitvnm.innertube.requests.playlistPage
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
|
@ -74,13 +73,12 @@ fun PlaylistSongList(
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
val menuState = LocalMenuState.current
|
val menuState = LocalMenuState.current
|
||||||
|
|
||||||
val playlistPage by produceSaveableState(
|
var playlistPage by persist<Innertube.PlaylistOrAlbumPage?>("playlist/$browseId/playlistPage")
|
||||||
initialValue = null,
|
|
||||||
stateSaver = nullableSaver(InnertubePlaylistOrAlbumPageSaver),
|
|
||||||
) {
|
|
||||||
if (value != null && value?.songsPage?.continuation == null) return@produceSaveableState
|
|
||||||
|
|
||||||
value = withContext(Dispatchers.IO) {
|
LaunchedEffect(Unit) {
|
||||||
|
if (playlistPage != null && playlistPage?.songsPage?.continuation == null) return@LaunchedEffect
|
||||||
|
|
||||||
|
playlistPage = withContext(Dispatchers.IO) {
|
||||||
Innertube.playlistPage(BrowseBody(browseId = browseId))?.completed()?.getOrNull()
|
Innertube.playlistPage(BrowseBody(browseId = browseId))?.completed()?.getOrNull()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,27 +4,30 @@ import androidx.compose.animation.ExperimentalAnimationApi
|
||||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||||
import androidx.compose.foundation.combinedClickable
|
import androidx.compose.foundation.combinedClickable
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.WindowInsetsSides
|
||||||
|
import androidx.compose.foundation.layout.asPaddingValues
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.only
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.lazy.items
|
import androidx.compose.foundation.lazy.items
|
||||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||||
import androidx.compose.foundation.text.BasicTextField
|
import androidx.compose.foundation.text.BasicTextField
|
||||||
import androidx.compose.foundation.text.KeyboardOptions
|
import androidx.compose.foundation.text.KeyboardOptions
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.SolidColor
|
import androidx.compose.ui.graphics.SolidColor
|
||||||
import androidx.compose.ui.text.input.ImeAction
|
import androidx.compose.ui.text.input.ImeAction
|
||||||
import androidx.compose.ui.text.input.TextFieldValue
|
import androidx.compose.ui.text.input.TextFieldValue
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
|
import it.vfsfitvnm.compose.persist.persistList
|
||||||
|
import it.vfsfitvnm.innertube.models.NavigationEndpoint
|
||||||
import it.vfsfitvnm.vimusic.Database
|
import it.vfsfitvnm.vimusic.Database
|
||||||
import it.vfsfitvnm.vimusic.LocalPlayerAwareWindowInsets
|
import it.vfsfitvnm.vimusic.LocalPlayerAwareWindowInsets
|
||||||
import androidx.compose.foundation.layout.WindowInsetsSides
|
|
||||||
import androidx.compose.foundation.layout.asPaddingValues
|
|
||||||
import androidx.compose.foundation.layout.only
|
|
||||||
import it.vfsfitvnm.vimusic.LocalPlayerServiceBinder
|
import it.vfsfitvnm.vimusic.LocalPlayerServiceBinder
|
||||||
import it.vfsfitvnm.vimusic.models.DetailedSong
|
import it.vfsfitvnm.vimusic.models.DetailedSong
|
||||||
import it.vfsfitvnm.vimusic.savers.DetailedSongListSaver
|
|
||||||
import it.vfsfitvnm.vimusic.ui.components.LocalMenuState
|
import it.vfsfitvnm.vimusic.ui.components.LocalMenuState
|
||||||
import it.vfsfitvnm.vimusic.ui.components.themed.FloatingActionsContainerWithScrollToTop
|
import it.vfsfitvnm.vimusic.ui.components.themed.FloatingActionsContainerWithScrollToTop
|
||||||
import it.vfsfitvnm.vimusic.ui.components.themed.Header
|
import it.vfsfitvnm.vimusic.ui.components.themed.Header
|
||||||
|
@ -38,10 +41,6 @@ import it.vfsfitvnm.vimusic.utils.align
|
||||||
import it.vfsfitvnm.vimusic.utils.asMediaItem
|
import it.vfsfitvnm.vimusic.utils.asMediaItem
|
||||||
import it.vfsfitvnm.vimusic.utils.forcePlay
|
import it.vfsfitvnm.vimusic.utils.forcePlay
|
||||||
import it.vfsfitvnm.vimusic.utils.medium
|
import it.vfsfitvnm.vimusic.utils.medium
|
||||||
import it.vfsfitvnm.vimusic.utils.produceSaveableState
|
|
||||||
import it.vfsfitvnm.innertube.models.NavigationEndpoint
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.flow.flowOn
|
|
||||||
|
|
||||||
@ExperimentalFoundationApi
|
@ExperimentalFoundationApi
|
||||||
@ExperimentalAnimationApi
|
@ExperimentalAnimationApi
|
||||||
|
@ -55,16 +54,11 @@ fun LocalSongSearch(
|
||||||
val binder = LocalPlayerServiceBinder.current
|
val binder = LocalPlayerServiceBinder.current
|
||||||
val menuState = LocalMenuState.current
|
val menuState = LocalMenuState.current
|
||||||
|
|
||||||
val items by produceSaveableState(
|
var items by persistList<DetailedSong>("search/local/songs")
|
||||||
initialValue = emptyList(),
|
|
||||||
stateSaver = DetailedSongListSaver,
|
LaunchedEffect(textFieldValue.text) {
|
||||||
key1 = textFieldValue.text
|
|
||||||
) {
|
|
||||||
if (textFieldValue.text.length > 1) {
|
if (textFieldValue.text.length > 1) {
|
||||||
Database
|
Database.search("%${textFieldValue.text}%").collect { items = it }
|
||||||
.search("%${textFieldValue.text}%")
|
|
||||||
.flowOn(Dispatchers.IO)
|
|
||||||
.collect { value = it }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,10 +25,7 @@ import androidx.compose.material.ripple.rememberRipple
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.saveable.autoSaver
|
|
||||||
import androidx.compose.runtime.saveable.rememberSaveable
|
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
@ -46,14 +43,16 @@ import androidx.compose.ui.text.input.TextFieldValue
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.core.net.toUri
|
import androidx.core.net.toUri
|
||||||
|
import it.vfsfitvnm.compose.persist.persist
|
||||||
|
import it.vfsfitvnm.compose.persist.persistList
|
||||||
|
import it.vfsfitvnm.innertube.Innertube
|
||||||
|
import it.vfsfitvnm.innertube.models.bodies.SearchSuggestionsBody
|
||||||
|
import it.vfsfitvnm.innertube.requests.searchSuggestions
|
||||||
import it.vfsfitvnm.vimusic.Database
|
import it.vfsfitvnm.vimusic.Database
|
||||||
import it.vfsfitvnm.vimusic.LocalPlayerAwareWindowInsets
|
import it.vfsfitvnm.vimusic.LocalPlayerAwareWindowInsets
|
||||||
import it.vfsfitvnm.vimusic.R
|
import it.vfsfitvnm.vimusic.R
|
||||||
import it.vfsfitvnm.vimusic.models.SearchQuery
|
import it.vfsfitvnm.vimusic.models.SearchQuery
|
||||||
import it.vfsfitvnm.vimusic.query
|
import it.vfsfitvnm.vimusic.query
|
||||||
import it.vfsfitvnm.vimusic.savers.SearchQuerySaver
|
|
||||||
import it.vfsfitvnm.vimusic.savers.listSaver
|
|
||||||
import it.vfsfitvnm.vimusic.savers.resultSaver
|
|
||||||
import it.vfsfitvnm.vimusic.ui.components.themed.FloatingActionsContainerWithScrollToTop
|
import it.vfsfitvnm.vimusic.ui.components.themed.FloatingActionsContainerWithScrollToTop
|
||||||
import it.vfsfitvnm.vimusic.ui.components.themed.Header
|
import it.vfsfitvnm.vimusic.ui.components.themed.Header
|
||||||
import it.vfsfitvnm.vimusic.ui.components.themed.SecondaryTextButton
|
import it.vfsfitvnm.vimusic.ui.components.themed.SecondaryTextButton
|
||||||
|
@ -61,17 +60,11 @@ import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance
|
||||||
import it.vfsfitvnm.vimusic.utils.align
|
import it.vfsfitvnm.vimusic.utils.align
|
||||||
import it.vfsfitvnm.vimusic.utils.center
|
import it.vfsfitvnm.vimusic.utils.center
|
||||||
import it.vfsfitvnm.vimusic.utils.medium
|
import it.vfsfitvnm.vimusic.utils.medium
|
||||||
import it.vfsfitvnm.vimusic.utils.produceSaveableState
|
|
||||||
import it.vfsfitvnm.vimusic.utils.secondary
|
|
||||||
import it.vfsfitvnm.innertube.Innertube
|
|
||||||
import it.vfsfitvnm.innertube.models.bodies.SearchSuggestionsBody
|
|
||||||
import it.vfsfitvnm.innertube.requests.searchSuggestions
|
|
||||||
import it.vfsfitvnm.vimusic.utils.pauseSearchHistoryKey
|
import it.vfsfitvnm.vimusic.utils.pauseSearchHistoryKey
|
||||||
import it.vfsfitvnm.vimusic.utils.preferences
|
import it.vfsfitvnm.vimusic.utils.preferences
|
||||||
import kotlinx.coroutines.Dispatchers
|
import it.vfsfitvnm.vimusic.utils.secondary
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||||
import kotlinx.coroutines.flow.flowOn
|
|
||||||
|
|
||||||
@ExperimentalAnimationApi
|
@ExperimentalAnimationApi
|
||||||
@Composable
|
@Composable
|
||||||
|
@ -86,22 +79,17 @@ fun OnlineSearch(
|
||||||
|
|
||||||
val (colorPalette, typography) = LocalAppearance.current
|
val (colorPalette, typography) = LocalAppearance.current
|
||||||
|
|
||||||
val history by produceSaveableState(
|
var history by persistList<SearchQuery>("search/online/history")
|
||||||
initialValue = emptyList(),
|
|
||||||
stateSaver = listSaver(SearchQuerySaver),
|
LaunchedEffect(textFieldValue.text) {
|
||||||
key1 = textFieldValue.text
|
|
||||||
) {
|
|
||||||
if (!context.preferences.getBoolean(pauseSearchHistoryKey, false)) {
|
if (!context.preferences.getBoolean(pauseSearchHistoryKey, false)) {
|
||||||
Database.queries("%${textFieldValue.text}%")
|
Database.queries("%${textFieldValue.text}%")
|
||||||
.flowOn(Dispatchers.IO)
|
|
||||||
.distinctUntilChanged { old, new -> old.size == new.size }
|
.distinctUntilChanged { old, new -> old.size == new.size }
|
||||||
.collect { value = it }
|
.collect { history = it }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var suggestionsResult by rememberSaveable(stateSaver = resultSaver(autoSaver<List<String>?>())) {
|
var suggestionsResult by persist<Result<List<String>?>?>("search/online/suggestionsResult")
|
||||||
mutableStateOf(null)
|
|
||||||
}
|
|
||||||
|
|
||||||
LaunchedEffect(textFieldValue.text) {
|
LaunchedEffect(textFieldValue.text) {
|
||||||
if (textFieldValue.text.isNotEmpty()) {
|
if (textFieldValue.text.isNotEmpty()) {
|
||||||
|
|
|
@ -16,6 +16,7 @@ import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.text.TextRange
|
import androidx.compose.ui.text.TextRange
|
||||||
import androidx.compose.ui.text.input.TextFieldValue
|
import androidx.compose.ui.text.input.TextFieldValue
|
||||||
|
import it.vfsfitvnm.compose.persist.PersistMapCleanup
|
||||||
import it.vfsfitvnm.route.RouteHandler
|
import it.vfsfitvnm.route.RouteHandler
|
||||||
import it.vfsfitvnm.vimusic.R
|
import it.vfsfitvnm.vimusic.R
|
||||||
import it.vfsfitvnm.vimusic.ui.components.themed.Scaffold
|
import it.vfsfitvnm.vimusic.ui.components.themed.Scaffold
|
||||||
|
@ -49,6 +50,8 @@ fun SearchScreen(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PersistMapCleanup(tagPrefix = "search/")
|
||||||
|
|
||||||
RouteHandler(listenToGlobalEmitter = true) {
|
RouteHandler(listenToGlobalEmitter = true) {
|
||||||
globalRoutes()
|
globalRoutes()
|
||||||
|
|
||||||
|
|
|
@ -2,8 +2,11 @@ package it.vfsfitvnm.vimusic.ui.screens.searchresult
|
||||||
|
|
||||||
import androidx.compose.animation.ExperimentalAnimationApi
|
import androidx.compose.animation.ExperimentalAnimationApi
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.WindowInsetsSides
|
||||||
|
import androidx.compose.foundation.layout.asPaddingValues
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.only
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.lazy.LazyItemScope
|
import androidx.compose.foundation.lazy.LazyItemScope
|
||||||
|
@ -11,32 +14,29 @@ import androidx.compose.foundation.lazy.items
|
||||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||||
import androidx.compose.foundation.text.BasicText
|
import androidx.compose.foundation.text.BasicText
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.rememberUpdatedState
|
import androidx.compose.runtime.rememberUpdatedState
|
||||||
import androidx.compose.runtime.saveable.Saver
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.runtime.snapshotFlow
|
import androidx.compose.runtime.snapshotFlow
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
import it.vfsfitvnm.compose.persist.persist
|
||||||
|
import it.vfsfitvnm.innertube.Innertube
|
||||||
|
import it.vfsfitvnm.innertube.utils.plus
|
||||||
import it.vfsfitvnm.vimusic.LocalPlayerAwareWindowInsets
|
import it.vfsfitvnm.vimusic.LocalPlayerAwareWindowInsets
|
||||||
import androidx.compose.foundation.layout.WindowInsetsSides
|
|
||||||
import androidx.compose.foundation.layout.asPaddingValues
|
|
||||||
import androidx.compose.foundation.layout.only
|
|
||||||
import it.vfsfitvnm.vimusic.savers.nullableSaver
|
|
||||||
import it.vfsfitvnm.vimusic.ui.components.ShimmerHost
|
import it.vfsfitvnm.vimusic.ui.components.ShimmerHost
|
||||||
import it.vfsfitvnm.vimusic.ui.components.themed.FloatingActionsContainerWithScrollToTop
|
import it.vfsfitvnm.vimusic.ui.components.themed.FloatingActionsContainerWithScrollToTop
|
||||||
import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance
|
import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance
|
||||||
import it.vfsfitvnm.vimusic.utils.center
|
import it.vfsfitvnm.vimusic.utils.center
|
||||||
import it.vfsfitvnm.vimusic.utils.produceSaveableState
|
|
||||||
import it.vfsfitvnm.vimusic.utils.secondary
|
import it.vfsfitvnm.vimusic.utils.secondary
|
||||||
import it.vfsfitvnm.innertube.Innertube
|
|
||||||
import it.vfsfitvnm.innertube.utils.plus
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
@ExperimentalAnimationApi
|
@ExperimentalAnimationApi
|
||||||
@Composable
|
@Composable
|
||||||
inline fun <T : Innertube.Item> ItemsPage(
|
inline fun <T : Innertube.Item> ItemsPage(
|
||||||
stateSaver: Saver<Innertube.ItemsPage<T>, List<Any?>>,
|
tag: String,
|
||||||
crossinline headerContent: @Composable (textButton: (@Composable () -> Unit)?) -> Unit,
|
crossinline headerContent: @Composable (textButton: (@Composable () -> Unit)?) -> Unit,
|
||||||
crossinline itemContent: @Composable LazyItemScope.(T) -> Unit,
|
crossinline itemContent: @Composable LazyItemScope.(T) -> Unit,
|
||||||
noinline itemPlaceholderContent: @Composable () -> Unit,
|
noinline itemPlaceholderContent: @Composable () -> Unit,
|
||||||
|
@ -47,29 +47,29 @@ inline fun <T : Innertube.Item> ItemsPage(
|
||||||
noinline itemsPageProvider: (suspend (String?) -> Result<Innertube.ItemsPage<T>?>?)? = null,
|
noinline itemsPageProvider: (suspend (String?) -> Result<Innertube.ItemsPage<T>?>?)? = null,
|
||||||
) {
|
) {
|
||||||
val (_, typography) = LocalAppearance.current
|
val (_, typography) = LocalAppearance.current
|
||||||
val lazyListState = rememberLazyListState()
|
|
||||||
val updatedItemsPageProvider by rememberUpdatedState(itemsPageProvider)
|
val updatedItemsPageProvider by rememberUpdatedState(itemsPageProvider)
|
||||||
|
|
||||||
val itemsPage by produceSaveableState(
|
val lazyListState = rememberLazyListState()
|
||||||
initialValue = null,
|
|
||||||
stateSaver = nullableSaver(stateSaver),
|
var itemsPage by persist<Innertube.ItemsPage<T>?>(tag)
|
||||||
lazyListState, updatedItemsPageProvider
|
|
||||||
) {
|
LaunchedEffect(lazyListState, updatedItemsPageProvider) {
|
||||||
val currentItemsPageProvider = updatedItemsPageProvider ?: return@produceSaveableState
|
val currentItemsPageProvider = updatedItemsPageProvider ?: return@LaunchedEffect
|
||||||
|
|
||||||
snapshotFlow { lazyListState.layoutInfo.visibleItemsInfo.any { it.key == "loading" } }
|
snapshotFlow { lazyListState.layoutInfo.visibleItemsInfo.any { it.key == "loading" } }
|
||||||
.collect { shouldLoadMore ->
|
.collect { shouldLoadMore ->
|
||||||
if (!shouldLoadMore) return@collect
|
if (!shouldLoadMore) return@collect
|
||||||
|
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
currentItemsPageProvider(value?.continuation)
|
currentItemsPageProvider(itemsPage?.continuation)
|
||||||
}?.onSuccess {
|
}?.onSuccess {
|
||||||
if (it == null) {
|
if (it == null) {
|
||||||
if (value == null) {
|
if (itemsPage == null) {
|
||||||
value = Innertube.ItemsPage(null, null)
|
itemsPage = Innertube.ItemsPage(null, null)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
value += it
|
itemsPage += it
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,16 +9,18 @@ import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.saveable.rememberSaveableStateHolder
|
import androidx.compose.runtime.saveable.rememberSaveableStateHolder
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.input.pointer.pointerInput
|
import androidx.compose.ui.input.pointer.pointerInput
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
import it.vfsfitvnm.compose.persist.PersistMapCleanup
|
||||||
|
import it.vfsfitvnm.compose.persist.persistMap
|
||||||
|
import it.vfsfitvnm.innertube.Innertube
|
||||||
|
import it.vfsfitvnm.innertube.models.bodies.ContinuationBody
|
||||||
|
import it.vfsfitvnm.innertube.models.bodies.SearchBody
|
||||||
|
import it.vfsfitvnm.innertube.requests.searchPage
|
||||||
|
import it.vfsfitvnm.innertube.utils.from
|
||||||
import it.vfsfitvnm.route.RouteHandler
|
import it.vfsfitvnm.route.RouteHandler
|
||||||
import it.vfsfitvnm.vimusic.LocalPlayerServiceBinder
|
import it.vfsfitvnm.vimusic.LocalPlayerServiceBinder
|
||||||
import it.vfsfitvnm.vimusic.R
|
import it.vfsfitvnm.vimusic.R
|
||||||
import it.vfsfitvnm.vimusic.savers.InnertubeAlbumsPageSaver
|
|
||||||
import it.vfsfitvnm.vimusic.savers.InnertubeArtistItemListSaver
|
|
||||||
import it.vfsfitvnm.vimusic.savers.InnertubePlaylistItemListSaver
|
|
||||||
import it.vfsfitvnm.vimusic.savers.InnertubeSongsPageSaver
|
|
||||||
import it.vfsfitvnm.vimusic.savers.InnertubeVideoItemListSaver
|
|
||||||
import it.vfsfitvnm.vimusic.savers.innertubeItemsPageSaver
|
|
||||||
import it.vfsfitvnm.vimusic.ui.components.LocalMenuState
|
import it.vfsfitvnm.vimusic.ui.components.LocalMenuState
|
||||||
import it.vfsfitvnm.vimusic.ui.components.themed.Header
|
import it.vfsfitvnm.vimusic.ui.components.themed.Header
|
||||||
import it.vfsfitvnm.vimusic.ui.components.themed.NonQueuedMediaItemMenu
|
import it.vfsfitvnm.vimusic.ui.components.themed.NonQueuedMediaItemMenu
|
||||||
|
@ -43,19 +45,17 @@ import it.vfsfitvnm.vimusic.utils.asMediaItem
|
||||||
import it.vfsfitvnm.vimusic.utils.forcePlay
|
import it.vfsfitvnm.vimusic.utils.forcePlay
|
||||||
import it.vfsfitvnm.vimusic.utils.rememberPreference
|
import it.vfsfitvnm.vimusic.utils.rememberPreference
|
||||||
import it.vfsfitvnm.vimusic.utils.searchResultScreenTabIndexKey
|
import it.vfsfitvnm.vimusic.utils.searchResultScreenTabIndexKey
|
||||||
import it.vfsfitvnm.innertube.Innertube
|
|
||||||
import it.vfsfitvnm.innertube.models.bodies.ContinuationBody
|
|
||||||
import it.vfsfitvnm.innertube.models.bodies.SearchBody
|
|
||||||
import it.vfsfitvnm.innertube.requests.searchPage
|
|
||||||
import it.vfsfitvnm.innertube.utils.from
|
|
||||||
|
|
||||||
@ExperimentalFoundationApi
|
@ExperimentalFoundationApi
|
||||||
@ExperimentalAnimationApi
|
@ExperimentalAnimationApi
|
||||||
@Composable
|
@Composable
|
||||||
fun SearchResultScreen(query: String, onSearchAgain: () -> Unit) {
|
fun SearchResultScreen(query: String, onSearchAgain: () -> Unit) {
|
||||||
|
val context = LocalContext.current
|
||||||
val saveableStateHolder = rememberSaveableStateHolder()
|
val saveableStateHolder = rememberSaveableStateHolder()
|
||||||
val (tabIndex, onTabIndexChanges) = rememberPreference(searchResultScreenTabIndexKey, 0)
|
val (tabIndex, onTabIndexChanges) = rememberPreference(searchResultScreenTabIndexKey, 0)
|
||||||
|
|
||||||
|
PersistMapCleanup(tagPrefix = "searchResults/$query/")
|
||||||
|
|
||||||
RouteHandler(listenToGlobalEmitter = true) {
|
RouteHandler(listenToGlobalEmitter = true) {
|
||||||
globalRoutes()
|
globalRoutes()
|
||||||
|
|
||||||
|
@ -66,6 +66,9 @@ fun SearchResultScreen(query: String, onSearchAgain: () -> Unit) {
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.pointerInput(Unit) {
|
.pointerInput(Unit) {
|
||||||
detectTapGestures {
|
detectTapGestures {
|
||||||
|
context.persistMap?.keys?.removeAll {
|
||||||
|
it.startsWith("searchResults/$query/")
|
||||||
|
}
|
||||||
onSearchAgain()
|
onSearchAgain()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -74,8 +77,6 @@ fun SearchResultScreen(query: String, onSearchAgain: () -> Unit) {
|
||||||
|
|
||||||
val emptyItemsText = "No results found. Please try a different query or category"
|
val emptyItemsText = "No results found. Please try a different query or category"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Scaffold(
|
Scaffold(
|
||||||
topIconButtonId = R.drawable.chevron_back,
|
topIconButtonId = R.drawable.chevron_back,
|
||||||
onTopIconButtonClick = pop,
|
onTopIconButtonClick = pop,
|
||||||
|
@ -99,7 +100,7 @@ fun SearchResultScreen(query: String, onSearchAgain: () -> Unit) {
|
||||||
val thumbnailSizePx = thumbnailSizeDp.px
|
val thumbnailSizePx = thumbnailSizeDp.px
|
||||||
|
|
||||||
ItemsPage(
|
ItemsPage(
|
||||||
stateSaver = InnertubeSongsPageSaver,
|
tag = "searchResults/$query/songs",
|
||||||
itemsPageProvider = { continuation ->
|
itemsPageProvider = { continuation ->
|
||||||
if (continuation == null) {
|
if (continuation == null) {
|
||||||
Innertube.searchPage(
|
Innertube.searchPage(
|
||||||
|
@ -149,7 +150,7 @@ fun SearchResultScreen(query: String, onSearchAgain: () -> Unit) {
|
||||||
val thumbnailSizePx = thumbnailSizeDp.px
|
val thumbnailSizePx = thumbnailSizeDp.px
|
||||||
|
|
||||||
ItemsPage(
|
ItemsPage(
|
||||||
stateSaver = InnertubeAlbumsPageSaver,
|
tag = "searchResults/$query/albums",
|
||||||
itemsPageProvider = { continuation ->
|
itemsPageProvider = { continuation ->
|
||||||
if (continuation == null) {
|
if (continuation == null) {
|
||||||
Innertube.searchPage(
|
Innertube.searchPage(
|
||||||
|
@ -186,7 +187,7 @@ fun SearchResultScreen(query: String, onSearchAgain: () -> Unit) {
|
||||||
val thumbnailSizePx = thumbnailSizeDp.px
|
val thumbnailSizePx = thumbnailSizeDp.px
|
||||||
|
|
||||||
ItemsPage(
|
ItemsPage(
|
||||||
stateSaver = innertubeItemsPageSaver(InnertubeArtistItemListSaver),
|
tag = "searchResults/$query/artists",
|
||||||
itemsPageProvider = { continuation ->
|
itemsPageProvider = { continuation ->
|
||||||
if (continuation == null) {
|
if (continuation == null) {
|
||||||
Innertube.searchPage(
|
Innertube.searchPage(
|
||||||
|
@ -224,7 +225,7 @@ fun SearchResultScreen(query: String, onSearchAgain: () -> Unit) {
|
||||||
val thumbnailWidthDp = 128.dp
|
val thumbnailWidthDp = 128.dp
|
||||||
|
|
||||||
ItemsPage(
|
ItemsPage(
|
||||||
stateSaver = innertubeItemsPageSaver(InnertubeVideoItemListSaver),
|
tag = "searchResults/$query/videos",
|
||||||
itemsPageProvider = { continuation ->
|
itemsPageProvider = { continuation ->
|
||||||
if (continuation == null) {
|
if (continuation == null) {
|
||||||
Innertube.searchPage(
|
Innertube.searchPage(
|
||||||
|
@ -277,7 +278,7 @@ fun SearchResultScreen(query: String, onSearchAgain: () -> Unit) {
|
||||||
val thumbnailSizePx = thumbnailSizeDp.px
|
val thumbnailSizePx = thumbnailSizeDp.px
|
||||||
|
|
||||||
ItemsPage(
|
ItemsPage(
|
||||||
stateSaver = innertubeItemsPageSaver(InnertubePlaylistItemListSaver),
|
tag = "searchResults/$query/${if (tabIndex == 4) "playlists" else "featured"}",
|
||||||
itemsPageProvider = { continuation ->
|
itemsPageProvider = { continuation ->
|
||||||
if (continuation == null) {
|
if (continuation == null) {
|
||||||
val filter = if (tabIndex == 4) {
|
val filter = if (tabIndex == 4) {
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
package it.vfsfitvnm.vimusic.ui.screens.settings
|
package it.vfsfitvnm.vimusic.ui.screens.settings
|
||||||
|
|
||||||
import android.os.Build
|
|
||||||
import androidx.compose.animation.ExperimentalAnimationApi
|
import androidx.compose.animation.ExperimentalAnimationApi
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.border
|
import androidx.compose.foundation.border
|
||||||
|
|
|
@ -15,8 +15,9 @@ import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.rememberScrollState
|
import androidx.compose.foundation.rememberScrollState
|
||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.collectAsState
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.saveable.autoSaver
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import it.vfsfitvnm.vimusic.Database
|
import it.vfsfitvnm.vimusic.Database
|
||||||
|
@ -28,16 +29,13 @@ import it.vfsfitvnm.vimusic.service.PlayerService
|
||||||
import it.vfsfitvnm.vimusic.ui.components.themed.Header
|
import it.vfsfitvnm.vimusic.ui.components.themed.Header
|
||||||
import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance
|
import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance
|
||||||
import it.vfsfitvnm.vimusic.utils.intent
|
import it.vfsfitvnm.vimusic.utils.intent
|
||||||
import it.vfsfitvnm.vimusic.utils.produceSaveableState
|
|
||||||
import it.vfsfitvnm.vimusic.utils.toast
|
import it.vfsfitvnm.vimusic.utils.toast
|
||||||
import java.io.FileInputStream
|
import java.io.FileInputStream
|
||||||
import java.io.FileOutputStream
|
import java.io.FileOutputStream
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
import java.util.Date
|
import java.util.Date
|
||||||
import kotlin.system.exitProcess
|
import kotlin.system.exitProcess
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||||
import kotlinx.coroutines.flow.flowOn
|
|
||||||
|
|
||||||
@ExperimentalAnimationApi
|
@ExperimentalAnimationApi
|
||||||
@Composable
|
@Composable
|
||||||
|
@ -45,12 +43,9 @@ fun DatabaseSettings() {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
val (colorPalette) = LocalAppearance.current
|
val (colorPalette) = LocalAppearance.current
|
||||||
|
|
||||||
val eventsCount by produceSaveableState(initialValue = 0, stateSaver = autoSaver()) {
|
val eventsCount by remember {
|
||||||
Database.eventsCount()
|
Database.eventsCount().distinctUntilChanged()
|
||||||
.flowOn(Dispatchers.IO)
|
}.collectAsState(initial = 0)
|
||||||
.distinctUntilChanged()
|
|
||||||
.collect { value = it }
|
|
||||||
}
|
|
||||||
|
|
||||||
val backupLauncher =
|
val backupLauncher =
|
||||||
rememberLauncherForActivityResult(ActivityResultContracts.CreateDocument("application/vnd.sqlite3")) { uri ->
|
rememberLauncherForActivityResult(ActivityResultContracts.CreateDocument("application/vnd.sqlite3")) { uri ->
|
||||||
|
|
|
@ -21,10 +21,10 @@ import androidx.compose.foundation.rememberScrollState
|
||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.SnapshotMutationPolicy
|
import androidx.compose.runtime.SnapshotMutationPolicy
|
||||||
|
import androidx.compose.runtime.collectAsState
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.saveable.autoSaver
|
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
@ -39,12 +39,9 @@ import it.vfsfitvnm.vimusic.utils.isAtLeastAndroid6
|
||||||
import it.vfsfitvnm.vimusic.utils.isIgnoringBatteryOptimizations
|
import it.vfsfitvnm.vimusic.utils.isIgnoringBatteryOptimizations
|
||||||
import it.vfsfitvnm.vimusic.utils.isInvincibilityEnabledKey
|
import it.vfsfitvnm.vimusic.utils.isInvincibilityEnabledKey
|
||||||
import it.vfsfitvnm.vimusic.utils.pauseSearchHistoryKey
|
import it.vfsfitvnm.vimusic.utils.pauseSearchHistoryKey
|
||||||
import it.vfsfitvnm.vimusic.utils.produceSaveableState
|
|
||||||
import it.vfsfitvnm.vimusic.utils.rememberPreference
|
import it.vfsfitvnm.vimusic.utils.rememberPreference
|
||||||
import it.vfsfitvnm.vimusic.utils.toast
|
import it.vfsfitvnm.vimusic.utils.toast
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||||
import kotlinx.coroutines.flow.flowOn
|
|
||||||
|
|
||||||
@SuppressLint("BatteryLife")
|
@SuppressLint("BatteryLife")
|
||||||
@ExperimentalAnimationApi
|
@ExperimentalAnimationApi
|
||||||
|
@ -86,12 +83,9 @@ fun OtherSettings() {
|
||||||
|
|
||||||
var pauseSearchHistory by rememberPreference(pauseSearchHistoryKey, false)
|
var pauseSearchHistory by rememberPreference(pauseSearchHistoryKey, false)
|
||||||
|
|
||||||
val queriesCount by produceSaveableState(initialValue = 0, stateSaver = autoSaver()) {
|
val queriesCount by remember {
|
||||||
Database.queriesCount()
|
Database.queriesCount().distinctUntilChanged()
|
||||||
.flowOn(Dispatchers.IO)
|
}.collectAsState(initial = 0)
|
||||||
.distinctUntilChanged()
|
|
||||||
.collect { value = it }
|
|
||||||
}
|
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
|
|
|
@ -11,6 +11,7 @@ import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.alpha
|
import androidx.compose.ui.draw.alpha
|
||||||
import androidx.compose.ui.graphics.*
|
import androidx.compose.ui.graphics.*
|
||||||
|
import androidx.compose.ui.platform.LocalSavedStateRegistryOwner
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import it.vfsfitvnm.route.*
|
import it.vfsfitvnm.route.*
|
||||||
import it.vfsfitvnm.vimusic.R
|
import it.vfsfitvnm.vimusic.R
|
||||||
|
|
|
@ -1,82 +0,0 @@
|
||||||
@file:OptIn(ExperimentalTypeInference::class)
|
|
||||||
|
|
||||||
package it.vfsfitvnm.vimusic.utils
|
|
||||||
|
|
||||||
import androidx.compose.runtime.Composable
|
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
|
||||||
import androidx.compose.runtime.MutableState
|
|
||||||
import androidx.compose.runtime.ProduceStateScope
|
|
||||||
import androidx.compose.runtime.State
|
|
||||||
import androidx.compose.runtime.mutableStateOf
|
|
||||||
import androidx.compose.runtime.saveable.Saver
|
|
||||||
import androidx.compose.runtime.saveable.rememberSaveable
|
|
||||||
import kotlin.coroutines.CoroutineContext
|
|
||||||
import kotlin.experimental.ExperimentalTypeInference
|
|
||||||
import kotlinx.coroutines.suspendCancellableCoroutine
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun <T> produceSaveableState(
|
|
||||||
initialValue: T,
|
|
||||||
stateSaver: Saver<T, out Any>,
|
|
||||||
@BuilderInference producer: suspend ProduceStateScope<T>.() -> Unit
|
|
||||||
): State<T> {
|
|
||||||
val result = rememberSaveable(stateSaver = stateSaver) {
|
|
||||||
mutableStateOf(initialValue)
|
|
||||||
}
|
|
||||||
|
|
||||||
LaunchedEffect(Unit) {
|
|
||||||
ProduceSaveableStateScope(result, coroutineContext).producer()
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun <T> produceSaveableState(
|
|
||||||
initialValue: T,
|
|
||||||
stateSaver: Saver<T, out Any>,
|
|
||||||
key1: Any?,
|
|
||||||
@BuilderInference producer: suspend ProduceStateScope<T>.() -> Unit
|
|
||||||
): State<T> {
|
|
||||||
val state = rememberSaveable(stateSaver = stateSaver) {
|
|
||||||
mutableStateOf(initialValue)
|
|
||||||
}
|
|
||||||
|
|
||||||
LaunchedEffect(key1) {
|
|
||||||
ProduceSaveableStateScope(state, coroutineContext).producer()
|
|
||||||
}
|
|
||||||
|
|
||||||
return state
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun <T> produceSaveableState(
|
|
||||||
initialValue: T,
|
|
||||||
stateSaver: Saver<T, out Any>,
|
|
||||||
key1: Any?,
|
|
||||||
key2: Any?,
|
|
||||||
@BuilderInference producer: suspend ProduceStateScope<T>.() -> Unit
|
|
||||||
): State<T> {
|
|
||||||
val result = rememberSaveable(stateSaver = stateSaver) {
|
|
||||||
mutableStateOf(initialValue)
|
|
||||||
}
|
|
||||||
|
|
||||||
LaunchedEffect(key1, key2) {
|
|
||||||
ProduceSaveableStateScope(result, coroutineContext).producer()
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
private class ProduceSaveableStateScope<T>(
|
|
||||||
state: MutableState<T>,
|
|
||||||
override val coroutineContext: CoroutineContext
|
|
||||||
) : ProduceStateScope<T>, MutableState<T> by state {
|
|
||||||
override suspend fun awaitDispose(onDispose: () -> Unit): Nothing {
|
|
||||||
try {
|
|
||||||
suspendCancellableCoroutine<Nothing> { }
|
|
||||||
} finally {
|
|
||||||
onDispose()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
1
compose-persist/.gitignore
vendored
Normal file
1
compose-persist/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
/build
|
46
compose-persist/build.gradle.kts
Normal file
46
compose-persist/build.gradle.kts
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
plugins {
|
||||||
|
id("com.android.library")
|
||||||
|
kotlin("android")
|
||||||
|
}
|
||||||
|
|
||||||
|
android {
|
||||||
|
namespace = "it.vfsfitvnm.compose.persist"
|
||||||
|
compileSdk = 33
|
||||||
|
|
||||||
|
defaultConfig {
|
||||||
|
minSdk = 21
|
||||||
|
targetSdk = 33
|
||||||
|
}
|
||||||
|
|
||||||
|
buildTypes {
|
||||||
|
release {
|
||||||
|
isMinifyEnabled = true
|
||||||
|
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceSets.all {
|
||||||
|
kotlin.srcDir("src/$name/kotlin")
|
||||||
|
}
|
||||||
|
|
||||||
|
buildFeatures {
|
||||||
|
compose = true
|
||||||
|
}
|
||||||
|
|
||||||
|
compileOptions {
|
||||||
|
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||||
|
targetCompatibility = JavaVersion.VERSION_1_8
|
||||||
|
}
|
||||||
|
|
||||||
|
composeOptions {
|
||||||
|
kotlinCompilerExtensionVersion = libs.versions.compose.compiler.get()
|
||||||
|
}
|
||||||
|
|
||||||
|
kotlinOptions {
|
||||||
|
jvmTarget = "1.8"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation(libs.compose.foundation)
|
||||||
|
}
|
4
compose-persist/src/main/AndroidManifest.xml
Normal file
4
compose-persist/src/main/AndroidManifest.xml
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest>
|
||||||
|
|
||||||
|
</manifest>
|
|
@ -0,0 +1,26 @@
|
||||||
|
package it.vfsfitvnm.compose.persist
|
||||||
|
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.MutableState
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
@Composable
|
||||||
|
fun <T> persist(tag: String, initialValue: T): MutableState<T> {
|
||||||
|
val context = LocalContext.current
|
||||||
|
|
||||||
|
return remember {
|
||||||
|
context.persistMap?.getOrPut(tag) { mutableStateOf(initialValue) } as? MutableState<T>
|
||||||
|
?: mutableStateOf(initialValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun <T> persistList(tag: String): MutableState<List<T>> =
|
||||||
|
persist(tag = tag, initialValue = emptyList())
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun <T : Any?> persist(tag: String): MutableState<T?> =
|
||||||
|
persist(tag = tag, initialValue = null)
|
|
@ -0,0 +1,3 @@
|
||||||
|
package it.vfsfitvnm.compose.persist
|
||||||
|
|
||||||
|
typealias PersistMap = HashMap<String, Any?>
|
|
@ -0,0 +1,19 @@
|
||||||
|
package it.vfsfitvnm.compose.persist
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.DisposableEffect
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun PersistMapCleanup(tagPrefix: String) {
|
||||||
|
val context = LocalContext.current
|
||||||
|
|
||||||
|
DisposableEffect(context) {
|
||||||
|
onDispose {
|
||||||
|
if (context.findOwner<Activity>()?.isChangingConfigurations == false) {
|
||||||
|
context.persistMap?.keys?.removeAll { it.startsWith(tagPrefix) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
package it.vfsfitvnm.compose.persist
|
||||||
|
|
||||||
|
interface PersistMapOwner {
|
||||||
|
val persistMap: PersistMap
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
package it.vfsfitvnm.compose.persist
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.ContextWrapper
|
||||||
|
|
||||||
|
val Context.persistMap: PersistMap?
|
||||||
|
get() = findOwner<PersistMapOwner>()?.persistMap
|
||||||
|
|
||||||
|
internal inline fun <reified T> Context.findOwner(): T? {
|
||||||
|
var context = this
|
||||||
|
while (context is ContextWrapper) {
|
||||||
|
if (context is T) return context
|
||||||
|
context = context.baseContext
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
|
@ -62,6 +62,7 @@ rootProject.name = "ViMusic"
|
||||||
include(":app")
|
include(":app")
|
||||||
include(":compose-routing")
|
include(":compose-routing")
|
||||||
include(":compose-reordering")
|
include(":compose-reordering")
|
||||||
|
include(":compose-persist")
|
||||||
include(":innertube")
|
include(":innertube")
|
||||||
include(":ktor-client-brotli")
|
include(":ktor-client-brotli")
|
||||||
include(":kugou")
|
include(":kugou")
|
||||||
|
|
Loading…
Reference in a new issue