Skip to content

Commit c435228

Browse files
committed
refactor: make AppPreference type-safe
1 parent fed187f commit c435228

File tree

3 files changed

+354
-200
lines changed

3 files changed

+354
-200
lines changed

app/src/main/java/me/ash/reader/infrastructure/preference/DarkThemePreference.kt

Lines changed: 31 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -6,26 +6,27 @@ import androidx.compose.runtime.Composable
66
import androidx.compose.runtime.ReadOnlyComposable
77
import androidx.compose.runtime.compositionLocalOf
88
import androidx.datastore.preferences.core.Preferences
9-
import kotlinx.coroutines.CoroutineScope
10-
import kotlinx.coroutines.launch
9+
import androidx.datastore.preferences.core.edit
1110
import me.ash.reader.R
12-
import me.ash.reader.ui.ext.DataStoreKey
13-
import me.ash.reader.ui.ext.DataStoreKey.Companion.darkTheme
11+
import me.ash.reader.ui.ext.PreferenceKey
1412
import me.ash.reader.ui.ext.dataStore
15-
import me.ash.reader.ui.ext.put
13+
import me.ash.reader.ui.ext.get
14+
import me.ash.reader.ui.ext.set
1615

17-
val LocalDarkTheme =
18-
compositionLocalOf<DarkThemePreference> { DarkThemePreference.default }
16+
val LocalDarkTheme = compositionLocalOf<DarkThemePreference> { DarkThemePreference.default }
17+
18+
sealed class DarkThemePreference(override val value: Int) :
19+
AppPreference.IntPreference, AppPreference.Editable {
20+
override val key: PreferenceKey.IntKey = Companion.key
1921

20-
sealed class DarkThemePreference(val value: Int) : Preference() {
2122
object UseDeviceTheme : DarkThemePreference(0)
23+
2224
object ON : DarkThemePreference(1)
25+
2326
object OFF : DarkThemePreference(2)
2427

25-
override fun put(context: Context, scope: CoroutineScope) {
26-
scope.launch {
27-
context.dataStore.put(darkTheme, value)
28-
}
28+
override suspend fun put(context: Context) {
29+
context.dataStore.edit { it[key] = value }
2930
}
3031

3132
fun toDesc(context: Context): String =
@@ -37,19 +38,21 @@ sealed class DarkThemePreference(val value: Int) : Preference() {
3738

3839
@Composable
3940
@ReadOnlyComposable
40-
fun isDarkTheme(): Boolean = when (this) {
41-
UseDeviceTheme -> isSystemInDarkTheme()
42-
ON -> true
43-
OFF -> false
44-
}
41+
fun isDarkTheme(): Boolean =
42+
when (this) {
43+
UseDeviceTheme -> isSystemInDarkTheme()
44+
ON -> true
45+
OFF -> false
46+
}
4547

46-
companion object {
48+
companion object : AppPreference.PreferenceCompanion {
49+
override val key: PreferenceKey.IntKey = PreferenceKey.DarkTheme
4750

48-
val default = UseDeviceTheme
49-
val values = listOf(UseDeviceTheme, ON, OFF)
51+
override val default = UseDeviceTheme
52+
override val values = listOf(UseDeviceTheme, ON, OFF)
5053

51-
fun fromPreferences(preferences: Preferences) =
52-
when (preferences[DataStoreKey.keys[darkTheme]?.key as Preferences.Key<Int>]) {
54+
override fun fromPreferences(preferences: Preferences) =
55+
when (preferences[key]) {
5356
0 -> UseDeviceTheme
5457
1 -> ON
5558
2 -> OFF
@@ -61,11 +64,12 @@ sealed class DarkThemePreference(val value: Int) : Preference() {
6164
@Composable
6265
operator fun DarkThemePreference.not(): DarkThemePreference =
6366
when (this) {
64-
DarkThemePreference.UseDeviceTheme -> if (isSystemInDarkTheme()) {
65-
DarkThemePreference.OFF
66-
} else {
67-
DarkThemePreference.ON
68-
}
67+
DarkThemePreference.UseDeviceTheme ->
68+
if (isSystemInDarkTheme()) {
69+
DarkThemePreference.OFF
70+
} else {
71+
DarkThemePreference.ON
72+
}
6973

7074
DarkThemePreference.ON -> DarkThemePreference.OFF
7175
DarkThemePreference.OFF -> DarkThemePreference.ON

app/src/main/java/me/ash/reader/infrastructure/preference/Preference.kt

Lines changed: 66 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,66 @@ package me.ash.reader.infrastructure.preference
33
import android.content.Context
44
import androidx.datastore.preferences.core.Preferences
55
import kotlinx.coroutines.CoroutineScope
6+
import kotlinx.coroutines.launch
7+
import me.ash.reader.ui.ext.PreferenceKey
8+
import org.json.JSONObject
9+
10+
sealed interface AppPreference {
11+
val value: Any
12+
val key: PreferenceKey
13+
14+
interface IntPreference : AppPreference {
15+
override val value: Int
16+
override val key: PreferenceKey.IntKey
17+
}
18+
19+
interface LongPreference : AppPreference {
20+
override val value: Long
21+
override val key: PreferenceKey.LongKey
22+
}
23+
24+
interface StringPreference : AppPreference {
25+
override val value: String
26+
override val key: PreferenceKey.StringKey
27+
}
28+
29+
interface BooleanPreference : AppPreference {
30+
override val value: Boolean
31+
override val key: PreferenceKey.BooleanKey
32+
}
33+
34+
interface FloatPreference : AppPreference {
35+
override val value: Float
36+
override val key: PreferenceKey.FloatKey
37+
}
38+
39+
interface PreferenceCompanion {
40+
val key: PreferenceKey
41+
val default: AppPreference
42+
43+
fun fromPreferences(preferences: Preferences): AppPreference
44+
45+
val values: List<AppPreference>
46+
}
47+
48+
sealed interface Editable {
49+
suspend fun put(context: Context)
50+
51+
@Deprecated("Use the suspend function instead")
52+
fun put(context: Context, coroutineScope: CoroutineScope) =
53+
coroutineScope.launch { put(context) }
54+
}
55+
}
56+
57+
fun JSONObject.put(preference: AppPreference) {
58+
when (preference) {
59+
is AppPreference.BooleanPreference -> put(preference.key.name, preference.value)
60+
is AppPreference.FloatPreference -> put(preference.key.name, preference.value)
61+
is AppPreference.IntPreference -> put(preference.key.name, preference.value)
62+
is AppPreference.LongPreference -> put(preference.key.name, preference.value)
63+
is AppPreference.StringPreference -> put(preference.key.name, preference.value)
64+
}
65+
}
666

767
sealed class Preference {
868

@@ -44,11 +104,11 @@ fun Preferences.toSettings(): Settings {
44104
flowArticleListImage = FlowArticleListImagePreference.fromPreferences(this),
45105
flowArticleListDesc = FlowArticleListDescPreference.fromPreferences(this),
46106
flowArticleListTime = FlowArticleListTimePreference.fromPreferences(this),
47-
flowArticleListDateStickyHeader = FlowArticleListDateStickyHeaderPreference.fromPreferences(
48-
this
49-
),
107+
flowArticleListDateStickyHeader =
108+
FlowArticleListDateStickyHeaderPreference.fromPreferences(this),
50109
flowArticleListReadIndicator = FlowArticleReadIndicatorPreference.fromPreferences(this),
51-
flowArticleListTonalElevation = FlowArticleListTonalElevationPreference.fromPreferences(this),
110+
flowArticleListTonalElevation =
111+
FlowArticleListTonalElevationPreference.fromPreferences(this),
52112
flowSortUnreadArticles = SortUnreadArticlesPreference.fromPreferences(this),
53113

54114
// Reading page
@@ -70,7 +130,8 @@ fun Preferences.toSettings(): Settings {
70130
readingSubheadBold = ReadingSubheadBoldPreference.fromPreferences(this),
71131
readingTitleUpperCase = ReadingTitleUpperCasePreference.fromPreferences(this),
72132
readingSubheadUpperCase = ReadingSubheadUpperCasePreference.fromPreferences(this),
73-
readingImageHorizontalPadding = ReadingImageHorizontalPaddingPreference.fromPreferences(this),
133+
readingImageHorizontalPadding =
134+
ReadingImageHorizontalPaddingPreference.fromPreferences(this),
74135
readingImageRoundedCorners = ReadingImageRoundedCornersPreference.fromPreferences(this),
75136
readingImageMaximize = ReadingImageMaximizePreference.fromPreferences(this),
76137

0 commit comments

Comments
 (0)