Skip to content

Commit 4c95476

Browse files
committed
feat: Add support for more data types and improve DataStoreManager
This commit introduces several significant enhancements to the ComposePreferences library: - **Expanded Data Type Support: ** The `DataStoreManager` now supports storing and retrieving a wider range of data types, including `Int`, `Boolean`, `Float`, `Long`, and `Set<String>`, in addition to existing support. This is achieved through efficient serialization using Kotlin Serialization. - **Improved DataStoreManager:** The `DataStoreManager`'s `setPreferences` and `getPreference` functions are refined to handle the expanded data types and use Kotlin Serialization for type-safe operations. The internal `Preference` class is restructured for better type safety and maintainability. The `PreferenceData` class is internal and handles serialization. - **Added Unit Tests:** Comprehensive unit tests for `DataStoreManager` have been implemented to ensure the reliability and correctness of the added functionality. An existing, irrelevant test file (`ExampleInstrumentedTest.kt`) has been removed. - **Updated Dependencies:** The `kotlin-serialization` plugin has been added to the project's dependencies. - **Version Bump:** The library version has been bumped to 1.0.1. - **Improved Readme: ** The README has been updated to reflect the new features and dependency changes, including support for a version catalog. Example usage has been improved to showcase the new features. These changes significantly enhance the flexibility and usability of ComposePreferences.
1 parent b1f7bc2 commit 4c95476

20 files changed

Lines changed: 231 additions & 125 deletions

ComposePreferences/build.gradle.kts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ plugins {
22
alias(libs.plugins.android.library)
33
alias(libs.plugins.kotlin.android)
44
alias(libs.plugins.kotlin.compose)
5+
alias(libs.plugins.kotlin.serialization)
56
alias(libs.plugins.maven.publish)
67
alias(libs.plugins.gradleup.nmcp)
78
}
@@ -45,4 +46,9 @@ dependencies {
4546
implementation(libs.androidx.material3)
4647
implementation(libs.androidx.datastore.preferences)
4748
implementation(libs.kotlin.serialization.json)
49+
50+
testImplementation(libs.junit)
51+
androidTestImplementation(libs.androidx.junit)
52+
androidTestImplementation(platform(libs.androidx.compose.bom))
53+
androidTestImplementation(libs.androidx.ui.test.junit4)
4854
}
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
package com.strabled.composepreferences
2+
3+
import androidx.test.ext.junit.runners.AndroidJUnit4
4+
import androidx.test.platform.app.InstrumentationRegistry
5+
import com.strabled.composepreferences.utilis.DataStoreManager
6+
import kotlinx.serialization.Serializable
7+
import org.junit.Assert.assertEquals
8+
import org.junit.Before
9+
import org.junit.Test
10+
import org.junit.runner.RunWith
11+
12+
/**
13+
* Instrumented test for the DataStoreManager class.
14+
*/
15+
@RunWith(AndroidJUnit4::class)
16+
class DataStoreManagerTest {
17+
18+
private lateinit var dataStoreManager: DataStoreManager
19+
20+
val testUser = User("John", 30)
21+
val testTheme = Theme.DARK
22+
val testString = "testString"
23+
val testInt = 123
24+
val testBoolean = true
25+
val testFloat = 3.14f
26+
val testLong = 123456789L
27+
28+
val preferenceMap = mapOf(
29+
"user" to testUser,
30+
"theme" to testTheme,
31+
"string" to testString,
32+
"int" to testInt,
33+
"boolean" to testBoolean,
34+
"float" to testFloat,
35+
"long" to testLong
36+
)
37+
38+
/**
39+
* Sets up the test environment before each test.
40+
*/
41+
@Before
42+
fun setUp() {
43+
val context = InstrumentationRegistry.getInstrumentation().targetContext
44+
dataStoreManager = DataStoreManager(context)
45+
dataStoreManager.setPreferences(preferenceMap)
46+
}
47+
48+
/**
49+
* Tests the DataStoreManager's ability to store and retrieve preferences.
50+
*/
51+
@Test
52+
fun testDataStoreManager() {
53+
val userPreference by dataStoreManager.getPreference<User>("user")
54+
val userPreferenceData = userPreference.value
55+
assertEquals(userPreferenceData, testUser)
56+
57+
val themePreference by dataStoreManager.getPreference<Theme>("theme")
58+
val themePreferenceData: Theme = themePreference.value
59+
assertEquals(themePreferenceData, testTheme)
60+
61+
val stringPreference by dataStoreManager.getPreference<String>("string")
62+
val stringPreferenceData: String = stringPreference.value
63+
assertEquals(stringPreferenceData, testString)
64+
65+
val intPreference by dataStoreManager.getPreference<Int>("int")
66+
val intPreferenceData: Int = intPreference.value
67+
assertEquals(intPreferenceData, testInt)
68+
69+
val booleanPreference by dataStoreManager.getPreference<Boolean>("boolean")
70+
val booleanPreferenceData: Boolean = booleanPreference.value
71+
assertEquals(booleanPreferenceData, testBoolean)
72+
73+
val floatPreference by dataStoreManager.getPreference<Float>("float")
74+
val floatPreferenceData: Float = floatPreference.value
75+
assertEquals(floatPreferenceData, testFloat)
76+
77+
val longPreference by dataStoreManager.getPreference<Long>("long")
78+
val longPreferenceData: Long = longPreference.value
79+
assertEquals(longPreferenceData, testLong)
80+
}
81+
82+
}
83+
84+
/**
85+
* Serializable data class representing a User.
86+
*/
87+
@Serializable
88+
data class User(val name: String, val age: Int)
89+
90+
/**
91+
* Enum class representing different themes.
92+
*/
93+
enum class Theme {
94+
LIGHT, DARK
95+
}

ComposePreferences/src/androidTest/java/com/strabled/composepreferences/ExampleInstrumentedTest.kt

Lines changed: 0 additions & 24 deletions
This file was deleted.

ComposePreferences/src/main/java/com/strabled/composepreferences/BasicPreference.kt

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import androidx.compose.runtime.Composable
1616
import androidx.compose.runtime.CompositionLocalProvider
1717
import androidx.compose.ui.Alignment
1818
import androidx.compose.ui.Modifier
19+
import androidx.compose.ui.graphics.Color
1920
import androidx.compose.ui.text.TextStyle
2021
import androidx.compose.ui.unit.dp
2122

@@ -75,22 +76,34 @@ fun BasicPreference(
7576
}
7677

7778
Column(verticalArrangement = Arrangement.Center, modifier = Modifier.weight(1f)) {
78-
ProvideTextStyle(PreferenceTheme.typography.titleStyle.applyAlpha(if (enabled || !darkenOnDisable) AlphaHigh else AlphaDisabled)) {
79+
ProvideTextStyle(
80+
PreferenceTheme.typography.titleStyle.applyColor(
81+
PreferenceTheme.colorScheme.titleColor,
82+
if (enabled || !darkenOnDisable) AlphaHigh else AlphaDisabled
83+
)
84+
) {
7985
text()
8086
}
8187
CompositionLocalProvider(
82-
LocalTextStyle provides PreferenceTheme.typography.summaryStyle.applyAlpha(if (enabled || !darkenOnDisable) AlphaMedium else AlphaDisabled),
83-
LocalContentColor provides PreferenceTheme.colorScheme.summaryColor,
88+
LocalTextStyle provides PreferenceTheme.typography.summaryStyle.applyColor(
89+
PreferenceTheme.colorScheme.summaryColor,
90+
if (enabled || !darkenOnDisable) AlphaMedium else AlphaDisabled
91+
),
8492
content = { secondaryText?.invoke() }
8593
)
8694
}
87-
ProvideTextStyle(PreferenceTheme.typography.trailingContentStyle.applyAlpha(if (enabled || !darkenOnDisable) AlphaHigh else AlphaDisabled)) {
95+
ProvideTextStyle(
96+
PreferenceTheme.typography.trailingContentStyle.applyColor(
97+
PreferenceTheme.colorScheme.trailingContentColor,
98+
if (enabled || !darkenOnDisable) AlphaHigh else AlphaDisabled
99+
)
100+
) {
88101
trailingContent()
89102
}
90103
}
91104
}
92105

93-
private fun TextStyle.applyAlpha(alpha: Float): TextStyle = copy(color = color.copy(alpha = alpha))
106+
private fun TextStyle.applyColor(color: Color, alpha: Float = 1f): TextStyle = copy(color = color.copy(alpha = alpha))
94107

95108
internal fun applyClickable(enabled: Boolean, onClick: () -> Unit): Modifier = if (enabled) Modifier.clickable(onClick = onClick) else Modifier
96109

ComposePreferences/src/main/java/com/strabled/composepreferences/PreferenceProviders.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ fun ProvideDataStoreManager(dataStoreManager: DataStoreManager = defaultDataStor
5252
* @see DataStoreManager.setPreferences
5353
*/
5454
@Composable
55-
fun setPreferences(preferences: Map<String, *>) {
55+
fun setPreferences(preferences: Map<String, Any>) {
5656
LocalDataStoreManager.current.setPreferences(preferences)
5757
}
5858

@@ -66,6 +66,6 @@ fun setPreferences(preferences: Map<String, *>) {
6666
* @see DataStoreManager.getPreference
6767
*/
6868
@Composable
69-
fun <T> getPreference(key: String): DataStoreManager.Preference<T> {
69+
fun <T: Any> getPreference(key: String): DataStoreManager.Preference<T> {
7070
return LocalDataStoreManager.current.getPreference<T>(key)
7171
}

ComposePreferences/src/main/java/com/strabled/composepreferences/preferences/BottomSheetListPreference.kt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ import com.strabled.composepreferences.utilis.*
4343
*/
4444
@OptIn(ExperimentalMaterial3Api::class)
4545
@Composable
46-
fun <T> BottomSheetListPreference(
46+
fun <T: Any> BottomSheetListPreference(
4747
preference: DataStoreManager.Preference<T>,
4848
title: String,
4949
modifier: Modifier = Modifier,
@@ -60,7 +60,7 @@ fun <T> BottomSheetListPreference(
6060
) {
6161
require(!useSelectedInSummary || summary != null) { "When useSelectedInSummary is true, summary must be provided." }
6262
val preferenceData by preference
63-
val preferenceValue by preferenceData.flow.collectAsState()
63+
val preferenceValue by preferenceData.collectAsState()
6464

6565
var showBottomSheet by rememberSaveable { mutableStateOf(false) }
6666
val bottomSheetState = rememberModalBottomSheetState(skipPartiallyExpanded = false)
@@ -75,7 +75,7 @@ fun <T> BottomSheetListPreference(
7575

7676
fun edit(value: T) {
7777
try {
78-
preferenceData.set(value)
78+
preference.set(value)
7979
showBottomSheet = false
8080
onValueChange(value)
8181
} catch (e: Exception) {
@@ -148,7 +148,7 @@ fun <T> BottomSheetListPreference(
148148
* @param trailingContent A composable function to display trailing content for the [preference item][PreferenceItem].
149149
*/
150150
@Composable
151-
fun <T> BottomSheetListPreference(
151+
fun <T: Any> BottomSheetListPreference(
152152
preference: DataStoreManager.Preference<T>,
153153
title: String,
154154
modifier: Modifier = Modifier,

ComposePreferences/src/main/java/com/strabled/composepreferences/preferences/CheckBoxPreference.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,12 +48,12 @@ fun CheckBoxPreference(
4848
leadingIcon: @Composable (() -> Unit)? = null,
4949
onValueChange: (Boolean) -> Unit = {}
5050
) {
51-
val preferenceData: DataStoreManager.PreferenceData<Boolean> by preference
52-
val preferenceValue by preferenceData.flow.collectAsState()
51+
val preferenceData by preference
52+
val preferenceValue by preferenceData.collectAsState()
5353

5454
fun edit(newValue: Boolean) {
5555
try {
56-
preferenceData.set(newValue)
56+
preference.set(newValue)
5757
onValueChange(newValue)
5858
} catch (e: Exception) {
5959
Log.e("CheckBoxPreference", "Could not write preference $preference to database.", e)

ComposePreferences/src/main/java/com/strabled/composepreferences/preferences/ColorPickerPreference.kt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -109,14 +109,14 @@ internal fun IntColorPickerDialog(
109109
) {
110110
require(!useSelectedInSummary || summary != null) { "When useSeletcedInSummary is true, summary must be provided." }
111111
val preferenceDate by preference
112-
val preferenceValue by preferenceDate.flow.collectAsState()
112+
val preferenceValue by preferenceDate.collectAsState()
113113
val color = Color(preferenceValue)
114114

115115
var showDialog by rememberSaveable { mutableStateOf(false) }
116116

117117
fun edit(color: Color) {
118118
try {
119-
preferenceDate.set(color.toArgb())
119+
preference.set(color.toArgb())
120120
showDialog = false
121121
onColorSelected(color)
122122
} catch (e: Exception) {
@@ -177,14 +177,14 @@ internal fun LongColorPickerDialog(
177177
) {
178178
require(!useSelectedInSummary || summary != null) { "When useSeletcedInSummary is true, summary must be provided." }
179179
val preferenceDate by preference
180-
val preferenceValue by preferenceDate.flow.collectAsState()
180+
val preferenceValue by preferenceDate.collectAsState()
181181
val color = Color(preferenceValue)
182182

183183
var showDialog by rememberSaveable { mutableStateOf(false) }
184184

185185
fun edit(color: Color) {
186186
try {
187-
preferenceDate.set(color.toArgb().toLong())
187+
preference.set(color.toArgb().toLong())
188188
showDialog = false
189189
onColorSelected(color)
190190
} catch (e: Exception) {

ComposePreferences/src/main/java/com/strabled/composepreferences/preferences/DialogListPreference.kt

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ import com.strabled.composepreferences.utilis.*
5656
* @see androidx.compose.foundation.layout.Row
5757
*/
5858
@Composable
59-
fun <T> DialogListPreference(
59+
fun <T: Any> DialogListPreference(
6060
preference: DataStoreManager.Preference<T>,
6161
title: String,
6262
modifier: Modifier = Modifier,
@@ -71,14 +71,14 @@ fun <T> DialogListPreference(
7171
trailingContent: @Composable () -> Unit = {}
7272
) {
7373
require(!useSelectedInSummary || summary != null) { "When useSelectedInSummary is true, summary must be provided." }
74-
val preferenceData: DataStoreManager.PreferenceData<T> by preference
75-
val preferenceValue by preferenceData.flow.collectAsState()
74+
val preferenceData by preference
75+
val preferenceValue by preferenceData.collectAsState()
7676

7777
var showDialog by rememberSaveable { mutableStateOf(false) }
7878

7979
fun edit(value: T) {
8080
try {
81-
preferenceData.set(value)
81+
preference.set(value)
8282
showDialog = false
8383
onItemSelected(value)
8484
} catch (e: Exception) {
@@ -178,7 +178,7 @@ fun <T> DialogListPreference(
178178
* @see androidx.compose.material3.RadioButton
179179
*/
180180
@Composable
181-
fun <T> DialogListPreference(
181+
fun <T: Any> DialogListPreference(
182182
preference: DataStoreManager.Preference<T>,
183183
title: String,
184184
modifier: Modifier = Modifier,

ComposePreferences/src/main/java/com/strabled/composepreferences/preferences/DropDownListPreference.kt

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ import com.strabled.composepreferences.utilis.DataStoreManager
6666
* ```
6767
*/
6868
@Composable
69-
fun <T> DropDownListPreference(
69+
fun <T: Any> DropDownListPreference(
7070
preference: DataStoreManager.Preference<T>,
7171
title: String,
7272
modifier: Modifier = Modifier,
@@ -80,14 +80,14 @@ fun <T> DropDownListPreference(
8080
trailingContent: @Composable () -> Unit = {}
8181
) {
8282
require(!useSelectedInSummary || summary != null) { "Summary must be provided when useSelectedInSummary is true" }
83-
val preferenceData: DataStoreManager.PreferenceData<T> by preference
84-
val preferenceValue by preferenceData.flow.collectAsState()
83+
val preferenceData by preference
84+
val preferenceValue by preferenceData.collectAsState()
8585

8686
var expanded by rememberSaveable { mutableStateOf(false) }
8787

8888
fun edit(value: T) {
8989
try {
90-
preferenceData.set(value)
90+
preference.set(value)
9191
expanded = false
9292
onItemSelected(value)
9393
} catch (e: Exception) {
@@ -180,7 +180,7 @@ fun <T> DropDownListPreference(
180180
* ```
181181
*/
182182
@Composable
183-
fun <T> DropDownListPreference(
183+
fun <T: Any> DropDownListPreference(
184184
preference: DataStoreManager.Preference<T>,
185185
title: String,
186186
modifier: Modifier = Modifier,

0 commit comments

Comments
 (0)