New Preferences class
This commit is contained in:
parent
9139609cf3
commit
b738910d63
|
@ -4,7 +4,7 @@ import android.app.Application
|
||||||
import coil.ImageLoader
|
import coil.ImageLoader
|
||||||
import coil.ImageLoaderFactory
|
import coil.ImageLoaderFactory
|
||||||
import coil.disk.DiskCache
|
import coil.disk.DiskCache
|
||||||
import it.vfsfitvnm.vimusic.utils.preferences
|
import it.vfsfitvnm.vimusic.utils.Preferences
|
||||||
|
|
||||||
|
|
||||||
class MainApplication : Application(), ImageLoaderFactory {
|
class MainApplication : Application(), ImageLoaderFactory {
|
||||||
|
@ -19,7 +19,7 @@ class MainApplication : Application(), ImageLoaderFactory {
|
||||||
.diskCache(
|
.diskCache(
|
||||||
DiskCache.Builder()
|
DiskCache.Builder()
|
||||||
.directory(filesDir.resolve("coil"))
|
.directory(filesDir.resolve("coil"))
|
||||||
.maxSizeBytes(preferences.coilDiskCacheMaxSizeBytes)
|
.maxSizeBytes(Preferences().coilDiskCacheMaxSizeBytes)
|
||||||
.build()
|
.build()
|
||||||
)
|
)
|
||||||
.build()
|
.build()
|
||||||
|
|
|
@ -127,6 +127,8 @@ class PlayerService : Service(), Player.Listener, PlaybackStatsListener.Callback
|
||||||
|
|
||||||
createNotificationChannel()
|
createNotificationChannel()
|
||||||
|
|
||||||
|
val preferences = Preferences()
|
||||||
|
|
||||||
val cacheEvictor = LeastRecentlyUsedCacheEvictor(preferences.exoPlayerDiskCacheMaxSizeBytes)
|
val cacheEvictor = LeastRecentlyUsedCacheEvictor(preferences.exoPlayerDiskCacheMaxSizeBytes)
|
||||||
cache = SimpleCache(cacheDir, cacheEvictor, StandaloneDatabaseProvider(this))
|
cache = SimpleCache(cacheDir, cacheEvictor, StandaloneDatabaseProvider(this))
|
||||||
|
|
||||||
|
@ -201,7 +203,7 @@ class PlayerService : Service(), Player.Listener, PlaybackStatsListener.Callback
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
if (preferences.persistentQueue) {
|
if (Preferences().persistentQueue) {
|
||||||
val mediaItems = player.currentTimeline.mediaItems
|
val mediaItems = player.currentTimeline.mediaItems
|
||||||
val mediaItemIndex = player.currentMediaItemIndex
|
val mediaItemIndex = player.currentMediaItemIndex
|
||||||
val mediaItemPosition = player.currentPosition
|
val mediaItemPosition = player.currentPosition
|
||||||
|
@ -262,7 +264,7 @@ class PlayerService : Service(), Player.Listener, PlaybackStatsListener.Callback
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun normalizeVolume() {
|
private fun normalizeVolume() {
|
||||||
if (preferences.volumeNormalization) {
|
if (Preferences().volumeNormalization) {
|
||||||
player.volume = player.currentMediaItem?.let { mediaItem ->
|
player.volume = player.currentMediaItem?.let { mediaItem ->
|
||||||
songPendingLoudnessDb.getOrElse(mediaItem.mediaId) {
|
songPendingLoudnessDb.getOrElse(mediaItem.mediaId) {
|
||||||
mediaItem.mediaMetadata.extras?.getFloatOrNull("loudnessDb")
|
mediaItem.mediaMetadata.extras?.getFloatOrNull("loudnessDb")
|
||||||
|
|
|
@ -4,10 +4,6 @@ import android.content.Context
|
||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.*
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.unit.Dp
|
|
||||||
import androidx.compose.ui.unit.TextUnit
|
|
||||||
import androidx.compose.ui.unit.dp
|
|
||||||
import androidx.compose.ui.unit.sp
|
|
||||||
import androidx.core.content.edit
|
import androidx.core.content.edit
|
||||||
import androidx.media3.common.Player
|
import androidx.media3.common.Player
|
||||||
import it.vfsfitvnm.vimusic.enums.ColorPaletteMode
|
import it.vfsfitvnm.vimusic.enums.ColorPaletteMode
|
||||||
|
@ -15,105 +11,128 @@ import it.vfsfitvnm.vimusic.enums.SongCollection
|
||||||
import it.vfsfitvnm.vimusic.enums.ThumbnailRoundness
|
import it.vfsfitvnm.vimusic.enums.ThumbnailRoundness
|
||||||
import it.vfsfitvnm.youtubemusic.YouTube
|
import it.vfsfitvnm.youtubemusic.YouTube
|
||||||
|
|
||||||
@Stable
|
|
||||||
class Preferences(holder: SharedPreferences) : SharedPreferences by holder {
|
|
||||||
var colorPaletteMode by preference("colorPaletteMode", ColorPaletteMode.System)
|
|
||||||
var searchFilter by preference("searchFilter", YouTube.Item.Song.Filter.value)
|
|
||||||
var repeatMode by preference("repeatMode", Player.REPEAT_MODE_OFF)
|
|
||||||
var homePageSongCollection by preference("homePageSongCollection", SongCollection.MostPlayed)
|
|
||||||
var thumbnailRoundness by preference("thumbnailRoundness", ThumbnailRoundness.Light)
|
|
||||||
var coilDiskCacheMaxSizeBytes by preference("coilDiskCacheMaxSizeBytes", 512L * 1024 * 1024)
|
|
||||||
var exoPlayerDiskCacheMaxSizeBytes by preference("exoPlayerDiskCacheMaxSizeBytes", 512L * 1024 * 1024)
|
|
||||||
var skipSilence by preference("skipSilence", false)
|
|
||||||
var volumeNormalization by preference("volumeNormalization", true)
|
|
||||||
var persistentQueue by preference("persistentQueue", false)
|
|
||||||
}
|
|
||||||
|
|
||||||
val Context.preferences: Preferences
|
@Stable
|
||||||
get() = Preferences(getSharedPreferences("preferences", Context.MODE_PRIVATE))
|
class Preferences(
|
||||||
|
private val edit: (action: SharedPreferences.Editor.() -> Unit) -> Unit,
|
||||||
|
initialColorPaletteMode: ColorPaletteMode,
|
||||||
|
initialSearchFilter: String,
|
||||||
|
initialRepeatMode: Int,
|
||||||
|
initialHomePageSongCollection: SongCollection,
|
||||||
|
initialThumbnailRoundness: ThumbnailRoundness,
|
||||||
|
initialCoilDiskCacheMaxSizeBytes: Long,
|
||||||
|
initialExoPlayerDiskCacheMaxSizeBytes: Long,
|
||||||
|
initialSkipSilence: Boolean,
|
||||||
|
initialVolumeNormalization: Boolean,
|
||||||
|
initialPersistentQueue: Boolean,
|
||||||
|
) {
|
||||||
|
constructor(preferences: SharedPreferences) : this(
|
||||||
|
edit = { action: SharedPreferences.Editor.() -> Unit ->
|
||||||
|
preferences.edit(action = action)
|
||||||
|
},
|
||||||
|
initialColorPaletteMode = preferences.getEnum(Keys.colorPaletteMode, ColorPaletteMode.System),
|
||||||
|
initialSearchFilter = preferences.getString(Keys.searchFilter, YouTube.Item.Song.Filter.value)!!,
|
||||||
|
initialRepeatMode = preferences.getInt(Keys.repeatMode, Player.REPEAT_MODE_OFF),
|
||||||
|
initialHomePageSongCollection = preferences.getEnum(Keys.homePageSongCollection, SongCollection.History),
|
||||||
|
initialThumbnailRoundness = preferences.getEnum(Keys.thumbnailRoundness, ThumbnailRoundness.Light),
|
||||||
|
initialCoilDiskCacheMaxSizeBytes = preferences.getLong(Keys.coilDiskCacheMaxSizeBytes, 512L * 1024 * 1024),
|
||||||
|
initialExoPlayerDiskCacheMaxSizeBytes = preferences.getLong(Keys.exoPlayerDiskCacheMaxSizeBytes, 512L * 1024 * 1024),
|
||||||
|
initialSkipSilence = preferences.getBoolean(Keys.skipSilence, false),
|
||||||
|
initialVolumeNormalization = preferences.getBoolean(Keys.volumeNormalization, false),
|
||||||
|
initialPersistentQueue = preferences.getBoolean(Keys.persistentQueue, false)
|
||||||
|
)
|
||||||
|
|
||||||
|
var colorPaletteMode = initialColorPaletteMode
|
||||||
|
set(value) = edit { putEnum(Keys.colorPaletteMode, value) }
|
||||||
|
|
||||||
|
var searchFilter = initialSearchFilter
|
||||||
|
set(value) = edit { putString(Keys.searchFilter, value) }
|
||||||
|
|
||||||
|
var repeatMode = initialRepeatMode
|
||||||
|
set(value) = edit { putInt(Keys.repeatMode, value) }
|
||||||
|
|
||||||
|
var homePageSongCollection = initialHomePageSongCollection
|
||||||
|
set(value) = edit { putEnum(Keys.homePageSongCollection, value) }
|
||||||
|
|
||||||
|
var thumbnailRoundness = initialThumbnailRoundness
|
||||||
|
set(value) = edit { putEnum(Keys.thumbnailRoundness, value) }
|
||||||
|
|
||||||
|
var coilDiskCacheMaxSizeBytes = initialCoilDiskCacheMaxSizeBytes
|
||||||
|
set(value) = edit { putLong(Keys.coilDiskCacheMaxSizeBytes, value) }
|
||||||
|
|
||||||
|
var exoPlayerDiskCacheMaxSizeBytes = initialExoPlayerDiskCacheMaxSizeBytes
|
||||||
|
set(value) = edit { putLong(Keys.exoPlayerDiskCacheMaxSizeBytes, value) }
|
||||||
|
|
||||||
|
var skipSilence = initialSkipSilence
|
||||||
|
set(value) = edit { putBoolean(Keys.skipSilence, value) }
|
||||||
|
|
||||||
|
var volumeNormalization = initialVolumeNormalization
|
||||||
|
set(value) = edit { putBoolean(Keys.volumeNormalization, value) }
|
||||||
|
|
||||||
|
var persistentQueue = initialPersistentQueue
|
||||||
|
set(value) = edit { putBoolean(Keys.persistentQueue, value) }
|
||||||
|
|
||||||
|
private object Keys {
|
||||||
|
const val colorPaletteMode = "colorPaletteMode"
|
||||||
|
const val searchFilter = "searchFilter"
|
||||||
|
const val repeatMode = "repeatMode"
|
||||||
|
const val homePageSongCollection = "homePageSongCollection"
|
||||||
|
const val thumbnailRoundness = "thumbnailRoundness"
|
||||||
|
const val coilDiskCacheMaxSizeBytes = "coilDiskCacheMaxSizeBytes"
|
||||||
|
const val exoPlayerDiskCacheMaxSizeBytes = "exoPlayerDiskCacheMaxSizeBytes"
|
||||||
|
const val skipSilence = "skipSilence"
|
||||||
|
const val volumeNormalization = "volumeNormalization"
|
||||||
|
const val persistentQueue = "persistentQueue"
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val fileName = "preferences"
|
||||||
|
|
||||||
|
context(Context)
|
||||||
|
operator fun invoke() =
|
||||||
|
Preferences(getSharedPreferences(fileName, Context.MODE_PRIVATE))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val LocalPreferences = staticCompositionLocalOf<Preferences> { TODO() }
|
val LocalPreferences = staticCompositionLocalOf<Preferences> { TODO() }
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun rememberPreferences(): Preferences {
|
fun rememberPreferences(): Preferences {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
return remember {
|
var preferences by remember {
|
||||||
context.preferences
|
mutableStateOf(context.run { Preferences() })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DisposableEffect(Unit) {
|
||||||
|
val holder = context.getSharedPreferences(Preferences.fileName, Context.MODE_PRIVATE)
|
||||||
|
|
||||||
|
val listener =
|
||||||
|
SharedPreferences.OnSharedPreferenceChangeListener { sharedPreferences, _ ->
|
||||||
|
preferences = Preferences(sharedPreferences)
|
||||||
|
}
|
||||||
|
|
||||||
|
holder.registerOnSharedPreferenceChangeListener(listener)
|
||||||
|
|
||||||
|
onDispose {
|
||||||
|
holder.unregisterOnSharedPreferenceChangeListener(listener)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return preferences
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun SharedPreferences.preference(key: String, defaultValue: Boolean) =
|
private inline fun <reified T : Enum<T>> SharedPreferences.getEnum(
|
||||||
mutableStateOf(value = getBoolean(key, defaultValue)) {
|
|
||||||
edit {
|
|
||||||
putBoolean(key, it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun SharedPreferences.preference(key: String, defaultValue: Int) =
|
|
||||||
mutableStateOf(value = getInt(key, defaultValue)) {
|
|
||||||
edit {
|
|
||||||
putInt(key, it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun SharedPreferences.preference(key: String, defaultValue: Long) =
|
|
||||||
mutableStateOf(value = getLong(key, defaultValue)) {
|
|
||||||
edit {
|
|
||||||
putLong(key, it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun SharedPreferences.preference(key: String, defaultValue: Float) =
|
|
||||||
mutableStateOf(value = getFloat(key, defaultValue)) {
|
|
||||||
edit {
|
|
||||||
putFloat(key, it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun SharedPreferences.preference(key: String, defaultValue: String) =
|
|
||||||
mutableStateOf(value = getString(key, defaultValue)!!) {
|
|
||||||
edit {
|
|
||||||
putString(key, it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun SharedPreferences.preference(key: String, defaultValue: Set<String>) =
|
|
||||||
mutableStateOf(value = getStringSet(key, defaultValue)!!) {
|
|
||||||
edit {
|
|
||||||
putStringSet(key, it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun SharedPreferences.preference(key: String, defaultValue: Dp) =
|
|
||||||
mutableStateOf(value = getFloat(key, defaultValue.value).dp) {
|
|
||||||
edit {
|
|
||||||
putFloat(key, it.value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun SharedPreferences.preference(key: String, defaultValue: TextUnit) =
|
|
||||||
mutableStateOf(value = getFloat(key, defaultValue.value).sp) {
|
|
||||||
edit {
|
|
||||||
putFloat(key, it.value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private inline fun <reified T : Enum<T>> SharedPreferences.preference(
|
|
||||||
key: String,
|
key: String,
|
||||||
defaultValue: T
|
defaultValue: T
|
||||||
) = mutableStateOf(value = enumValueOf<T>(getString(key, defaultValue.name)!!)) {
|
): T =
|
||||||
edit {
|
getString(key, null)?.let {
|
||||||
putString(key, it.name)
|
try {
|
||||||
}
|
enumValueOf<T>(it)
|
||||||
|
} catch (e: IllegalArgumentException) {
|
||||||
|
null
|
||||||
}
|
}
|
||||||
|
} ?: defaultValue
|
||||||
|
|
||||||
|
private inline fun <reified T : Enum<T>> SharedPreferences.Editor.putEnum(key: String, value: T) =
|
||||||
|
putString(key, value.name)
|
||||||
|
|
||||||
private fun <T> mutableStateOf(value: T, onStructuralInequality: (newValue: T) -> Unit) =
|
|
||||||
mutableStateOf(
|
|
||||||
value = value,
|
|
||||||
policy = object : SnapshotMutationPolicy<T> {
|
|
||||||
override fun equivalent(a: T, b: T): Boolean {
|
|
||||||
val areEquals = a == b
|
|
||||||
if (!areEquals) onStructuralInequality(b)
|
|
||||||
return areEquals
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
Loading…
Reference in a new issue