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 {
|
||||
implementation(projects.composePersist)
|
||||
implementation(projects.composeRouting)
|
||||
implementation(projects.composeReordering)
|
||||
|
||||
|
|
|
@ -58,6 +58,8 @@ import androidx.media3.common.MediaItem
|
|||
import androidx.media3.common.Player
|
||||
import com.valentinilk.shimmer.LocalShimmerTheme
|
||||
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.models.bodies.BrowseBody
|
||||
import it.vfsfitvnm.innertube.requests.playlistPage
|
||||
|
@ -99,7 +101,7 @@ import kotlinx.coroutines.flow.first
|
|||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
class MainActivity : ComponentActivity() {
|
||||
class MainActivity : ComponentActivity(), PersistMapOwner {
|
||||
private val serviceConnection = object : ServiceConnection {
|
||||
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
|
||||
if (service is PlayerService.Binder) {
|
||||
|
@ -114,20 +116,20 @@ class MainActivity : ComponentActivity() {
|
|||
|
||||
private var binder by mutableStateOf<PlayerService.Binder?>(null)
|
||||
|
||||
override lateinit var persistMap: PersistMap
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
bindService(intent<PlayerService>(), serviceConnection, Context.BIND_AUTO_CREATE)
|
||||
}
|
||||
|
||||
override fun onStop() {
|
||||
unbindService(serviceConnection)
|
||||
super.onStop()
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class, ExperimentalAnimationApi::class)
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
@Suppress("DEPRECATION", "UNCHECKED_CAST")
|
||||
persistMap = lastCustomNonConfigurationInstance as? PersistMap ?: PersistMap()
|
||||
|
||||
WindowCompat.setDecorFitsSystemWindows(window, false)
|
||||
|
||||
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) {
|
||||
with(WindowCompat.getInsetsController(window, window.decorView.rootView)) {
|
||||
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() {
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
println(intent.action)
|
||||
when (intent.action) {
|
||||
Action.pause.value -> player.pause()
|
||||
Action.play.value -> player.play()
|
||||
|
|
|
@ -17,17 +17,17 @@ import androidx.compose.ui.Modifier
|
|||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.unit.dp
|
||||
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.vimusic.Database
|
||||
import it.vfsfitvnm.vimusic.R
|
||||
import it.vfsfitvnm.vimusic.models.Album
|
||||
import it.vfsfitvnm.vimusic.models.SongAlbumMap
|
||||
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.HeaderIconButton
|
||||
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.px
|
||||
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.flow.combine
|
||||
import kotlinx.coroutines.withContext
|
||||
|
@ -58,13 +55,10 @@ fun AlbumScreen(browseId: String) {
|
|||
mutableStateOf(0)
|
||||
}
|
||||
|
||||
var album by rememberSaveable(stateSaver = nullableSaver(AlbumSaver)) {
|
||||
mutableStateOf(null)
|
||||
}
|
||||
var album by persist<Album?>("album/$browseId/album")
|
||||
var albumPage by persist<Innertube.PlaylistOrAlbumPage?>("album/$browseId/albumPage")
|
||||
|
||||
var albumPage by rememberSaveable(stateSaver = nullableSaver(InnertubePlaylistOrAlbumPageSaver)) {
|
||||
mutableStateOf(null)
|
||||
}
|
||||
PersistMapCleanup(tagPrefix = "album/$browseId/")
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
Database
|
||||
|
@ -205,7 +199,7 @@ fun AlbumScreen(browseId: String) {
|
|||
val thumbnailSizePx = thumbnailSizeDp.px
|
||||
|
||||
ItemsPage(
|
||||
stateSaver = innertubeItemsPageSaver(InnertubeAlbumItemListSaver),
|
||||
tag = "album/$browseId/alternatives",
|
||||
headerContent = headerContent,
|
||||
initialPlaceholderCount = 1,
|
||||
continuationPlaceholderCount = 1,
|
||||
|
|
|
@ -6,26 +6,28 @@ import androidx.compose.foundation.background
|
|||
import androidx.compose.foundation.combinedClickable
|
||||
import androidx.compose.foundation.layout.Box
|
||||
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.only
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.foundation.text.BasicText
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import it.vfsfitvnm.compose.persist.persistList
|
||||
import it.vfsfitvnm.vimusic.Database
|
||||
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.R
|
||||
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.ShimmerHost
|
||||
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.forcePlayFromBeginning
|
||||
import it.vfsfitvnm.vimusic.utils.isLandscape
|
||||
import it.vfsfitvnm.vimusic.utils.produceSaveableState
|
||||
import it.vfsfitvnm.vimusic.utils.semiBold
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.flowOn
|
||||
|
||||
@ExperimentalAnimationApi
|
||||
@ExperimentalFoundationApi
|
||||
|
@ -60,14 +59,10 @@ fun AlbumSongs(
|
|||
val binder = LocalPlayerServiceBinder.current
|
||||
val menuState = LocalMenuState.current
|
||||
|
||||
val songs by produceSaveableState(
|
||||
initialValue = emptyList(),
|
||||
stateSaver = DetailedSongListSaver
|
||||
) {
|
||||
Database
|
||||
.albumSongs(browseId)
|
||||
.flowOn(Dispatchers.IO)
|
||||
.collect { value = it }
|
||||
var songs by persistList<DetailedSong>("album/$browseId/songs")
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
Database.albumSongs(browseId).collect { songs = it }
|
||||
}
|
||||
|
||||
val thumbnailSizeDp = Dimensions.thumbnails.song
|
||||
|
|
|
@ -6,24 +6,25 @@ import androidx.compose.foundation.background
|
|||
import androidx.compose.foundation.combinedClickable
|
||||
import androidx.compose.foundation.layout.Box
|
||||
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.only
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import it.vfsfitvnm.compose.persist.persist
|
||||
import it.vfsfitvnm.vimusic.Database
|
||||
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.R
|
||||
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.ShimmerHost
|
||||
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.forcePlayAtIndex
|
||||
import it.vfsfitvnm.vimusic.utils.forcePlayFromBeginning
|
||||
import it.vfsfitvnm.vimusic.utils.produceSaveableState
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.flowOn
|
||||
|
||||
@ExperimentalFoundationApi
|
||||
@ExperimentalAnimationApi
|
||||
|
@ -55,14 +53,10 @@ fun ArtistLocalSongs(
|
|||
val (colorPalette) = LocalAppearance.current
|
||||
val menuState = LocalMenuState.current
|
||||
|
||||
val songs by produceSaveableState(
|
||||
initialValue = null,
|
||||
stateSaver = nullableSaver(DetailedSongListSaver)
|
||||
) {
|
||||
Database
|
||||
.artistSongs(browseId)
|
||||
.flowOn(Dispatchers.IO)
|
||||
.collect { value = it }
|
||||
var songs by persist<List<DetailedSong>?>("artist/$browseId/localSongs")
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
Database.artistSongs(browseId).collect { songs = it }
|
||||
}
|
||||
|
||||
val songThumbnailSizeDp = Dimensions.thumbnails.song
|
||||
|
|
|
@ -26,6 +26,8 @@ import androidx.compose.ui.Alignment
|
|||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
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.LocalPlayerServiceBinder
|
||||
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.secondary
|
||||
import it.vfsfitvnm.vimusic.utils.semiBold
|
||||
import it.vfsfitvnm.innertube.Innertube
|
||||
import it.vfsfitvnm.innertube.models.NavigationEndpoint
|
||||
|
||||
@ExperimentalFoundationApi
|
||||
@ExperimentalAnimationApi
|
||||
|
|
|
@ -10,8 +10,6 @@ import androidx.compose.foundation.shape.CircleShape
|
|||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
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.setValue
|
||||
import androidx.compose.runtime.snapshotFlow
|
||||
|
@ -19,17 +17,20 @@ import androidx.compose.ui.Modifier
|
|||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.unit.dp
|
||||
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.vimusic.Database
|
||||
import it.vfsfitvnm.vimusic.LocalPlayerServiceBinder
|
||||
import it.vfsfitvnm.vimusic.R
|
||||
import it.vfsfitvnm.vimusic.models.Artist
|
||||
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.themed.Header
|
||||
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.forcePlay
|
||||
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.flow.combine
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
|
@ -71,13 +66,11 @@ fun ArtistScreen(browseId: String) {
|
|||
|
||||
var tabIndex by rememberPreference(artistScreenTabIndexKey, defaultValue = 0)
|
||||
|
||||
var artist by rememberSaveable(stateSaver = nullableSaver(ArtistSaver)) {
|
||||
mutableStateOf(null)
|
||||
}
|
||||
PersistMapCleanup(tagPrefix = "artist/$browseId/")
|
||||
|
||||
var artistPage by rememberSaveable(stateSaver = nullableSaver(InnertubeArtistPageSaver)) {
|
||||
mutableStateOf(null)
|
||||
}
|
||||
var artist by persist<Artist?>("artist/$browseId/artist")
|
||||
|
||||
var artistPage by persist<Innertube.ArtistPage?>("artist/$browseId/artistPage")
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
Database
|
||||
|
@ -209,7 +202,7 @@ fun ArtistScreen(browseId: String) {
|
|||
val thumbnailSizePx = thumbnailSizeDp.px
|
||||
|
||||
ItemsPage(
|
||||
stateSaver = InnertubeSongsPageSaver,
|
||||
tag = "artist/$browseId/songs",
|
||||
headerContent = headerContent,
|
||||
itemsPageProvider = artistPage?.let {
|
||||
({ continuation ->
|
||||
|
@ -272,7 +265,7 @@ fun ArtistScreen(browseId: String) {
|
|||
val thumbnailSizePx = thumbnailSizeDp.px
|
||||
|
||||
ItemsPage(
|
||||
stateSaver = InnertubeAlbumsPageSaver,
|
||||
tag = "artist/$browseId/albums",
|
||||
headerContent = headerContent,
|
||||
emptyItemsText = "This artist didn't release any album",
|
||||
itemsPageProvider = artistPage?.let {
|
||||
|
@ -322,7 +315,7 @@ fun ArtistScreen(browseId: String) {
|
|||
val thumbnailSizePx = thumbnailSizeDp.px
|
||||
|
||||
ItemsPage(
|
||||
stateSaver = InnertubeAlbumsPageSaver,
|
||||
tag = "artist/$browseId/singles",
|
||||
headerContent = headerContent,
|
||||
emptyItemsText = "This artist didn't release any single",
|
||||
itemsPageProvider = artistPage?.let {
|
||||
|
|
|
@ -6,6 +6,7 @@ import androidx.compose.runtime.Composable
|
|||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.saveable.rememberSaveableStateHolder
|
||||
import it.vfsfitvnm.compose.persist.PersistMapCleanup
|
||||
import it.vfsfitvnm.route.RouteHandler
|
||||
import it.vfsfitvnm.vimusic.R
|
||||
import it.vfsfitvnm.vimusic.enums.BuiltInPlaylist
|
||||
|
@ -25,6 +26,8 @@ fun BuiltInPlaylistScreen(builtInPlaylist: BuiltInPlaylist) {
|
|||
})
|
||||
}
|
||||
|
||||
PersistMapCleanup(tagPrefix = "${builtInPlaylist.name}/")
|
||||
|
||||
RouteHandler(listenToGlobalEmitter = true) {
|
||||
globalRoutes()
|
||||
|
||||
|
|
|
@ -15,16 +15,18 @@ import androidx.compose.foundation.lazy.LazyColumn
|
|||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import it.vfsfitvnm.compose.persist.persistList
|
||||
import it.vfsfitvnm.vimusic.Database
|
||||
import it.vfsfitvnm.vimusic.LocalPlayerAwareWindowInsets
|
||||
import it.vfsfitvnm.vimusic.LocalPlayerServiceBinder
|
||||
import it.vfsfitvnm.vimusic.R
|
||||
import it.vfsfitvnm.vimusic.enums.BuiltInPlaylist
|
||||
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.themed.FloatingActionsContainerWithScrollToTop
|
||||
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.forcePlayAtIndex
|
||||
import it.vfsfitvnm.vimusic.utils.forcePlayFromBeginning
|
||||
import it.vfsfitvnm.vimusic.utils.produceSaveableState
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.flowOn
|
||||
import kotlinx.coroutines.flow.map
|
||||
|
@ -52,14 +53,13 @@ fun BuiltInPlaylistSongs(builtInPlaylist: BuiltInPlaylist) {
|
|||
val binder = LocalPlayerServiceBinder.current
|
||||
val menuState = LocalMenuState.current
|
||||
|
||||
val songs by produceSaveableState(
|
||||
initialValue = emptyList(),
|
||||
stateSaver = DetailedSongListSaver
|
||||
) {
|
||||
var songs by persistList<DetailedSong>("${builtInPlaylist.name}/songs")
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
when (builtInPlaylist) {
|
||||
BuiltInPlaylist.Favorites -> Database
|
||||
.favorites()
|
||||
.flowOn(Dispatchers.IO)
|
||||
|
||||
BuiltInPlaylist.Offline -> Database
|
||||
.songsWithContentLength()
|
||||
.flowOn(Dispatchers.IO)
|
||||
|
@ -70,7 +70,7 @@ fun BuiltInPlaylistSongs(builtInPlaylist: BuiltInPlaylist) {
|
|||
} ?: false
|
||||
}
|
||||
}
|
||||
}.collect { value = it }
|
||||
}.collect { songs = it }
|
||||
}
|
||||
|
||||
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.width
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.LazyListState
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.graphicsLayer
|
||||
import androidx.compose.ui.unit.dp
|
||||
import it.vfsfitvnm.compose.persist.persist
|
||||
import it.vfsfitvnm.vimusic.Database
|
||||
import it.vfsfitvnm.vimusic.LocalPlayerAwareWindowInsets
|
||||
import it.vfsfitvnm.vimusic.R
|
||||
import it.vfsfitvnm.vimusic.enums.AlbumSortBy
|
||||
import it.vfsfitvnm.vimusic.enums.SortOrder
|
||||
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.Header
|
||||
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.utils.albumSortByKey
|
||||
import it.vfsfitvnm.vimusic.utils.albumSortOrderKey
|
||||
import it.vfsfitvnm.vimusic.utils.produceSaveableState
|
||||
import it.vfsfitvnm.vimusic.utils.rememberPreference
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.flowOn
|
||||
|
||||
@ExperimentalFoundationApi
|
||||
@ExperimentalAnimationApi
|
||||
|
@ -56,15 +56,10 @@ fun HomeAlbums(
|
|||
var sortBy by rememberPreference(albumSortByKey, AlbumSortBy.DateAdded)
|
||||
var sortOrder by rememberPreference(albumSortOrderKey, SortOrder.Descending)
|
||||
|
||||
val items by produceSaveableState(
|
||||
initialValue = emptyList(),
|
||||
stateSaver = AlbumListSaver,
|
||||
sortBy, sortOrder,
|
||||
) {
|
||||
Database
|
||||
.albums(sortBy, sortOrder)
|
||||
.flowOn(Dispatchers.IO)
|
||||
.collect { value = it }
|
||||
var items by persist<List<Album>>(tag = "home/albums", emptyList())
|
||||
|
||||
LaunchedEffect(sortBy, sortOrder) {
|
||||
Database.albums(sortBy, sortOrder).collect { items = it }
|
||||
}
|
||||
|
||||
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.Box
|
||||
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.only
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.lazy.grid.GridCells
|
||||
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.rememberLazyGridState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.graphicsLayer
|
||||
import androidx.compose.ui.unit.dp
|
||||
import it.vfsfitvnm.compose.persist.persistList
|
||||
import it.vfsfitvnm.vimusic.Database
|
||||
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.enums.ArtistSortBy
|
||||
import it.vfsfitvnm.vimusic.enums.SortOrder
|
||||
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.Header
|
||||
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.utils.artistSortByKey
|
||||
import it.vfsfitvnm.vimusic.utils.artistSortOrderKey
|
||||
import it.vfsfitvnm.vimusic.utils.produceSaveableState
|
||||
import it.vfsfitvnm.vimusic.utils.rememberPreference
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.flowOn
|
||||
|
||||
@ExperimentalFoundationApi
|
||||
@ExperimentalAnimationApi
|
||||
|
@ -60,15 +58,10 @@ fun HomeArtistList(
|
|||
var sortBy by rememberPreference(artistSortByKey, ArtistSortBy.DateAdded)
|
||||
var sortOrder by rememberPreference(artistSortOrderKey, SortOrder.Descending)
|
||||
|
||||
val items by produceSaveableState(
|
||||
initialValue = emptyList(),
|
||||
stateSaver = ArtistListSaver,
|
||||
sortBy, sortOrder,
|
||||
) {
|
||||
Database
|
||||
.artists(sortBy, sortOrder)
|
||||
.flowOn(Dispatchers.IO)
|
||||
.collect { value = it }
|
||||
var items by persistList<Artist>("home/artists")
|
||||
|
||||
LaunchedEffect(sortBy, sortOrder) {
|
||||
Database.artists(sortBy, sortOrder).collect { items = it }
|
||||
}
|
||||
|
||||
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.rememberLazyGridState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
|
@ -29,6 +30,7 @@ import androidx.compose.ui.Alignment
|
|||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.graphicsLayer
|
||||
import androidx.compose.ui.unit.dp
|
||||
import it.vfsfitvnm.compose.persist.persistList
|
||||
import it.vfsfitvnm.vimusic.Database
|
||||
import it.vfsfitvnm.vimusic.LocalPlayerAwareWindowInsets
|
||||
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.SortOrder
|
||||
import it.vfsfitvnm.vimusic.models.Playlist
|
||||
import it.vfsfitvnm.vimusic.models.PlaylistPreview
|
||||
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.Header
|
||||
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.utils.playlistSortByKey
|
||||
import it.vfsfitvnm.vimusic.utils.playlistSortOrderKey
|
||||
import it.vfsfitvnm.vimusic.utils.produceSaveableState
|
||||
import it.vfsfitvnm.vimusic.utils.rememberPreference
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.flowOn
|
||||
|
||||
@ExperimentalAnimationApi
|
||||
@ExperimentalFoundationApi
|
||||
|
@ -85,15 +84,10 @@ fun HomePlaylists(
|
|||
var sortBy by rememberPreference(playlistSortByKey, PlaylistSortBy.DateAdded)
|
||||
var sortOrder by rememberPreference(playlistSortOrderKey, SortOrder.Descending)
|
||||
|
||||
val items by produceSaveableState(
|
||||
initialValue = emptyList(),
|
||||
stateSaver = PlaylistPreviewListSaver,
|
||||
sortBy, sortOrder,
|
||||
) {
|
||||
Database
|
||||
.playlistPreviews(sortBy, sortOrder)
|
||||
.flowOn(Dispatchers.IO)
|
||||
.collect { value = it }
|
||||
var items by persistList<PlaylistPreview>("home/playlists")
|
||||
|
||||
LaunchedEffect(sortBy, sortOrder) {
|
||||
Database.playlistPreviews(sortBy, sortOrder).collect { items = it }
|
||||
}
|
||||
|
||||
val sortOrderIconRotation by animateFloatAsState(
|
||||
|
|
|
@ -5,6 +5,7 @@ import androidx.compose.foundation.ExperimentalFoundationApi
|
|||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.saveable.rememberSaveableStateHolder
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import it.vfsfitvnm.compose.persist.PersistMapCleanup
|
||||
import it.vfsfitvnm.route.RouteHandler
|
||||
import it.vfsfitvnm.route.defaultStacking
|
||||
import it.vfsfitvnm.route.defaultStill
|
||||
|
@ -42,6 +43,8 @@ import it.vfsfitvnm.vimusic.utils.rememberPreference
|
|||
fun HomeScreen(onPlaylistUrl: (String) -> Unit) {
|
||||
val saveableStateHolder = rememberSaveableStateHolder()
|
||||
|
||||
PersistMapCleanup("home/")
|
||||
|
||||
RouteHandler(
|
||||
listenToGlobalEmitter = true,
|
||||
transitionSpec = {
|
||||
|
|
|
@ -21,6 +21,7 @@ import androidx.compose.foundation.lazy.itemsIndexed
|
|||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.foundation.text.BasicText
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.setValue
|
||||
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.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import it.vfsfitvnm.compose.persist.persistList
|
||||
import it.vfsfitvnm.vimusic.Database
|
||||
import it.vfsfitvnm.vimusic.LocalPlayerAwareWindowInsets
|
||||
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.SortOrder
|
||||
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.themed.FloatingActionsContainerWithScrollToTop
|
||||
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.color
|
||||
import it.vfsfitvnm.vimusic.utils.forcePlayAtIndex
|
||||
import it.vfsfitvnm.vimusic.utils.produceSaveableState
|
||||
import it.vfsfitvnm.vimusic.utils.rememberPreference
|
||||
import it.vfsfitvnm.vimusic.utils.semiBold
|
||||
import it.vfsfitvnm.vimusic.utils.songSortByKey
|
||||
import it.vfsfitvnm.vimusic.utils.songSortOrderKey
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.flowOn
|
||||
|
||||
@ExperimentalFoundationApi
|
||||
@ExperimentalAnimationApi
|
||||
|
@ -77,15 +75,10 @@ fun HomeSongs(
|
|||
var sortBy by rememberPreference(songSortByKey, SongSortBy.DateAdded)
|
||||
var sortOrder by rememberPreference(songSortOrderKey, SortOrder.Descending)
|
||||
|
||||
val items by produceSaveableState(
|
||||
initialValue = emptyList(),
|
||||
stateSaver = DetailedSongListSaver,
|
||||
sortBy, sortOrder,
|
||||
) {
|
||||
Database
|
||||
.songs(sortBy, sortOrder)
|
||||
.flowOn(Dispatchers.IO)
|
||||
.collect { value = it }
|
||||
var items by persistList<DetailedSong>("home/songs")
|
||||
|
||||
LaunchedEffect(sortBy, sortOrder) {
|
||||
Database.songs(sortBy, sortOrder).collect { items = it }
|
||||
}
|
||||
|
||||
val sortOrderIconRotation by animateFloatAsState(
|
||||
|
|
|
@ -31,24 +31,24 @@ import androidx.compose.foundation.verticalScroll
|
|||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.ColorFilter
|
||||
import androidx.compose.ui.res.painterResource
|
||||
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.LocalPlayerAwareWindowInsets
|
||||
import it.vfsfitvnm.vimusic.LocalPlayerServiceBinder
|
||||
import it.vfsfitvnm.vimusic.R
|
||||
import it.vfsfitvnm.vimusic.models.DetailedSong
|
||||
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.ShimmerHost
|
||||
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.center
|
||||
import it.vfsfitvnm.vimusic.utils.forcePlay
|
||||
import it.vfsfitvnm.vimusic.utils.isLandscape
|
||||
import it.vfsfitvnm.vimusic.utils.secondary
|
||||
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.flowOn
|
||||
|
||||
@ExperimentalFoundationApi
|
||||
@ExperimentalAnimationApi
|
||||
|
@ -95,19 +89,12 @@ fun QuickPicks(
|
|||
val menuState = LocalMenuState.current
|
||||
val windowInsets = LocalPlayerAwareWindowInsets.current
|
||||
|
||||
var trending by rememberSaveable(stateSaver = nullableSaver(DetailedSongSaver)) {
|
||||
mutableStateOf(null)
|
||||
}
|
||||
var trending by persist<DetailedSong?>("home/trending")
|
||||
|
||||
var relatedPageResult by rememberSaveable(stateSaver = resultSaver(nullableSaver(InnertubeRelatedPageSaver))) {
|
||||
mutableStateOf(null)
|
||||
}
|
||||
var relatedPageResult by persist<Result<Innertube.RelatedPage?>?>(tag = "home/relatedPageResult")
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
Database.trending()
|
||||
.flowOn(Dispatchers.IO)
|
||||
.distinctUntilChanged()
|
||||
.collect { song ->
|
||||
Database.trending().distinctUntilChanged().collect { song ->
|
||||
if ((song == null && relatedPageResult == null) || trending?.id != song?.id) {
|
||||
relatedPageResult =
|
||||
Innertube.relatedPage(NextBody(videoId = (song?.id ?: "J7p4bzqLvCw")))
|
||||
|
|
|
@ -4,6 +4,7 @@ import androidx.compose.animation.ExperimentalAnimationApi
|
|||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.saveable.rememberSaveableStateHolder
|
||||
import it.vfsfitvnm.compose.persist.PersistMapCleanup
|
||||
import it.vfsfitvnm.route.RouteHandler
|
||||
import it.vfsfitvnm.vimusic.R
|
||||
import it.vfsfitvnm.vimusic.ui.components.themed.Scaffold
|
||||
|
@ -15,6 +16,8 @@ import it.vfsfitvnm.vimusic.ui.screens.globalRoutes
|
|||
fun LocalPlaylistScreen(playlistId: Long) {
|
||||
val saveableStateHolder = rememberSaveableStateHolder()
|
||||
|
||||
PersistMapCleanup(tagPrefix = "localPlaylist/$playlistId/")
|
||||
|
||||
RouteHandler(listenToGlobalEmitter = true) {
|
||||
globalRoutes()
|
||||
|
||||
|
@ -28,8 +31,9 @@ fun LocalPlaylistScreen(playlistId: Long) {
|
|||
Item(0, "Songs", R.drawable.musical_notes)
|
||||
}
|
||||
) { currentTabIndex ->
|
||||
saveableStateHolder.SaveableStateProvider(key = currentTabIndex) {
|
||||
LocalPlaylistSongs(
|
||||
saveableStateHolder.SaveableStateProvider(currentTabIndex) {
|
||||
when (currentTabIndex) {
|
||||
0 -> LocalPlaylistSongs(
|
||||
playlistId = playlistId,
|
||||
onDelete = pop
|
||||
)
|
||||
|
@ -38,3 +42,4 @@ fun LocalPlaylistScreen(playlistId: Long) {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,12 +16,17 @@ import androidx.compose.foundation.lazy.itemsIndexed
|
|||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.material.ripple.rememberRipple
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
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.animateItemPlacement
|
||||
import it.vfsfitvnm.reordering.draggedItem
|
||||
|
@ -32,10 +37,9 @@ import it.vfsfitvnm.vimusic.LocalPlayerAwareWindowInsets
|
|||
import it.vfsfitvnm.vimusic.LocalPlayerServiceBinder
|
||||
import it.vfsfitvnm.vimusic.R
|
||||
import it.vfsfitvnm.vimusic.models.DetailedSong
|
||||
import it.vfsfitvnm.vimusic.models.PlaylistWithSongs
|
||||
import it.vfsfitvnm.vimusic.models.SongPlaylistMap
|
||||
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.ui.components.LocalMenuState
|
||||
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.forcePlayAtIndex
|
||||
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.flow.filterNotNull
|
||||
import kotlinx.coroutines.runBlocking
|
||||
|
@ -77,14 +77,10 @@ fun LocalPlaylistSongs(
|
|||
val binder = LocalPlayerServiceBinder.current
|
||||
val menuState = LocalMenuState.current
|
||||
|
||||
val playlistWithSongs by produceSaveableState(
|
||||
initialValue = null,
|
||||
stateSaver = nullableSaver(PlaylistWithSongsSaver)
|
||||
) {
|
||||
Database
|
||||
.playlistWithSongs(playlistId)
|
||||
.filterNotNull()
|
||||
.collect { value = it }
|
||||
var playlistWithSongs by persist<PlaylistWithSongs?>("localPlaylist/$playlistId/playlistWithSongs")
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
Database.playlistWithSongs(playlistId).filterNotNull().collect { playlistWithSongs = it }
|
||||
}
|
||||
|
||||
val lazyListState = rememberLazyListState()
|
||||
|
|
|
@ -52,9 +52,7 @@ import it.vfsfitvnm.vimusic.utils.rememberPreference
|
|||
import it.vfsfitvnm.vimusic.utils.secondary
|
||||
import it.vfsfitvnm.vimusic.utils.semiBold
|
||||
import it.vfsfitvnm.vimusic.utils.trackLoopEnabledKey
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.flowOn
|
||||
|
||||
@Composable
|
||||
fun Controls(
|
||||
|
@ -82,11 +80,7 @@ fun Controls(
|
|||
}
|
||||
|
||||
LaunchedEffect(mediaId) {
|
||||
Database
|
||||
.likedAt(mediaId)
|
||||
.flowOn(Dispatchers.IO)
|
||||
.distinctUntilChanged()
|
||||
.collect { likedAt = it }
|
||||
Database.likedAt(mediaId).distinctUntilChanged().collect { likedAt = it }
|
||||
}
|
||||
|
||||
val shouldBePlayingTransition = updateTransition(shouldBePlaying, label = "shouldBePlaying")
|
||||
|
|
|
@ -32,7 +32,7 @@ import androidx.compose.runtime.LaunchedEffect
|
|||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
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.ui.Alignment
|
||||
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.isShowingSynchronizedLyricsKey
|
||||
import it.vfsfitvnm.vimusic.utils.medium
|
||||
import it.vfsfitvnm.vimusic.utils.produceSaveableState
|
||||
import it.vfsfitvnm.vimusic.utils.rememberPreference
|
||||
import it.vfsfitvnm.vimusic.utils.toast
|
||||
import it.vfsfitvnm.vimusic.utils.verticalFadingEdge
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.flowOn
|
||||
import kotlinx.coroutines.isActive
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
|
@ -108,19 +106,16 @@ fun Lyrics(
|
|||
mutableStateOf(false)
|
||||
}
|
||||
|
||||
val lyrics by produceSaveableState(
|
||||
initialValue = ".",
|
||||
stateSaver = autoSaver<String?>(),
|
||||
mediaId, isShowingSynchronizedLyrics
|
||||
) {
|
||||
var lyrics by rememberSaveable {
|
||||
mutableStateOf<String?>(".")
|
||||
}
|
||||
|
||||
LaunchedEffect(mediaId, isShowingSynchronizedLyrics) {
|
||||
if (isShowingSynchronizedLyrics) {
|
||||
Database.synchronizedLyrics(mediaId)
|
||||
} else {
|
||||
Database.lyrics(mediaId)
|
||||
}
|
||||
.flowOn(Dispatchers.IO)
|
||||
.distinctUntilChanged()
|
||||
.collect { value = it }
|
||||
}.distinctUntilChanged().collect { lyrics = it }
|
||||
}
|
||||
|
||||
var isError by remember(lyrics) {
|
||||
|
|
|
@ -4,6 +4,7 @@ import androidx.compose.animation.ExperimentalAnimationApi
|
|||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.saveable.rememberSaveableStateHolder
|
||||
import it.vfsfitvnm.compose.persist.PersistMapCleanup
|
||||
import it.vfsfitvnm.route.RouteHandler
|
||||
import it.vfsfitvnm.vimusic.R
|
||||
import it.vfsfitvnm.vimusic.ui.components.themed.Scaffold
|
||||
|
@ -14,6 +15,7 @@ import it.vfsfitvnm.vimusic.ui.screens.globalRoutes
|
|||
@Composable
|
||||
fun PlaylistScreen(browseId: String) {
|
||||
val saveableStateHolder = rememberSaveableStateHolder()
|
||||
PersistMapCleanup(tagPrefix = "playlist/$browseId")
|
||||
|
||||
RouteHandler(listenToGlobalEmitter = true) {
|
||||
globalRoutes()
|
||||
|
|
|
@ -8,11 +8,15 @@ import androidx.compose.foundation.combinedClickable
|
|||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
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.only
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
|
@ -21,18 +25,17 @@ import androidx.compose.ui.Alignment
|
|||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
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.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.R
|
||||
import it.vfsfitvnm.vimusic.models.Playlist
|
||||
import it.vfsfitvnm.vimusic.models.SongPlaylistMap
|
||||
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.ui.components.LocalMenuState
|
||||
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.forcePlayFromBeginning
|
||||
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.withContext
|
||||
|
||||
|
@ -74,13 +73,12 @@ fun PlaylistSongList(
|
|||
val context = LocalContext.current
|
||||
val menuState = LocalMenuState.current
|
||||
|
||||
val playlistPage by produceSaveableState(
|
||||
initialValue = null,
|
||||
stateSaver = nullableSaver(InnertubePlaylistOrAlbumPageSaver),
|
||||
) {
|
||||
if (value != null && value?.songsPage?.continuation == null) return@produceSaveableState
|
||||
var playlistPage by persist<Innertube.PlaylistOrAlbumPage?>("playlist/$browseId/playlistPage")
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,27 +4,30 @@ import androidx.compose.animation.ExperimentalAnimationApi
|
|||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.combinedClickable
|
||||
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.only
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.foundation.text.BasicTextField
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.SolidColor
|
||||
import androidx.compose.ui.text.input.ImeAction
|
||||
import androidx.compose.ui.text.input.TextFieldValue
|
||||
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.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.models.DetailedSong
|
||||
import it.vfsfitvnm.vimusic.savers.DetailedSongListSaver
|
||||
import it.vfsfitvnm.vimusic.ui.components.LocalMenuState
|
||||
import it.vfsfitvnm.vimusic.ui.components.themed.FloatingActionsContainerWithScrollToTop
|
||||
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.forcePlay
|
||||
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
|
||||
@ExperimentalAnimationApi
|
||||
|
@ -55,16 +54,11 @@ fun LocalSongSearch(
|
|||
val binder = LocalPlayerServiceBinder.current
|
||||
val menuState = LocalMenuState.current
|
||||
|
||||
val items by produceSaveableState(
|
||||
initialValue = emptyList(),
|
||||
stateSaver = DetailedSongListSaver,
|
||||
key1 = textFieldValue.text
|
||||
) {
|
||||
var items by persistList<DetailedSong>("search/local/songs")
|
||||
|
||||
LaunchedEffect(textFieldValue.text) {
|
||||
if (textFieldValue.text.length > 1) {
|
||||
Database
|
||||
.search("%${textFieldValue.text}%")
|
||||
.flowOn(Dispatchers.IO)
|
||||
.collect { value = it }
|
||||
Database.search("%${textFieldValue.text}%").collect { items = it }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -25,10 +25,7 @@ import androidx.compose.material.ripple.rememberRipple
|
|||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.saveable.autoSaver
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
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.unit.dp
|
||||
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.LocalPlayerAwareWindowInsets
|
||||
import it.vfsfitvnm.vimusic.R
|
||||
import it.vfsfitvnm.vimusic.models.SearchQuery
|
||||
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.Header
|
||||
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.center
|
||||
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.preferences
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import it.vfsfitvnm.vimusic.utils.secondary
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.flowOn
|
||||
|
||||
@ExperimentalAnimationApi
|
||||
@Composable
|
||||
|
@ -86,22 +79,17 @@ fun OnlineSearch(
|
|||
|
||||
val (colorPalette, typography) = LocalAppearance.current
|
||||
|
||||
val history by produceSaveableState(
|
||||
initialValue = emptyList(),
|
||||
stateSaver = listSaver(SearchQuerySaver),
|
||||
key1 = textFieldValue.text
|
||||
) {
|
||||
var history by persistList<SearchQuery>("search/online/history")
|
||||
|
||||
LaunchedEffect(textFieldValue.text) {
|
||||
if (!context.preferences.getBoolean(pauseSearchHistoryKey, false)) {
|
||||
Database.queries("%${textFieldValue.text}%")
|
||||
.flowOn(Dispatchers.IO)
|
||||
.distinctUntilChanged { old, new -> old.size == new.size }
|
||||
.collect { value = it }
|
||||
.collect { history = it }
|
||||
}
|
||||
}
|
||||
|
||||
var suggestionsResult by rememberSaveable(stateSaver = resultSaver(autoSaver<List<String>?>())) {
|
||||
mutableStateOf(null)
|
||||
}
|
||||
var suggestionsResult by persist<Result<List<String>?>?>("search/online/suggestionsResult")
|
||||
|
||||
LaunchedEffect(textFieldValue.text) {
|
||||
if (textFieldValue.text.isNotEmpty()) {
|
||||
|
|
|
@ -16,6 +16,7 @@ import androidx.compose.ui.Alignment
|
|||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.text.TextRange
|
||||
import androidx.compose.ui.text.input.TextFieldValue
|
||||
import it.vfsfitvnm.compose.persist.PersistMapCleanup
|
||||
import it.vfsfitvnm.route.RouteHandler
|
||||
import it.vfsfitvnm.vimusic.R
|
||||
import it.vfsfitvnm.vimusic.ui.components.themed.Scaffold
|
||||
|
@ -49,6 +50,8 @@ fun SearchScreen(
|
|||
)
|
||||
}
|
||||
|
||||
PersistMapCleanup(tagPrefix = "search/")
|
||||
|
||||
RouteHandler(listenToGlobalEmitter = true) {
|
||||
globalRoutes()
|
||||
|
||||
|
|
|
@ -2,8 +2,11 @@ package it.vfsfitvnm.vimusic.ui.screens.searchresult
|
|||
|
||||
import androidx.compose.animation.ExperimentalAnimationApi
|
||||
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.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.only
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
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.text.BasicText
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.rememberUpdatedState
|
||||
import androidx.compose.runtime.saveable.Saver
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.runtime.snapshotFlow
|
||||
import androidx.compose.ui.Modifier
|
||||
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 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.themed.FloatingActionsContainerWithScrollToTop
|
||||
import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance
|
||||
import it.vfsfitvnm.vimusic.utils.center
|
||||
import it.vfsfitvnm.vimusic.utils.produceSaveableState
|
||||
import it.vfsfitvnm.vimusic.utils.secondary
|
||||
import it.vfsfitvnm.innertube.Innertube
|
||||
import it.vfsfitvnm.innertube.utils.plus
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
@ExperimentalAnimationApi
|
||||
@Composable
|
||||
inline fun <T : Innertube.Item> ItemsPage(
|
||||
stateSaver: Saver<Innertube.ItemsPage<T>, List<Any?>>,
|
||||
tag: String,
|
||||
crossinline headerContent: @Composable (textButton: (@Composable () -> Unit)?) -> Unit,
|
||||
crossinline itemContent: @Composable LazyItemScope.(T) -> Unit,
|
||||
noinline itemPlaceholderContent: @Composable () -> Unit,
|
||||
|
@ -47,29 +47,29 @@ inline fun <T : Innertube.Item> ItemsPage(
|
|||
noinline itemsPageProvider: (suspend (String?) -> Result<Innertube.ItemsPage<T>?>?)? = null,
|
||||
) {
|
||||
val (_, typography) = LocalAppearance.current
|
||||
val lazyListState = rememberLazyListState()
|
||||
|
||||
val updatedItemsPageProvider by rememberUpdatedState(itemsPageProvider)
|
||||
|
||||
val itemsPage by produceSaveableState(
|
||||
initialValue = null,
|
||||
stateSaver = nullableSaver(stateSaver),
|
||||
lazyListState, updatedItemsPageProvider
|
||||
) {
|
||||
val currentItemsPageProvider = updatedItemsPageProvider ?: return@produceSaveableState
|
||||
val lazyListState = rememberLazyListState()
|
||||
|
||||
var itemsPage by persist<Innertube.ItemsPage<T>?>(tag)
|
||||
|
||||
LaunchedEffect(lazyListState, updatedItemsPageProvider) {
|
||||
val currentItemsPageProvider = updatedItemsPageProvider ?: return@LaunchedEffect
|
||||
|
||||
snapshotFlow { lazyListState.layoutInfo.visibleItemsInfo.any { it.key == "loading" } }
|
||||
.collect { shouldLoadMore ->
|
||||
if (!shouldLoadMore) return@collect
|
||||
|
||||
withContext(Dispatchers.IO) {
|
||||
currentItemsPageProvider(value?.continuation)
|
||||
currentItemsPageProvider(itemsPage?.continuation)
|
||||
}?.onSuccess {
|
||||
if (it == null) {
|
||||
if (value == null) {
|
||||
value = Innertube.ItemsPage(null, null)
|
||||
if (itemsPage == null) {
|
||||
itemsPage = Innertube.ItemsPage(null, null)
|
||||
}
|
||||
} else {
|
||||
value += it
|
||||
itemsPage += it
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,16 +9,18 @@ import androidx.compose.runtime.Composable
|
|||
import androidx.compose.runtime.saveable.rememberSaveableStateHolder
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.input.pointer.pointerInput
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
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.vimusic.LocalPlayerServiceBinder
|
||||
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.themed.Header
|
||||
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.rememberPreference
|
||||
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
|
||||
@ExperimentalAnimationApi
|
||||
@Composable
|
||||
fun SearchResultScreen(query: String, onSearchAgain: () -> Unit) {
|
||||
val context = LocalContext.current
|
||||
val saveableStateHolder = rememberSaveableStateHolder()
|
||||
val (tabIndex, onTabIndexChanges) = rememberPreference(searchResultScreenTabIndexKey, 0)
|
||||
|
||||
PersistMapCleanup(tagPrefix = "searchResults/$query/")
|
||||
|
||||
RouteHandler(listenToGlobalEmitter = true) {
|
||||
globalRoutes()
|
||||
|
||||
|
@ -66,6 +66,9 @@ fun SearchResultScreen(query: String, onSearchAgain: () -> Unit) {
|
|||
modifier = Modifier
|
||||
.pointerInput(Unit) {
|
||||
detectTapGestures {
|
||||
context.persistMap?.keys?.removeAll {
|
||||
it.startsWith("searchResults/$query/")
|
||||
}
|
||||
onSearchAgain()
|
||||
}
|
||||
}
|
||||
|
@ -74,8 +77,6 @@ fun SearchResultScreen(query: String, onSearchAgain: () -> Unit) {
|
|||
|
||||
val emptyItemsText = "No results found. Please try a different query or category"
|
||||
|
||||
|
||||
|
||||
Scaffold(
|
||||
topIconButtonId = R.drawable.chevron_back,
|
||||
onTopIconButtonClick = pop,
|
||||
|
@ -99,7 +100,7 @@ fun SearchResultScreen(query: String, onSearchAgain: () -> Unit) {
|
|||
val thumbnailSizePx = thumbnailSizeDp.px
|
||||
|
||||
ItemsPage(
|
||||
stateSaver = InnertubeSongsPageSaver,
|
||||
tag = "searchResults/$query/songs",
|
||||
itemsPageProvider = { continuation ->
|
||||
if (continuation == null) {
|
||||
Innertube.searchPage(
|
||||
|
@ -149,7 +150,7 @@ fun SearchResultScreen(query: String, onSearchAgain: () -> Unit) {
|
|||
val thumbnailSizePx = thumbnailSizeDp.px
|
||||
|
||||
ItemsPage(
|
||||
stateSaver = InnertubeAlbumsPageSaver,
|
||||
tag = "searchResults/$query/albums",
|
||||
itemsPageProvider = { continuation ->
|
||||
if (continuation == null) {
|
||||
Innertube.searchPage(
|
||||
|
@ -186,7 +187,7 @@ fun SearchResultScreen(query: String, onSearchAgain: () -> Unit) {
|
|||
val thumbnailSizePx = thumbnailSizeDp.px
|
||||
|
||||
ItemsPage(
|
||||
stateSaver = innertubeItemsPageSaver(InnertubeArtistItemListSaver),
|
||||
tag = "searchResults/$query/artists",
|
||||
itemsPageProvider = { continuation ->
|
||||
if (continuation == null) {
|
||||
Innertube.searchPage(
|
||||
|
@ -224,7 +225,7 @@ fun SearchResultScreen(query: String, onSearchAgain: () -> Unit) {
|
|||
val thumbnailWidthDp = 128.dp
|
||||
|
||||
ItemsPage(
|
||||
stateSaver = innertubeItemsPageSaver(InnertubeVideoItemListSaver),
|
||||
tag = "searchResults/$query/videos",
|
||||
itemsPageProvider = { continuation ->
|
||||
if (continuation == null) {
|
||||
Innertube.searchPage(
|
||||
|
@ -277,7 +278,7 @@ fun SearchResultScreen(query: String, onSearchAgain: () -> Unit) {
|
|||
val thumbnailSizePx = thumbnailSizeDp.px
|
||||
|
||||
ItemsPage(
|
||||
stateSaver = innertubeItemsPageSaver(InnertubePlaylistItemListSaver),
|
||||
tag = "searchResults/$query/${if (tabIndex == 4) "playlists" else "featured"}",
|
||||
itemsPageProvider = { continuation ->
|
||||
if (continuation == null) {
|
||||
val filter = if (tabIndex == 4) {
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package it.vfsfitvnm.vimusic.ui.screens.settings
|
||||
|
||||
import android.os.Build
|
||||
import androidx.compose.animation.ExperimentalAnimationApi
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.border
|
||||
|
|
|
@ -15,8 +15,9 @@ import androidx.compose.foundation.layout.padding
|
|||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
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.platform.LocalContext
|
||||
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.styling.LocalAppearance
|
||||
import it.vfsfitvnm.vimusic.utils.intent
|
||||
import it.vfsfitvnm.vimusic.utils.produceSaveableState
|
||||
import it.vfsfitvnm.vimusic.utils.toast
|
||||
import java.io.FileInputStream
|
||||
import java.io.FileOutputStream
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Date
|
||||
import kotlin.system.exitProcess
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.flowOn
|
||||
|
||||
@ExperimentalAnimationApi
|
||||
@Composable
|
||||
|
@ -45,12 +43,9 @@ fun DatabaseSettings() {
|
|||
val context = LocalContext.current
|
||||
val (colorPalette) = LocalAppearance.current
|
||||
|
||||
val eventsCount by produceSaveableState(initialValue = 0, stateSaver = autoSaver()) {
|
||||
Database.eventsCount()
|
||||
.flowOn(Dispatchers.IO)
|
||||
.distinctUntilChanged()
|
||||
.collect { value = it }
|
||||
}
|
||||
val eventsCount by remember {
|
||||
Database.eventsCount().distinctUntilChanged()
|
||||
}.collectAsState(initial = 0)
|
||||
|
||||
val backupLauncher =
|
||||
rememberLauncherForActivityResult(ActivityResultContracts.CreateDocument("application/vnd.sqlite3")) { uri ->
|
||||
|
|
|
@ -21,10 +21,10 @@ import androidx.compose.foundation.rememberScrollState
|
|||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.SnapshotMutationPolicy
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.saveable.autoSaver
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
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.isInvincibilityEnabledKey
|
||||
import it.vfsfitvnm.vimusic.utils.pauseSearchHistoryKey
|
||||
import it.vfsfitvnm.vimusic.utils.produceSaveableState
|
||||
import it.vfsfitvnm.vimusic.utils.rememberPreference
|
||||
import it.vfsfitvnm.vimusic.utils.toast
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.flowOn
|
||||
|
||||
@SuppressLint("BatteryLife")
|
||||
@ExperimentalAnimationApi
|
||||
|
@ -86,12 +83,9 @@ fun OtherSettings() {
|
|||
|
||||
var pauseSearchHistory by rememberPreference(pauseSearchHistoryKey, false)
|
||||
|
||||
val queriesCount by produceSaveableState(initialValue = 0, stateSaver = autoSaver()) {
|
||||
Database.queriesCount()
|
||||
.flowOn(Dispatchers.IO)
|
||||
.distinctUntilChanged()
|
||||
.collect { value = it }
|
||||
}
|
||||
val queriesCount by remember {
|
||||
Database.queriesCount().distinctUntilChanged()
|
||||
}.collectAsState(initial = 0)
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
|
|
|
@ -11,6 +11,7 @@ import androidx.compose.ui.Alignment
|
|||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.alpha
|
||||
import androidx.compose.ui.graphics.*
|
||||
import androidx.compose.ui.platform.LocalSavedStateRegistryOwner
|
||||
import androidx.compose.ui.unit.dp
|
||||
import it.vfsfitvnm.route.*
|
||||
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(":compose-routing")
|
||||
include(":compose-reordering")
|
||||
include(":compose-persist")
|
||||
include(":innertube")
|
||||
include(":ktor-client-brotli")
|
||||
include(":kugou")
|
||||
|
|
Loading…
Reference in a new issue