Add song cache settings
This commit is contained in:
@@ -42,6 +42,7 @@ import it.vfsfitvnm.vimusic.ui.components.rememberBottomSheetState
|
|||||||
import it.vfsfitvnm.vimusic.ui.components.rememberMenuState
|
import it.vfsfitvnm.vimusic.ui.components.rememberMenuState
|
||||||
import it.vfsfitvnm.vimusic.ui.screens.HomeScreen
|
import it.vfsfitvnm.vimusic.ui.screens.HomeScreen
|
||||||
import it.vfsfitvnm.vimusic.ui.screens.IntentUriScreen
|
import it.vfsfitvnm.vimusic.ui.screens.IntentUriScreen
|
||||||
|
import it.vfsfitvnm.vimusic.ui.screens.settings.OtherScreen
|
||||||
import it.vfsfitvnm.vimusic.ui.styling.*
|
import it.vfsfitvnm.vimusic.ui.styling.*
|
||||||
import it.vfsfitvnm.vimusic.ui.views.PlayerView
|
import it.vfsfitvnm.vimusic.ui.views.PlayerView
|
||||||
import it.vfsfitvnm.vimusic.utils.*
|
import it.vfsfitvnm.vimusic.utils.*
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package it.vfsfitvnm.vimusic
|
package it.vfsfitvnm.vimusic
|
||||||
|
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
import android.content.Context
|
|
||||||
import coil.ImageLoader
|
import coil.ImageLoader
|
||||||
import coil.ImageLoaderFactory
|
import coil.ImageLoaderFactory
|
||||||
import coil.disk.DiskCache
|
import coil.disk.DiskCache
|
||||||
@@ -15,18 +14,14 @@ class MainApplication : Application(), ImageLoaderFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun newImageLoader(): ImageLoader {
|
override fun newImageLoader(): ImageLoader {
|
||||||
return defaultCoilImageLoader(preferences.coilDiskCacheMaxSizeBytes)
|
return ImageLoader.Builder(this)
|
||||||
|
.crossfade(true)
|
||||||
|
.diskCache(
|
||||||
|
DiskCache.Builder()
|
||||||
|
.directory(filesDir.resolve("coil"))
|
||||||
|
.maxSizeBytes(preferences.coilDiskCacheMaxSizeBytes)
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
.build()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Context.defaultCoilImageLoader(diskCacheMaxSize: Long): ImageLoader {
|
|
||||||
return ImageLoader.Builder(this)
|
|
||||||
.crossfade(true)
|
|
||||||
.diskCache(
|
|
||||||
DiskCache.Builder()
|
|
||||||
.directory(filesDir.resolve("coil"))
|
|
||||||
.maxSizeBytes(diskCacheMaxSize)
|
|
||||||
.build()
|
|
||||||
)
|
|
||||||
.build()
|
|
||||||
}
|
|
||||||
@@ -18,6 +18,7 @@ import androidx.core.app.NotificationCompat
|
|||||||
import androidx.core.graphics.drawable.IconCompat
|
import androidx.core.graphics.drawable.IconCompat
|
||||||
import androidx.core.graphics.drawable.toBitmap
|
import androidx.core.graphics.drawable.toBitmap
|
||||||
import androidx.core.net.toUri
|
import androidx.core.net.toUri
|
||||||
|
import androidx.core.os.bundleOf
|
||||||
import androidx.media3.common.*
|
import androidx.media3.common.*
|
||||||
import androidx.media3.common.util.Util
|
import androidx.media3.common.util.Util
|
||||||
import androidx.media3.database.StandaloneDatabaseProvider
|
import androidx.media3.database.StandaloneDatabaseProvider
|
||||||
@@ -25,7 +26,7 @@ import androidx.media3.datasource.DataSource
|
|||||||
import androidx.media3.datasource.DefaultHttpDataSource
|
import androidx.media3.datasource.DefaultHttpDataSource
|
||||||
import androidx.media3.datasource.ResolvingDataSource
|
import androidx.media3.datasource.ResolvingDataSource
|
||||||
import androidx.media3.datasource.cache.CacheDataSource
|
import androidx.media3.datasource.cache.CacheDataSource
|
||||||
import androidx.media3.datasource.cache.NoOpCacheEvictor
|
import androidx.media3.datasource.cache.LeastRecentlyUsedCacheEvictor
|
||||||
import androidx.media3.datasource.cache.SimpleCache
|
import androidx.media3.datasource.cache.SimpleCache
|
||||||
import androidx.media3.exoplayer.ExoPlayer
|
import androidx.media3.exoplayer.ExoPlayer
|
||||||
import androidx.media3.exoplayer.analytics.AnalyticsListener
|
import androidx.media3.exoplayer.analytics.AnalyticsListener
|
||||||
@@ -36,11 +37,10 @@ import androidx.media3.session.*
|
|||||||
import androidx.media3.session.MediaNotification.ActionFactory
|
import androidx.media3.session.MediaNotification.ActionFactory
|
||||||
import coil.ImageLoader
|
import coil.ImageLoader
|
||||||
import coil.request.ImageRequest
|
import coil.request.ImageRequest
|
||||||
|
import com.google.common.util.concurrent.Futures
|
||||||
import com.google.common.util.concurrent.ListenableFuture
|
import com.google.common.util.concurrent.ListenableFuture
|
||||||
import it.vfsfitvnm.vimusic.Database
|
import it.vfsfitvnm.vimusic.*
|
||||||
import it.vfsfitvnm.vimusic.MainActivity
|
|
||||||
import it.vfsfitvnm.vimusic.R
|
import it.vfsfitvnm.vimusic.R
|
||||||
import it.vfsfitvnm.vimusic.internal
|
|
||||||
import it.vfsfitvnm.vimusic.models.QueuedMediaItem
|
import it.vfsfitvnm.vimusic.models.QueuedMediaItem
|
||||||
import it.vfsfitvnm.vimusic.utils.*
|
import it.vfsfitvnm.vimusic.utils.*
|
||||||
import it.vfsfitvnm.youtubemusic.Outcome
|
import it.vfsfitvnm.youtubemusic.Outcome
|
||||||
@@ -52,6 +52,8 @@ val StartRadioCommand = SessionCommand("StartRadioCommand", Bundle.EMPTY)
|
|||||||
val StartArtistRadioCommand = SessionCommand("StartArtistRadioCommand", Bundle.EMPTY)
|
val StartArtistRadioCommand = SessionCommand("StartArtistRadioCommand", Bundle.EMPTY)
|
||||||
val StopRadioCommand = SessionCommand("StopRadioCommand", Bundle.EMPTY)
|
val StopRadioCommand = SessionCommand("StopRadioCommand", Bundle.EMPTY)
|
||||||
|
|
||||||
|
val GetCacheSizeCommand = SessionCommand("GetCacheSizeCommand", Bundle.EMPTY)
|
||||||
|
|
||||||
@ExperimentalAnimationApi
|
@ExperimentalAnimationApi
|
||||||
@ExperimentalFoundationApi
|
@ExperimentalFoundationApi
|
||||||
class PlayerService : MediaSessionService(), MediaSession.MediaItemFiller,
|
class PlayerService : MediaSessionService(), MediaSession.MediaItemFiller,
|
||||||
@@ -83,7 +85,8 @@ class PlayerService : MediaSessionService(), MediaSession.MediaItemFiller,
|
|||||||
createNotificationChannel()
|
createNotificationChannel()
|
||||||
setMediaNotificationProvider(this)
|
setMediaNotificationProvider(this)
|
||||||
|
|
||||||
cache = SimpleCache(cacheDir, NoOpCacheEvictor(), StandaloneDatabaseProvider(this))
|
val cacheEvictor = LeastRecentlyUsedCacheEvictor(preferences.exoPlayerDiskCacheMaxSizeBytes)
|
||||||
|
cache = SimpleCache(cacheDir, cacheEvictor, StandaloneDatabaseProvider(this))
|
||||||
|
|
||||||
val player = ExoPlayer.Builder(this)
|
val player = ExoPlayer.Builder(this)
|
||||||
.setHandleAudioBecomingNoisy(true)
|
.setHandleAudioBecomingNoisy(true)
|
||||||
@@ -176,6 +179,7 @@ class PlayerService : MediaSessionService(), MediaSession.MediaItemFiller,
|
|||||||
.add(StartRadioCommand)
|
.add(StartRadioCommand)
|
||||||
.add(StartArtistRadioCommand)
|
.add(StartArtistRadioCommand)
|
||||||
.add(StopRadioCommand)
|
.add(StopRadioCommand)
|
||||||
|
.add(GetCacheSizeCommand)
|
||||||
.build()
|
.build()
|
||||||
val playerCommands = Player.Commands.Builder().addAllCommands().build()
|
val playerCommands = Player.Commands.Builder().addAllCommands().build()
|
||||||
return MediaSession.ConnectionResult.accept(sessionCommands, playerCommands)
|
return MediaSession.ConnectionResult.accept(sessionCommands, playerCommands)
|
||||||
@@ -206,6 +210,9 @@ class PlayerService : MediaSessionService(), MediaSession.MediaItemFiller,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
StopRadioCommand -> radio = null
|
StopRadioCommand -> radio = null
|
||||||
|
GetCacheSizeCommand -> {
|
||||||
|
return Futures.immediateFuture(SessionResult(SessionResult.RESULT_SUCCESS, bundleOf("cacheSize" to cache.cacheSpace)))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return super.onCustomCommand(session, controller, customCommand, args)
|
return super.onCustomCommand(session, controller, customCommand, args)
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package it.vfsfitvnm.vimusic.ui.screens.settings
|
package it.vfsfitvnm.vimusic.ui.screens.settings
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
import android.text.format.Formatter
|
import android.text.format.Formatter
|
||||||
import androidx.compose.animation.ExperimentalAnimationApi
|
import androidx.compose.animation.ExperimentalAnimationApi
|
||||||
import androidx.compose.foundation.*
|
import androidx.compose.foundation.*
|
||||||
@@ -16,15 +17,18 @@ import coil.Coil
|
|||||||
import coil.annotation.ExperimentalCoilApi
|
import coil.annotation.ExperimentalCoilApi
|
||||||
import it.vfsfitvnm.route.RouteHandler
|
import it.vfsfitvnm.route.RouteHandler
|
||||||
import it.vfsfitvnm.vimusic.R
|
import it.vfsfitvnm.vimusic.R
|
||||||
|
import it.vfsfitvnm.vimusic.services.GetCacheSizeCommand
|
||||||
import it.vfsfitvnm.vimusic.ui.components.SeekBar
|
import it.vfsfitvnm.vimusic.ui.components.SeekBar
|
||||||
import it.vfsfitvnm.vimusic.ui.components.TopAppBar
|
import it.vfsfitvnm.vimusic.ui.components.TopAppBar
|
||||||
import it.vfsfitvnm.vimusic.ui.screens.*
|
import it.vfsfitvnm.vimusic.ui.screens.*
|
||||||
import it.vfsfitvnm.vimusic.ui.styling.LocalColorPalette
|
import it.vfsfitvnm.vimusic.ui.styling.LocalColorPalette
|
||||||
import it.vfsfitvnm.vimusic.ui.styling.LocalTypography
|
import it.vfsfitvnm.vimusic.ui.styling.LocalTypography
|
||||||
import it.vfsfitvnm.vimusic.utils.LocalPreferences
|
import it.vfsfitvnm.vimusic.utils.LocalPreferences
|
||||||
|
import it.vfsfitvnm.vimusic.utils.LocalYoutubePlayer
|
||||||
import it.vfsfitvnm.vimusic.utils.secondary
|
import it.vfsfitvnm.vimusic.utils.secondary
|
||||||
import it.vfsfitvnm.vimusic.utils.semiBold
|
import it.vfsfitvnm.vimusic.utils.semiBold
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.guava.await
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
@OptIn(ExperimentalCoilApi::class)
|
@OptIn(ExperimentalCoilApi::class)
|
||||||
@@ -54,10 +58,9 @@ fun OtherScreen() {
|
|||||||
val colorPalette = LocalColorPalette.current
|
val colorPalette = LocalColorPalette.current
|
||||||
val typography = LocalTypography.current
|
val typography = LocalTypography.current
|
||||||
val preferences = LocalPreferences.current
|
val preferences = LocalPreferences.current
|
||||||
|
val mediaController = LocalYoutubePlayer.current?.mediaController
|
||||||
|
|
||||||
var coilDiskCache by remember {
|
val coilDiskCache = Coil.imageLoader(context).diskCache
|
||||||
mutableStateOf(Coil.imageLoader(context).diskCache)
|
|
||||||
}
|
|
||||||
|
|
||||||
val coroutineScope = rememberCoroutineScope()
|
val coroutineScope = rememberCoroutineScope()
|
||||||
|
|
||||||
@@ -94,7 +97,6 @@ fun OtherScreen() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
coilDiskCache?.let { diskCache ->
|
coilDiskCache?.let { diskCache ->
|
||||||
var diskCacheSize by remember(diskCache) {
|
var diskCacheSize by remember(diskCache) {
|
||||||
mutableStateOf(diskCache.size)
|
mutableStateOf(diskCache.size)
|
||||||
@@ -133,7 +135,6 @@ fun OtherScreen() {
|
|||||||
},
|
},
|
||||||
onDrag = { delta ->
|
onDrag = { delta ->
|
||||||
scrubbingDiskCacheMaxSize = scrubbingDiskCacheMaxSize?.plus(delta)?.coerceIn(250L * 1024 * 1024, 2048L * 1024 * 1024)
|
scrubbingDiskCacheMaxSize = scrubbingDiskCacheMaxSize?.plus(delta)?.coerceIn(250L * 1024 * 1024, 2048L * 1024 * 1024)
|
||||||
println("new = $scrubbingDiskCacheMaxSize")
|
|
||||||
},
|
},
|
||||||
onDragEnd = {
|
onDragEnd = {
|
||||||
preferences.coilDiskCacheMaxSizeBytes = scrubbingDiskCacheMaxSize ?: preferences.coilDiskCacheMaxSizeBytes
|
preferences.coilDiskCacheMaxSizeBytes = scrubbingDiskCacheMaxSize ?: preferences.coilDiskCacheMaxSizeBytes
|
||||||
@@ -164,6 +165,64 @@ fun OtherScreen() {
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mediaController?.let { mediaController ->
|
||||||
|
val diskCacheSize by produceState(initialValue = 0L) {
|
||||||
|
value = mediaController.sendCustomCommand(GetCacheSizeCommand, Bundle.EMPTY).await().extras.getLong("cacheSize")
|
||||||
|
}
|
||||||
|
|
||||||
|
var scrubbingDiskCacheMaxSize by remember {
|
||||||
|
mutableStateOf<Long?>(null)
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingsEntryGroupText(
|
||||||
|
title = "SONG CACHE",
|
||||||
|
)
|
||||||
|
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(start = 24.dp)
|
||||||
|
.padding(horizontal = 32.dp, vertical = 16.dp)
|
||||||
|
.fillMaxWidth()
|
||||||
|
) {
|
||||||
|
BasicText(
|
||||||
|
text = "Max size",
|
||||||
|
style = typography.xs.semiBold,
|
||||||
|
)
|
||||||
|
|
||||||
|
BasicText(
|
||||||
|
text = Formatter.formatShortFileSize(context, scrubbingDiskCacheMaxSize ?: preferences.exoPlayerDiskCacheMaxSizeBytes),
|
||||||
|
style = typography.xs.semiBold.secondary
|
||||||
|
)
|
||||||
|
|
||||||
|
SeekBar(
|
||||||
|
value = (scrubbingDiskCacheMaxSize ?: preferences.exoPlayerDiskCacheMaxSizeBytes).coerceIn(250L * 1024 * 1024, 4096L * 1024 * 1024),
|
||||||
|
minimumValue = 250L * 1024 * 1024,
|
||||||
|
maximumValue = 4096L * 1024 * 1024,
|
||||||
|
onDragStart = {
|
||||||
|
scrubbingDiskCacheMaxSize = it
|
||||||
|
},
|
||||||
|
onDrag = { delta ->
|
||||||
|
scrubbingDiskCacheMaxSize = scrubbingDiskCacheMaxSize?.plus(delta)?.coerceIn(250L * 1024 * 1024, 4096L * 1024 * 1024)
|
||||||
|
},
|
||||||
|
onDragEnd = {
|
||||||
|
preferences.exoPlayerDiskCacheMaxSizeBytes = scrubbingDiskCacheMaxSize ?: preferences.exoPlayerDiskCacheMaxSizeBytes
|
||||||
|
scrubbingDiskCacheMaxSize = null
|
||||||
|
},
|
||||||
|
color = colorPalette.text,
|
||||||
|
backgroundColor = colorPalette.textDisabled,
|
||||||
|
shape = RoundedCornerShape(8.dp),
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(top = 8.dp)
|
||||||
|
.fillMaxWidth()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
DisabledSettingsEntry(
|
||||||
|
title = "Space used",
|
||||||
|
text = "${Formatter.formatShortFileSize(context, diskCacheSize)} (${diskCacheSize * 100 / preferences.exoPlayerDiskCacheMaxSizeBytes.coerceAtLeast(1)}%)",
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ class Preferences(holder: SharedPreferences) : SharedPreferences by holder {
|
|||||||
var homePageSongCollection by preference("homePageSongCollection", SongCollection.MostPlayed)
|
var homePageSongCollection by preference("homePageSongCollection", SongCollection.MostPlayed)
|
||||||
var thumbnailRoundness by preference("thumbnailRoundness", ThumbnailRoundness.Light)
|
var thumbnailRoundness by preference("thumbnailRoundness", ThumbnailRoundness.Light)
|
||||||
var coilDiskCacheMaxSizeBytes by preference("coilDiskCacheMaxSizeBytes", 512L * 1024 * 1024)
|
var coilDiskCacheMaxSizeBytes by preference("coilDiskCacheMaxSizeBytes", 512L * 1024 * 1024)
|
||||||
|
var exoPlayerDiskCacheMaxSizeBytes by preference("exoPlayerDiskCacheMaxSizeBytes", 512L * 1024 * 1024)
|
||||||
var displayLikeButtonInNotification by preference("displayLikeButtonInNotification", false)
|
var displayLikeButtonInNotification by preference("displayLikeButtonInNotification", false)
|
||||||
var persistentQueue by preference("persistentQueue", false)
|
var persistentQueue by preference("persistentQueue", false)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user