diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/utils/Preferences.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/utils/Preferences.kt index 8077fd3..e3dd17f 100644 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/utils/Preferences.kt +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/utils/Preferences.kt @@ -1,112 +1,123 @@ package it.vfsfitvnm.vimusic.utils -import android.content.Context -import android.content.SharedPreferences import androidx.compose.runtime.* -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.datastore.core.DataStore +import androidx.datastore.preferences.core.edit +import androidx.datastore.preferences.core.intPreferencesKey +import androidx.datastore.preferences.core.stringPreferencesKey import androidx.media3.common.Player import it.vfsfitvnm.vimusic.enums.ColorPaletteMode import it.vfsfitvnm.vimusic.enums.SongCollection import it.vfsfitvnm.vimusic.enums.ThumbnailRoundness import it.vfsfitvnm.youtubemusic.YouTube +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.launch +import androidx.datastore.preferences.core.Preferences as DataStorePreferences -@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) + +@Immutable +data class Preferences( + val isReady: Boolean, + val colorPaletteMode: ColorPaletteMode, + val onColorPaletteModeChange: (ColorPaletteMode) -> Unit, + val searchFilter: String, + val onSearchFilterChange: (String) -> Unit, + val repeatMode: Int, + val onRepeatModeChange: (Int) -> Unit, + val homePageSongCollection: SongCollection, + val onHomePageSongCollectionChange: (SongCollection) -> Unit, + val thumbnailRoundness: ThumbnailRoundness, + val onThumbnailRoundnessChange: (ThumbnailRoundness) -> Unit, +) { + constructor( + isReady: Boolean, + colorPaletteMode: ColorPaletteMode? = null, + onColorPaletteModeChange: (ColorPaletteMode) -> Unit = {}, + searchFilter: String? = null, + onSearchFilterChange: (String) -> Unit = {}, + repeatMode: Int? = null, + onRepeatModeChange: (Int) -> Unit = {}, + homePageSongCollection: SongCollection? = null, + onHomePageSongCollectionChange: (SongCollection) -> Unit = {}, + thumbnailRoundness: ThumbnailRoundness? = null, + onThumbnailRoundnessChange: (ThumbnailRoundness) -> Unit = {}, + ) : this( + isReady = isReady, + colorPaletteMode = colorPaletteMode ?: ColorPaletteMode.System, + onColorPaletteModeChange = onColorPaletteModeChange, + searchFilter = searchFilter ?: YouTube.Item.Song.Filter.value, + onSearchFilterChange = onSearchFilterChange, + repeatMode = repeatMode ?: Player.REPEAT_MODE_OFF, + onRepeatModeChange = onRepeatModeChange, + homePageSongCollection = homePageSongCollection ?: SongCollection.MostPlayed, + onHomePageSongCollectionChange = onHomePageSongCollectionChange, + thumbnailRoundness = thumbnailRoundness ?: ThumbnailRoundness.Light, + onThumbnailRoundnessChange = onThumbnailRoundnessChange + ) + + companion object { + val Default = Preferences(isReady = false) + } } -val LocalPreferences = staticCompositionLocalOf { TODO() } +val LocalPreferences = staticCompositionLocalOf { Preferences.Default } + +private val colorPaletteModeKey = stringPreferencesKey("colorPaletteMode") +private val searchFilterKey = stringPreferencesKey("searchFilter") +private val repeatModeKey = intPreferencesKey("repeatMode") +private val homePageSongCollectionKey = stringPreferencesKey("homePageSongCollection") +private val thumbnailRoundnessKey = stringPreferencesKey("thumbnailRoundness") @Composable -fun rememberPreferences(): Preferences { - val context = LocalContext.current - return remember { - Preferences(context.getSharedPreferences("preferences", Context.MODE_PRIVATE)) - } +fun rememberPreferences(dataStore: DataStore): State { + val coroutineScope = rememberCoroutineScope() + + return remember(dataStore, coroutineScope) { + dataStore.data.map { preferences -> + Preferences( + isReady = true, + colorPaletteMode = preferences[colorPaletteModeKey]?.let { enumValueOf(it) }, + onColorPaletteModeChange = { colorPaletteMode -> + coroutineScope.launch(Dispatchers.IO) { + dataStore.edit { mutablePreferences -> + mutablePreferences[colorPaletteModeKey] = colorPaletteMode.name + } + } + }, + searchFilter = preferences[searchFilterKey], + onSearchFilterChange = { searchFilter -> + coroutineScope.launch(Dispatchers.IO) { + dataStore.edit { mutablePreferences -> + mutablePreferences[searchFilterKey] = searchFilter + } + } + }, + repeatMode = preferences[repeatModeKey], + onRepeatModeChange = { repeatMode -> + coroutineScope.launch(Dispatchers.IO) { + dataStore.edit { mutablePreferences -> + mutablePreferences[repeatModeKey] = repeatMode + } + } + }, + homePageSongCollection = preferences[homePageSongCollectionKey]?.let { enumValueOf(it) }, + onHomePageSongCollectionChange = { homePageSongCollection -> + coroutineScope.launch(Dispatchers.IO) { + dataStore.edit { mutablePreferences -> + mutablePreferences[homePageSongCollectionKey] = homePageSongCollection.name + } + } + }, + thumbnailRoundness = preferences[thumbnailRoundnessKey]?.let { enumValueOf(it) }, + onThumbnailRoundnessChange = { thumbnailRoundness -> + coroutineScope.launch(Dispatchers.IO) { + dataStore.edit { mutablePreferences -> + mutablePreferences[thumbnailRoundnessKey] = thumbnailRoundness.name + } + } + }, + ) + } + }.collectAsState(initial = Preferences.Default, context = Dispatchers.IO) } - -private fun SharedPreferences.preference(key: String, defaultValue: Boolean) = - 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) = - 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 > SharedPreferences.preference( - key: String, - defaultValue: T -) = - mutableStateOf(value = enumValueOf(getString(key, defaultValue.name)!!)) { - edit { - putString(key, it.name) - } - } - -private fun mutableStateOf(value: T, onStructuralInequality: (newValue: T) -> Unit) = - mutableStateOf( - value = value, - policy = object : SnapshotMutationPolicy { - override fun equivalent(a: T, b: T): Boolean { - val areEquals = a == b - if (!areEquals) onStructuralInequality(b) - return areEquals - } - })