New Preferences class

This commit is contained in:
vfsfitvnm 2022-07-01 16:45:32 +02:00
parent 9139609cf3
commit b738910d63
3 changed files with 117 additions and 96 deletions

View file

@ -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()

View file

@ -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")

View file

@ -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
}
})