diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 17b8b8f..a6fb69e 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -70,6 +70,7 @@ kapt { } dependencies { + implementation(projects.composePersist) implementation(projects.composeRouting) implementation(projects.composeReordering) diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/MainActivity.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/MainActivity.kt index d50f36d..f663119 100644 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/MainActivity.kt +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/MainActivity.kt @@ -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(null) + override lateinit var persistMap: PersistMap + override fun onStart() { super.onStart() bindService(intent(), 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 diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/savers/AlbumSaver.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/savers/AlbumSaver.kt deleted file mode 100644 index 2a62bb4..0000000 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/savers/AlbumSaver.kt +++ /dev/null @@ -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> { - override fun SaverScope.save(value: Album): List = listOf( - value.id, - value.title, - value.thumbnailUrl, - value.year, - value.authorsText, - value.shareUrl, - value.timestamp, - value.bookmarkedAt, - ) - - override fun restore(value: List): 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) diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/savers/ArtistSaver.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/savers/ArtistSaver.kt deleted file mode 100644 index b3ce159..0000000 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/savers/ArtistSaver.kt +++ /dev/null @@ -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> { - override fun SaverScope.save(value: Artist): List = listOf( - value.id, - value.name, - value.thumbnailUrl, - value.timestamp, - value.bookmarkedAt, - ) - - override fun restore(value: List): 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) diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/savers/DetailedSongSaver.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/savers/DetailedSongSaver.kt deleted file mode 100644 index e6dd4a9..0000000 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/savers/DetailedSongSaver.kt +++ /dev/null @@ -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> { - 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) = 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>?)?.let(InfoListSaver::restore) - ) -} - -val DetailedSongListSaver = listSaver(DetailedSongSaver) diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/savers/InfoSaver.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/savers/InfoSaver.kt deleted file mode 100644 index 12bd22d..0000000 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/savers/InfoSaver.kt +++ /dev/null @@ -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> { - override fun SaverScope.save(value: Info) = listOf(value.id, value.name) - - override fun restore(value: List) = Info(id = value[0] as String, name = value[1]) -} - -val InfoListSaver = listSaver(InfoSaver) diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/savers/InnertubeAlbumItemSaver.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/savers/InnertubeAlbumItemSaver.kt deleted file mode 100644 index 11dfe8b..0000000 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/savers/InnertubeAlbumItemSaver.kt +++ /dev/null @@ -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> { - override fun SaverScope.save(value: Innertube.AlbumItem): List = 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) = Innertube.AlbumItem( - info = (value[0] as List?)?.let(InnertubeBrowseInfoSaver::restore), - authors = (value[1] as List>?)?.let(InnertubeBrowseInfoListSaver::restore), - year = value[2] as String?, - thumbnail = (value[3] as List?)?.let(InnertubeThumbnailSaver::restore) - ) -} - -val InnertubeAlbumItemListSaver = listSaver(InnertubeAlbumItemSaver) diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/savers/InnertubeArtistItemSaver.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/savers/InnertubeArtistItemSaver.kt deleted file mode 100644 index 5b706a1..0000000 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/savers/InnertubeArtistItemSaver.kt +++ /dev/null @@ -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> { - override fun SaverScope.save(value: Innertube.ArtistItem): List = listOf( - value.info?.let { with(InnertubeBrowseInfoSaver) { save(it) } }, - value.subscribersCountText, - value.thumbnail?.let { with(InnertubeThumbnailSaver) { save(it) } } - ) - - override fun restore(value: List) = Innertube.ArtistItem( - info = (value[0] as List?)?.let(InnertubeBrowseInfoSaver::restore), - subscribersCountText = value[1] as String?, - thumbnail = (value[2] as List?)?.let(InnertubeThumbnailSaver::restore) - ) -} - -val InnertubeArtistItemListSaver = listSaver(InnertubeArtistItemSaver) diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/savers/InnertubeArtistPageSaver.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/savers/InnertubeArtistPageSaver.kt deleted file mode 100644 index 972ba95..0000000 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/savers/InnertubeArtistPageSaver.kt +++ /dev/null @@ -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> { - 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) = Innertube.ArtistPage( - name = value[0] as String?, - description = value[1] as String?, - thumbnail = (value[2] as List?)?.let(InnertubeThumbnailSaver::restore), - shuffleEndpoint = (value[3] as List?)?.let(InnertubeWatchEndpointSaver::restore), - radioEndpoint = (value[4] as List?)?.let(InnertubeWatchEndpointSaver::restore), - songs = (value[5] as List>?)?.let(InnertubeSongItemListSaver::restore), - songsEndpoint = (value[6] as List?)?.let(InnertubeBrowseEndpointSaver::restore), - albums = (value[7] as List>?)?.let(InnertubeAlbumItemListSaver::restore), - albumsEndpoint = (value[8] as List?)?.let(InnertubeBrowseEndpointSaver::restore), - singles = (value[9] as List>?)?.let(InnertubeAlbumItemListSaver::restore), - singlesEndpoint = (value[10] as List?)?.let(InnertubeBrowseEndpointSaver::restore), - ) -} diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/savers/InnertubeBrowseEndpointSaver.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/savers/InnertubeBrowseEndpointSaver.kt deleted file mode 100644 index 3b7097b..0000000 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/savers/InnertubeBrowseEndpointSaver.kt +++ /dev/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.models.NavigationEndpoint - -object InnertubeBrowseEndpointSaver : Saver> { - override fun SaverScope.save(value: NavigationEndpoint.Endpoint.Browse) = listOf( - value.browseId, - value.params - ) - - override fun restore(value: List) = NavigationEndpoint.Endpoint.Browse( - browseId = value[0] as String, - params = value[1] as String?, - browseEndpointContextSupportedConfigs = null - ) -} diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/savers/InnertubeBrowseInfoSaver.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/savers/InnertubeBrowseInfoSaver.kt deleted file mode 100644 index 5a14451..0000000 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/savers/InnertubeBrowseInfoSaver.kt +++ /dev/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, List> { - override fun SaverScope.save(value: Innertube.Info) = listOf( - value.name, - value.endpoint?.let { with(InnertubeBrowseEndpointSaver) { save(it) } } - ) - - override fun restore(value: List) = Innertube.Info( - name = value[0] as String?, - endpoint = (value[1] as List?)?.let(InnertubeBrowseEndpointSaver::restore) - ) -} - -val InnertubeBrowseInfoListSaver = listSaver(InnertubeBrowseInfoSaver) diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/savers/InnertubeItemsPageSaver.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/savers/InnertubeItemsPageSaver.kt deleted file mode 100644 index 7ecd85b..0000000 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/savers/InnertubeItemsPageSaver.kt +++ /dev/null @@ -1,4 +0,0 @@ -package it.vfsfitvnm.vimusic.savers - -val InnertubeSongsPageSaver = innertubeItemsPageSaver(InnertubeSongItemListSaver) -val InnertubeAlbumsPageSaver = innertubeItemsPageSaver(InnertubeAlbumItemListSaver) diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/savers/InnertubePlaylistItemSaver.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/savers/InnertubePlaylistItemSaver.kt deleted file mode 100644 index 21c0eb6..0000000 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/savers/InnertubePlaylistItemSaver.kt +++ /dev/null @@ -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> { - override fun SaverScope.save(value: Innertube.PlaylistItem): List = 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) = Innertube.PlaylistItem( - info = (value[0] as List?)?.let(InnertubeBrowseInfoSaver::restore), - channel = (value[1] as List?)?.let(InnertubeBrowseInfoSaver::restore), - songCount = value[2] as Int?, - thumbnail = (value[3] as List?)?.let(InnertubeThumbnailSaver::restore) - ) -} - -val InnertubePlaylistItemListSaver = listSaver(InnertubePlaylistItemSaver) diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/savers/InnertubePlaylistOrAlbumPageSaver.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/savers/InnertubePlaylistOrAlbumPageSaver.kt deleted file mode 100644 index e4f350d..0000000 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/savers/InnertubePlaylistOrAlbumPageSaver.kt +++ /dev/null @@ -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> { - override fun SaverScope.save(value: Innertube.PlaylistOrAlbumPage): List = 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) = Innertube.PlaylistOrAlbumPage( - title = value[0] as String?, - authors = (value[1] as List>?)?.let(InnertubeBrowseInfoListSaver::restore), - year = value[2] as String?, - thumbnail = (value[3] as List?)?.let(InnertubeThumbnailSaver::restore), - url = value[4] as String?, - songsPage = (value[5] as List?)?.let(InnertubeSongsPageSaver::restore), - otherVersions = (value[6] as List>?)?.let(InnertubeAlbumItemListSaver::restore), - ) -} diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/savers/InnertubeRelatedPageSaver.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/savers/InnertubeRelatedPageSaver.kt deleted file mode 100644 index a913411..0000000 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/savers/InnertubeRelatedPageSaver.kt +++ /dev/null @@ -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> { - override fun SaverScope.save(value: Innertube.RelatedPage): List = 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) = Innertube.RelatedPage( - songs = (value[0] as List>?)?.let(InnertubeSongItemListSaver::restore), - playlists = (value[1] as List>?)?.let(InnertubePlaylistItemListSaver::restore), - albums = (value[2] as List>?)?.let(InnertubeAlbumItemListSaver::restore), - artists = (value[3] as List>?)?.let(InnertubeArtistItemListSaver::restore), - ) -} diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/savers/InnertubeSongItemSaver.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/savers/InnertubeSongItemSaver.kt deleted file mode 100644 index 6d87947..0000000 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/savers/InnertubeSongItemSaver.kt +++ /dev/null @@ -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> { - override fun SaverScope.save(value: Innertube.SongItem): List = 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) = Innertube.SongItem( - info = (value[0] as List?)?.let(InnertubeWatchInfoSaver::restore), - authors = (value[1] as List>?)?.let(InnertubeBrowseInfoListSaver::restore), - album = (value[2] as List?)?.let(InnertubeBrowseInfoSaver::restore), - durationText = value[3] as String?, - thumbnail = (value[4] as List?)?.let(InnertubeThumbnailSaver::restore) - ) -} - -val InnertubeSongItemListSaver = listSaver(InnertubeSongItemSaver) diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/savers/InnertubeThumbnailSaver.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/savers/InnertubeThumbnailSaver.kt deleted file mode 100644 index 1b1d589..0000000 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/savers/InnertubeThumbnailSaver.kt +++ /dev/null @@ -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> { - override fun SaverScope.save(value: Thumbnail) = listOf( - value.url, - value.width, - value.height - ) - - override fun restore(value: List) = Thumbnail( - url = value[0] as String, - width = value[1] as Int, - height = value[2] as Int?, - ) -} diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/savers/InnertubeVideoItemSaver.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/savers/InnertubeVideoItemSaver.kt deleted file mode 100644 index 49c3177..0000000 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/savers/InnertubeVideoItemSaver.kt +++ /dev/null @@ -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> { - override fun SaverScope.save(value: Innertube.VideoItem): List = 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) = Innertube.VideoItem( - info = (value[0] as List?)?.let(InnertubeWatchInfoSaver::restore), - authors = (value[1] as List>?)?.let(InnertubeBrowseInfoListSaver::restore), - viewsText = value[2] as String?, - durationText = value[3] as String?, - thumbnail = (value[4] as List?)?.let(InnertubeThumbnailSaver::restore) - ) -} - -val InnertubeVideoItemListSaver = listSaver(InnertubeVideoItemSaver) diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/savers/InnertubeWatchEndpointSaver.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/savers/InnertubeWatchEndpointSaver.kt deleted file mode 100644 index f8ad670..0000000 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/savers/InnertubeWatchEndpointSaver.kt +++ /dev/null @@ -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> { - override fun SaverScope.save(value: NavigationEndpoint.Endpoint.Watch) = listOf( - value.params, - value.playlistId, - value.videoId, - value.index, - value.playlistSetVideoId, - ) - - override fun restore(value: List) = 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 - ) -} diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/savers/InnertubeWatchInfoSaver.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/savers/InnertubeWatchInfoSaver.kt deleted file mode 100644 index d9eacef..0000000 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/savers/InnertubeWatchInfoSaver.kt +++ /dev/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, List> { - override fun SaverScope.save(value: Innertube.Info) = listOf( - value.name, - value.endpoint?.let { with(InnertubeWatchEndpointSaver) { save(it) } }, - ) - - override fun restore(value: List) = Innertube.Info( - name = value[0] as String?, - endpoint = (value[1] as List?)?.let(InnertubeWatchEndpointSaver::restore) - ) -} diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/savers/PlaylistPreviewSaver.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/savers/PlaylistPreviewSaver.kt deleted file mode 100644 index 56d3fe1..0000000 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/savers/PlaylistPreviewSaver.kt +++ /dev/null @@ -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> { - override fun SaverScope.save(value: PlaylistPreview) = listOf( - with(PlaylistSaver) { save(value.playlist) }, - value.songCount, - ) - - override fun restore(value: List) = PlaylistPreview( - playlist = PlaylistSaver.restore(value[0] as List), - songCount = value[1] as Int, - ) -} - -val PlaylistPreviewListSaver = listSaver(PlaylistPreviewSaver) diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/savers/PlaylistSaver.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/savers/PlaylistSaver.kt deleted file mode 100644 index c660f57..0000000 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/savers/PlaylistSaver.kt +++ /dev/null @@ -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> { - override fun SaverScope.save(value: Playlist): List = listOf( - value.id, - value.name, - value.browseId, - ) - - override fun restore(value: List): Playlist = Playlist( - id = value[0] as Long, - name = value[1] as String, - browseId = value[2] as String?, - ) -} diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/savers/PlaylistWithSongsSaver.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/savers/PlaylistWithSongsSaver.kt deleted file mode 100644 index fe73abd..0000000 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/savers/PlaylistWithSongsSaver.kt +++ /dev/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.vimusic.models.PlaylistWithSongs - -object PlaylistWithSongsSaver : Saver> { - 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): PlaylistWithSongs = PlaylistWithSongs( - playlist = PlaylistSaver.restore(value[0] as List), - songs = DetailedSongListSaver.restore(value[1] as List>) - ) -} diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/savers/Savers.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/savers/Savers.kt deleted file mode 100644 index 3dd2811..0000000 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/savers/Savers.kt +++ /dev/null @@ -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 : Saver, List> { - override fun SaverScope.save(value: List): List - override fun restore(value: List): List -} - -fun resultSaver(saver: Saver) = - object : Saver?, Pair> { - override fun restore(value: Pair) = - value.first?.let(saver::restore)?.let(Result.Companion::success) - ?: value.second?.let(Result.Companion::failure) - - override fun SaverScope.save(value: Result?) = - with(saver) { value?.getOrNull()?.let { save(it) } } to value?.exceptionOrNull() - } - -fun listSaver(saver: Saver) = - object : ListSaver { - override fun restore(value: List) = - value.mapNotNull(saver::restore) - - override fun SaverScope.save(value: List) = - with(saver) { value.mapNotNull { save(it) } } - } - -fun nullableSaver(saver: Saver) = - object : Saver { - override fun SaverScope.save(value: Original?): Saveable? = - value?.let { with(saver) { save(it) } } - - override fun restore(value: Saveable): Original? = - saver.restore(value) - } - -fun innertubeItemsPageSaver(saver: ListSaver>) = - object : Saver, List> { - override fun SaverScope.save(value: Innertube.ItemsPage) = listOf( - value.items?.let { with(saver) { save(it) } }, - value.continuation - ) - - @Suppress("UNCHECKED_CAST") - override fun restore(value: List) = Innertube.ItemsPage( - items = (value[0] as List>?)?.let(saver::restore), - continuation = value[1] as String? - ) - } diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/savers/SearchQuerySaver.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/savers/SearchQuerySaver.kt deleted file mode 100644 index 57df500..0000000 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/savers/SearchQuerySaver.kt +++ /dev/null @@ -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> { - override fun SaverScope.save(value: SearchQuery): List = listOf( - value.id, - value.query, - ) - - override fun restore(value: List) = SearchQuery( - id = value[0] as Long, - query = value[1] as String - ) -} diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/service/PlayerService.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/service/PlayerService.kt index 77ad0e2..0262108 100644 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/service/PlayerService.kt +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/service/PlayerService.kt @@ -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() diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/album/AlbumScreen.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/album/AlbumScreen.kt index bed087f..885c778 100644 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/album/AlbumScreen.kt +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/album/AlbumScreen.kt @@ -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/$browseId/album") + var albumPage by persist("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, diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/album/AlbumSongs.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/album/AlbumSongs.kt index 175de83..3d08043 100644 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/album/AlbumSongs.kt +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/album/AlbumSongs.kt @@ -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("album/$browseId/songs") + + LaunchedEffect(Unit) { + Database.albumSongs(browseId).collect { songs = it } } val thumbnailSizeDp = Dimensions.thumbnails.song diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/artist/ArtistLocalSongs.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/artist/ArtistLocalSongs.kt index e6a5985..9d43137 100644 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/artist/ArtistLocalSongs.kt +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/artist/ArtistLocalSongs.kt @@ -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?>("artist/$browseId/localSongs") + + LaunchedEffect(Unit) { + Database.artistSongs(browseId).collect { songs = it } } val songThumbnailSizeDp = Dimensions.thumbnails.song diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/artist/ArtistOverview.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/artist/ArtistOverview.kt index 899121e..74592af 100644 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/artist/ArtistOverview.kt +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/artist/ArtistOverview.kt @@ -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 diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/artist/ArtistScreen.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/artist/ArtistScreen.kt index b81bdd1..60067a3 100644 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/artist/ArtistScreen.kt +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/artist/ArtistScreen.kt @@ -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/$browseId/artist") + + var artistPage by persist("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 { diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/builtinplaylist/BuiltInPlaylistScreen.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/builtinplaylist/BuiltInPlaylistScreen.kt index 42424ad..4517944 100644 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/builtinplaylist/BuiltInPlaylistScreen.kt +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/builtinplaylist/BuiltInPlaylistScreen.kt @@ -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() diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/builtinplaylist/BuiltInPlaylistSongs.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/builtinplaylist/BuiltInPlaylistSongs.kt index 98543c8..29ba524 100644 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/builtinplaylist/BuiltInPlaylistSongs.kt +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/builtinplaylist/BuiltInPlaylistSongs.kt @@ -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,25 +53,24 @@ fun BuiltInPlaylistSongs(builtInPlaylist: BuiltInPlaylist) { val binder = LocalPlayerServiceBinder.current val menuState = LocalMenuState.current - val songs by produceSaveableState( - initialValue = emptyList(), - stateSaver = DetailedSongListSaver - ) { + var songs by persistList("${builtInPlaylist.name}/songs") + + LaunchedEffect(Unit) { when (builtInPlaylist) { BuiltInPlaylist.Favorites -> Database .favorites() - .flowOn(Dispatchers.IO) + BuiltInPlaylist.Offline -> Database .songsWithContentLength() .flowOn(Dispatchers.IO) .map { songs -> - songs.filter { song -> - song.contentLength?.let { - binder?.cache?.isCached(song.id, 0, song.contentLength) - } ?: false + songs.filter { song -> + song.contentLength?.let { + binder?.cache?.isCached(song.id, 0, song.contentLength) + } ?: false + } } - } - }.collect { value = it } + }.collect { songs = it } } val thumbnailSizeDp = Dimensions.thumbnails.song diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/home/HomeAlbums.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/home/HomeAlbums.kt index 7c7e17f..5321ec9 100644 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/home/HomeAlbums.kt +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/home/HomeAlbums.kt @@ -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>(tag = "home/albums", emptyList()) + + LaunchedEffect(sortBy, sortOrder) { + Database.albums(sortBy, sortOrder).collect { items = it } } val thumbnailSizeDp = Dimensions.thumbnails.song * 2 diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/home/HomeArtists.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/home/HomeArtists.kt index a721eff..4eeba41 100644 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/home/HomeArtists.kt +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/home/HomeArtists.kt @@ -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("home/artists") + + LaunchedEffect(sortBy, sortOrder) { + Database.artists(sortBy, sortOrder).collect { items = it } } val thumbnailSizeDp = Dimensions.thumbnails.song * 2 diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/home/HomePlaylists.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/home/HomePlaylists.kt index 387b724..91ac7df 100644 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/home/HomePlaylists.kt +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/home/HomePlaylists.kt @@ -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("home/playlists") + + LaunchedEffect(sortBy, sortOrder) { + Database.playlistPreviews(sortBy, sortOrder).collect { items = it } } val sortOrderIconRotation by animateFloatAsState( diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/home/HomeScreen.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/home/HomeScreen.kt index 8d59233..e371bb0 100644 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/home/HomeScreen.kt +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/home/HomeScreen.kt @@ -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 = { diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/home/HomeSongs.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/home/HomeSongs.kt index a5729af..2c30448 100644 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/home/HomeSongs.kt +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/home/HomeSongs.kt @@ -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("home/songs") + + LaunchedEffect(sortBy, sortOrder) { + Database.songs(sortBy, sortOrder).collect { items = it } } val sortOrderIconRotation by animateFloatAsState( diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/home/QuickPicks.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/home/QuickPicks.kt index 24cd818..21abf45 100644 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/home/QuickPicks.kt +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/home/QuickPicks.kt @@ -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,25 +89,18 @@ fun QuickPicks( val menuState = LocalMenuState.current val windowInsets = LocalPlayerAwareWindowInsets.current - var trending by rememberSaveable(stateSaver = nullableSaver(DetailedSongSaver)) { - mutableStateOf(null) - } + var trending by persist("home/trending") - var relatedPageResult by rememberSaveable(stateSaver = resultSaver(nullableSaver(InnertubeRelatedPageSaver))) { - mutableStateOf(null) - } + var relatedPageResult by persist?>(tag = "home/relatedPageResult") LaunchedEffect(Unit) { - Database.trending() - .flowOn(Dispatchers.IO) - .distinctUntilChanged() - .collect { song -> - if ((song == null && relatedPageResult == null) || trending?.id != song?.id) { - relatedPageResult = - Innertube.relatedPage(NextBody(videoId = (song?.id ?: "J7p4bzqLvCw"))) - } - trending = song + Database.trending().distinctUntilChanged().collect { song -> + if ((song == null && relatedPageResult == null) || trending?.id != song?.id) { + relatedPageResult = + Innertube.relatedPage(NextBody(videoId = (song?.id ?: "J7p4bzqLvCw"))) } + trending = song + } } val songThumbnailSizeDp = Dimensions.thumbnails.song diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/localplaylist/LocalPlaylistScreen.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/localplaylist/LocalPlaylistScreen.kt index 7594526..9f5f92f 100644 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/localplaylist/LocalPlaylistScreen.kt +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/localplaylist/LocalPlaylistScreen.kt @@ -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,11 +31,13 @@ fun LocalPlaylistScreen(playlistId: Long) { Item(0, "Songs", R.drawable.musical_notes) } ) { currentTabIndex -> - saveableStateHolder.SaveableStateProvider(key = currentTabIndex) { - LocalPlaylistSongs( - playlistId = playlistId, - onDelete = pop - ) + saveableStateHolder.SaveableStateProvider(currentTabIndex) { + when (currentTabIndex) { + 0 -> LocalPlaylistSongs( + playlistId = playlistId, + onDelete = pop + ) + } } } } diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/localplaylist/LocalPlaylistSongs.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/localplaylist/LocalPlaylistSongs.kt index 4fef7b1..47ed32d 100644 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/localplaylist/LocalPlaylistSongs.kt +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/localplaylist/LocalPlaylistSongs.kt @@ -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("localPlaylist/$playlistId/playlistWithSongs") + + LaunchedEffect(Unit) { + Database.playlistWithSongs(playlistId).filterNotNull().collect { playlistWithSongs = it } } val lazyListState = rememberLazyListState() diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/player/Controls.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/player/Controls.kt index c89167f..f5ccd35 100644 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/player/Controls.kt +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/player/Controls.kt @@ -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") diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/player/Lyrics.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/player/Lyrics.kt index eb6eb99..5eafe54 100644 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/player/Lyrics.kt +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/player/Lyrics.kt @@ -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(), - mediaId, isShowingSynchronizedLyrics - ) { + var lyrics by rememberSaveable { + mutableStateOf(".") + } + + 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) { diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/playlist/PlaylistScreen.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/playlist/PlaylistScreen.kt index 407d362..a936005 100644 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/playlist/PlaylistScreen.kt +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/playlist/PlaylistScreen.kt @@ -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() diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/playlist/PlaylistSongList.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/playlist/PlaylistSongList.kt index 66959cf..08b8013 100644 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/playlist/PlaylistSongList.kt +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/playlist/PlaylistSongList.kt @@ -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("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() } } diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/search/LocalSongSearch.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/search/LocalSongSearch.kt index 01e3746..569b061 100644 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/search/LocalSongSearch.kt +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/search/LocalSongSearch.kt @@ -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("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 } } } diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/search/OnlineSearch.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/search/OnlineSearch.kt index d9b9f0e..fe7482f 100644 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/search/OnlineSearch.kt +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/search/OnlineSearch.kt @@ -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("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?>())) { - mutableStateOf(null) - } + var suggestionsResult by persist?>?>("search/online/suggestionsResult") LaunchedEffect(textFieldValue.text) { if (textFieldValue.text.isNotEmpty()) { diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/search/SearchScreen.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/search/SearchScreen.kt index 3f5e01d..eb6c3fc 100644 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/search/SearchScreen.kt +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/search/SearchScreen.kt @@ -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() diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/searchresult/ItemsPage.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/searchresult/ItemsPage.kt index 5a8343f..f2c26db 100644 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/searchresult/ItemsPage.kt +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/searchresult/ItemsPage.kt @@ -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 ItemsPage( - stateSaver: Saver, List>, + 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 ItemsPage( noinline itemsPageProvider: (suspend (String?) -> Result?>?)? = 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?>(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 } } } diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/searchresult/SearchResultScreen.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/searchresult/SearchResultScreen.kt index 6dc2d13..f980da7 100644 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/searchresult/SearchResultScreen.kt +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/searchresult/SearchResultScreen.kt @@ -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) { diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/settings/AppearanceSettings.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/settings/AppearanceSettings.kt index 827c2f7..c684cf9 100644 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/settings/AppearanceSettings.kt +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/settings/AppearanceSettings.kt @@ -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 diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/settings/DatabaseSettings.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/settings/DatabaseSettings.kt index cad0788..e0634a4 100644 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/settings/DatabaseSettings.kt +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/settings/DatabaseSettings.kt @@ -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 -> diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/settings/OtherSettings.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/settings/OtherSettings.kt index 94cb922..769fd87 100644 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/settings/OtherSettings.kt +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/settings/OtherSettings.kt @@ -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 diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/settings/SettingsScreen.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/settings/SettingsScreen.kt index fa15bbf..6186d7f 100644 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/settings/SettingsScreen.kt +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/settings/SettingsScreen.kt @@ -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 diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/utils/ProduceSaveableState.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/utils/ProduceSaveableState.kt deleted file mode 100644 index 0e566d1..0000000 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/utils/ProduceSaveableState.kt +++ /dev/null @@ -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 produceSaveableState( - initialValue: T, - stateSaver: Saver, - @BuilderInference producer: suspend ProduceStateScope.() -> Unit -): State { - val result = rememberSaveable(stateSaver = stateSaver) { - mutableStateOf(initialValue) - } - - LaunchedEffect(Unit) { - ProduceSaveableStateScope(result, coroutineContext).producer() - } - - return result -} - -@Composable -fun produceSaveableState( - initialValue: T, - stateSaver: Saver, - key1: Any?, - @BuilderInference producer: suspend ProduceStateScope.() -> Unit -): State { - val state = rememberSaveable(stateSaver = stateSaver) { - mutableStateOf(initialValue) - } - - LaunchedEffect(key1) { - ProduceSaveableStateScope(state, coroutineContext).producer() - } - - return state -} - -@Composable -fun produceSaveableState( - initialValue: T, - stateSaver: Saver, - key1: Any?, - key2: Any?, - @BuilderInference producer: suspend ProduceStateScope.() -> Unit -): State { - val result = rememberSaveable(stateSaver = stateSaver) { - mutableStateOf(initialValue) - } - - LaunchedEffect(key1, key2) { - ProduceSaveableStateScope(result, coroutineContext).producer() - } - - return result -} - -private class ProduceSaveableStateScope( - state: MutableState, - override val coroutineContext: CoroutineContext -) : ProduceStateScope, MutableState by state { - override suspend fun awaitDispose(onDispose: () -> Unit): Nothing { - try { - suspendCancellableCoroutine { } - } finally { - onDispose() - } - } -} diff --git a/compose-persist/.gitignore b/compose-persist/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/compose-persist/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/compose-persist/build.gradle.kts b/compose-persist/build.gradle.kts new file mode 100644 index 0000000..ac8ccf6 --- /dev/null +++ b/compose-persist/build.gradle.kts @@ -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) +} diff --git a/compose-persist/src/main/AndroidManifest.xml b/compose-persist/src/main/AndroidManifest.xml new file mode 100644 index 0000000..e100076 --- /dev/null +++ b/compose-persist/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + diff --git a/compose-persist/src/main/kotlin/it/vfsfitvnm/compose/persist/Persist.kt b/compose-persist/src/main/kotlin/it/vfsfitvnm/compose/persist/Persist.kt new file mode 100644 index 0000000..65eb114 --- /dev/null +++ b/compose-persist/src/main/kotlin/it/vfsfitvnm/compose/persist/Persist.kt @@ -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 persist(tag: String, initialValue: T): MutableState { + val context = LocalContext.current + + return remember { + context.persistMap?.getOrPut(tag) { mutableStateOf(initialValue) } as? MutableState + ?: mutableStateOf(initialValue) + } +} + +@Composable +fun persistList(tag: String): MutableState> = + persist(tag = tag, initialValue = emptyList()) + +@Composable +fun persist(tag: String): MutableState = + persist(tag = tag, initialValue = null) diff --git a/compose-persist/src/main/kotlin/it/vfsfitvnm/compose/persist/PersistMap.kt b/compose-persist/src/main/kotlin/it/vfsfitvnm/compose/persist/PersistMap.kt new file mode 100644 index 0000000..2cb0b5c --- /dev/null +++ b/compose-persist/src/main/kotlin/it/vfsfitvnm/compose/persist/PersistMap.kt @@ -0,0 +1,3 @@ +package it.vfsfitvnm.compose.persist + +typealias PersistMap = HashMap diff --git a/compose-persist/src/main/kotlin/it/vfsfitvnm/compose/persist/PersistMapCleanup.kt b/compose-persist/src/main/kotlin/it/vfsfitvnm/compose/persist/PersistMapCleanup.kt new file mode 100644 index 0000000..c236622 --- /dev/null +++ b/compose-persist/src/main/kotlin/it/vfsfitvnm/compose/persist/PersistMapCleanup.kt @@ -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()?.isChangingConfigurations == false) { + context.persistMap?.keys?.removeAll { it.startsWith(tagPrefix) } + } + } + } +} diff --git a/compose-persist/src/main/kotlin/it/vfsfitvnm/compose/persist/PersistMapOwner.kt b/compose-persist/src/main/kotlin/it/vfsfitvnm/compose/persist/PersistMapOwner.kt new file mode 100644 index 0000000..a6bec64 --- /dev/null +++ b/compose-persist/src/main/kotlin/it/vfsfitvnm/compose/persist/PersistMapOwner.kt @@ -0,0 +1,5 @@ +package it.vfsfitvnm.compose.persist + +interface PersistMapOwner { + val persistMap: PersistMap +} diff --git a/compose-persist/src/main/kotlin/it/vfsfitvnm/compose/persist/Utils.kt b/compose-persist/src/main/kotlin/it/vfsfitvnm/compose/persist/Utils.kt new file mode 100644 index 0000000..b91c4eb --- /dev/null +++ b/compose-persist/src/main/kotlin/it/vfsfitvnm/compose/persist/Utils.kt @@ -0,0 +1,16 @@ +package it.vfsfitvnm.compose.persist + +import android.content.Context +import android.content.ContextWrapper + +val Context.persistMap: PersistMap? + get() = findOwner()?.persistMap + +internal inline fun Context.findOwner(): T? { + var context = this + while (context is ContextWrapper) { + if (context is T) return context + context = context.baseContext + } + return null +} diff --git a/settings.gradle.kts b/settings.gradle.kts index b56a6b8..f11b1e6 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -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")