Refactor preferences to avoid a global state when it's not necessary

This commit is contained in:
vfsfitvnm
2022-07-16 13:51:38 +02:00
parent aa8e065412
commit 1e719b33ed
38 changed files with 415 additions and 452 deletions

View File

@@ -5,145 +5,24 @@ import android.content.SharedPreferences
import androidx.compose.runtime.*
import androidx.compose.ui.platform.LocalContext
import androidx.core.content.edit
import androidx.media3.common.Player
import it.vfsfitvnm.vimusic.enums.*
import it.vfsfitvnm.youtubemusic.YouTube
@Stable
class Preferences(
private val edit: (action: SharedPreferences.Editor.() -> Unit) -> Unit,
initialIsFirstLaunch: Boolean,
initialSongSortBy: SongSortBy,
initialSongSortOrder: SortOrder,
initialColorPaletteMode: ColorPaletteMode,
initialSearchFilter: String,
initialRepeatMode: Int,
initialThumbnailRoundness: ThumbnailRoundness,
initialCoilDiskCacheMaxSize: CoilDiskCacheMaxSize,
initialExoPlayerDiskCacheMaxSize: ExoPlayerDiskCacheMaxSize,
initialSkipSilence: Boolean,
initialVolumeNormalization: Boolean,
initialPersistentQueue: Boolean,
initialIsInvincibilityEnabled: Boolean,
initialIsCachedPlaylistShown: Boolean,
) {
constructor(preferences: SharedPreferences) : this(
edit = { action: SharedPreferences.Editor.() -> Unit ->
preferences.edit(action = action)
},
initialIsFirstLaunch = preferences.getBoolean(Keys.isFirstLaunch, true),
initialSongSortBy = preferences.getEnum(Keys.songSortBy, SongSortBy.DateAdded),
initialSongSortOrder = preferences.getEnum(Keys.songSortOrder, SortOrder.Descending),
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),
initialThumbnailRoundness = preferences.getEnum(Keys.thumbnailRoundness, ThumbnailRoundness.Light),
initialCoilDiskCacheMaxSize = preferences.getEnum(Keys.coilDiskCacheMaxSize, CoilDiskCacheMaxSize.`128MB`),
initialExoPlayerDiskCacheMaxSize = preferences.getEnum(Keys.exoPlayerDiskCacheMaxSize, ExoPlayerDiskCacheMaxSize.`2GB`),
initialSkipSilence = preferences.getBoolean(Keys.skipSilence, false),
initialVolumeNormalization = preferences.getBoolean(Keys.volumeNormalization, false),
initialPersistentQueue = preferences.getBoolean(Keys.persistentQueue, false),
initialIsInvincibilityEnabled = preferences.getBoolean(Keys.isInvincibilityEnabled, false),
initialIsCachedPlaylistShown = preferences.getBoolean(Keys.isCachedPlaylistShown, false),
)
const val colorPaletteModeKey = "colorPaletteMode"
const val thumbnailRoundnessKey = "thumbnailRoundness"
const val isCachedPlaylistShownKey = "isCachedPlaylistShown"
const val coilDiskCacheMaxSizeKey = "coilDiskCacheMaxSize"
const val exoPlayerDiskCacheMaxSizeKey = "exoPlayerDiskCacheMaxSize"
const val isInvincibilityEnabledKey = "isInvincibilityEnabled"
const val isFirstLaunchKey = "isFirstLaunch"
const val songSortOrderKey = "songSortOrder"
const val songSortByKey = "songSortBy"
const val searchFilterKey = "searchFilter"
const val repeatModeKey = "repeatMode"
const val skipSilenceKey = "skipSilence"
const val volumeNormalizationKey = "volumeNormalization"
const val persistentQueueKey = "persistentQueue"
var isFirstLaunch = initialIsFirstLaunch
set(value) = edit { putBoolean(Keys.isFirstLaunch, value) }
var songSortBy = initialSongSortBy
set(value) = edit { putEnum(Keys.songSortBy, value) }
var songSortOrder = initialSongSortOrder
set(value) = edit { putEnum(Keys.songSortOrder, value) }
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 thumbnailRoundness = initialThumbnailRoundness
set(value) = edit { putEnum(Keys.thumbnailRoundness, value) }
var coilDiskCacheMaxSize = initialCoilDiskCacheMaxSize
set(value) = edit { putEnum(Keys.coilDiskCacheMaxSize, value) }
var exoPlayerDiskCacheMaxSize = initialExoPlayerDiskCacheMaxSize
set(value) = edit { putEnum(Keys.exoPlayerDiskCacheMaxSize, 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) }
var isInvincibilityEnabled = initialIsInvincibilityEnabled
set(value) = edit { putBoolean(Keys.isInvincibilityEnabled, value) }
var isCachedPlaylistShown = initialIsCachedPlaylistShown
set(value) = edit { putBoolean(Keys.isCachedPlaylistShown, value) }
object Keys {
const val isFirstLaunch = "isFirstLaunch"
const val songSortOrder = "songSortOrder"
const val songSortBy = "songSortBy"
const val colorPaletteMode = "colorPaletteMode"
const val searchFilter = "searchFilter"
const val repeatMode = "repeatMode"
const val thumbnailRoundness = "thumbnailRoundness"
const val coilDiskCacheMaxSize = "coilDiskCacheMaxSize"
const val exoPlayerDiskCacheMaxSize = "exoPlayerDiskCacheMaxSize"
const val skipSilence = "skipSilence"
const val volumeNormalization = "volumeNormalization"
const val persistentQueue = "persistentQueue"
const val isInvincibilityEnabled = "isInvincibilityEnabled"
const val isCachedPlaylistShown = "isCachedPlaylistShown"
}
companion object {
const val fileName = "preferences"
context(Context)
operator fun invoke() =
Preferences(getSharedPreferences(fileName, Context.MODE_PRIVATE))
}
}
val LocalPreferences = staticCompositionLocalOf<Preferences> { TODO() }
@Composable
fun rememberPreferences(): Preferences {
val context = LocalContext.current
var preferences by remember {
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 inline fun <reified T : Enum<T>> SharedPreferences.getEnum(
inline fun <reified T : Enum<T>> SharedPreferences.getEnum(
key: String,
defaultValue: T
): T =
@@ -155,6 +34,51 @@ private inline fun <reified T : Enum<T>> SharedPreferences.getEnum(
}
} ?: defaultValue
private inline fun <reified T : Enum<T>> SharedPreferences.Editor.putEnum(key: String, value: T) =
inline fun <reified T : Enum<T>> SharedPreferences.Editor.putEnum(key: String, value: T) =
putString(key, value.name)
val Context.preferences: SharedPreferences
get() = getSharedPreferences("preferences", Context.MODE_PRIVATE)
@Composable
fun rememberPreference(key: String, defaultValue: Boolean): MutableState<Boolean> {
val context = LocalContext.current
return remember {
mutableStatePreferenceOf(context.preferences.getBoolean(key, defaultValue)) {
context.preferences.edit { putBoolean(key, it) }
}
}
}
@Composable
fun rememberPreference(key: String, defaultValue: String): MutableState<String> {
val context = LocalContext.current
return remember {
mutableStatePreferenceOf(context.preferences.getString(key, null) ?: defaultValue) {
context.preferences.edit { putString(key, it) }
}
}
}
@Composable
inline fun <reified T: Enum<T>> rememberPreference(key: String, defaultValue: T): MutableState<T> {
val context = LocalContext.current
return remember {
mutableStatePreferenceOf(context.preferences.getEnum(key, defaultValue)) {
context.preferences.edit { putEnum(key, it) }
}
}
}
inline fun <T> mutableStatePreferenceOf(value: T, crossinline 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
}
})

View File

@@ -7,7 +7,7 @@ import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import it.vfsfitvnm.vimusic.ui.styling.LocalColorPalette
import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance
fun TextStyle.style(style: FontStyle) = copy(fontStyle = style)
@@ -35,9 +35,9 @@ inline val TextStyle.center: TextStyle
inline val TextStyle.secondary: TextStyle
@Composable
@ReadOnlyComposable
get() = color(LocalColorPalette.current.textSecondary)
get() = color(LocalAppearance.current.colorPalette.textSecondary)
inline val TextStyle.disabled: TextStyle
@Composable
@ReadOnlyComposable
get() = color(LocalColorPalette.current.textDisabled)
get() = color(LocalAppearance.current.colorPalette.textDisabled)