Skip to content

Commit

Permalink
Feat/nullable (#98)
Browse files Browse the repository at this point in the history
* feat: nullable preferences

* feat: nullable preferences

* fix: tests

---------

Co-authored-by: Alexander Karkossa <alexander.karkossa@dataport.de>
  • Loading branch information
kalinjul and ch4rl3x authored Sep 7, 2023
1 parent 69246c6 commit 7421a2c
Show file tree
Hide file tree
Showing 9 changed files with 252 additions and 62 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,31 +3,46 @@ package de.charlex.settings.datastore.encryption
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.stringPreferencesKey

internal data class EncryptedPreference<T> (
private data class EncryptedPreference<T> (
override val defaultValue: T,
override val preferenceKey: Preferences.Key<String>,
) : IDataStoreEncryptedPreference<T>

fun encryptedStringPreference(name: String, defaultValue: String): IDataStoreEncryptedPreference<String> =
EncryptedPreference(preferenceKey = stringPreferencesKey(name), defaultValue = defaultValue)
private inline fun <reified T> encryptedPreferenceImpl(name: String, defaultValue: T): IDataStoreEncryptedPreference<T> {
val key = when (T::class) {
String::class,
Int::class,
Double::class,
Boolean::class,
Float::class,
Long::class -> stringPreferencesKey(name)
else -> error("Invalid type for encryptedPreference: ${T::class}")
}
return EncryptedPreference(preferenceKey = key, defaultValue = defaultValue)
}

fun <T : Enum<T>> encryptedEnumPreference(name: String, defaultValue: T): IDataStoreEncryptedPreference<T> =
EncryptedPreference(preferenceKey = stringPreferencesKey(name), defaultValue = defaultValue)

fun encryptedBooleanPreference(name: String, defaultValue: Boolean): IDataStoreEncryptedPreference<Boolean> =
EncryptedPreference(preferenceKey = stringPreferencesKey(name), defaultValue = defaultValue)

fun encryptedIntPreference(name: String, defaultValue: Int): IDataStoreEncryptedPreference<Int> =
EncryptedPreference(preferenceKey = stringPreferencesKey(name), defaultValue = defaultValue)

fun encryptedFloatPreference(name: String, defaultValue: Float): IDataStoreEncryptedPreference<Float> =
EncryptedPreference(preferenceKey = stringPreferencesKey(name), defaultValue = defaultValue)

fun encryptedLongPreference(name: String, defaultValue: Long): IDataStoreEncryptedPreference<Long> =
EncryptedPreference(preferenceKey = stringPreferencesKey(name), defaultValue = defaultValue)

fun encryptedDoublePreference(name: String, defaultValue: Double): IDataStoreEncryptedPreference<Double> =
EncryptedPreference(preferenceKey = stringPreferencesKey(name), defaultValue = defaultValue)

fun encryptedStringSetPreference(name: String, defaultValue: Set<String>): IDataStoreEncryptedPreference<Set<String>> =
EncryptedPreference(preferenceKey = stringPreferencesKey(name), defaultValue = defaultValue)
fun encryptedStringPreference(name: String, defaultValue: String) = encryptedPreferenceImpl(name, defaultValue)
fun encryptedBooleanPreference(name: String, defaultValue: Boolean) = encryptedPreferenceImpl(name, defaultValue)
fun encryptedIntPreference(name: String, defaultValue: Int) = encryptedPreferenceImpl(name, defaultValue)
fun encryptedFloatPreference(name: String, defaultValue: Float) = encryptedPreferenceImpl(name, defaultValue)
fun encryptedLongPreference(name: String, defaultValue: Long) = encryptedPreferenceImpl(name, defaultValue)
fun encryptedDoublePreference(name: String, defaultValue: Double) = encryptedPreferenceImpl(name, defaultValue)
fun encryptedStringSetPreference(name: String, defaultValue: Set<String>) = encryptedPreferenceImpl(name, defaultValue)

@JvmName("encryptedStringNullablePreference")
fun encryptedStringPreference(name: String, defaultValue: String?) = encryptedPreferenceImpl(name, defaultValue)
@JvmName("encryptedBooleanNullablePreference")
fun encryptedBooleanPreference(name: String, defaultValue: Boolean?) = encryptedPreferenceImpl(name, defaultValue)
@JvmName("encryptedIntNullablePreference")
fun encryptedIntPreference(name: String, defaultValue: Int?) = encryptedPreferenceImpl(name, defaultValue)
@JvmName("encryptedFloatNullablePreference")
fun encryptedFloatPreference(name: String, defaultValue: Float?) = encryptedPreferenceImpl(name, defaultValue)
@JvmName("encryptedLongNullablePreference")
fun encryptedLongPreference(name: String, defaultValue: Long?) = encryptedPreferenceImpl(name, defaultValue)
@JvmName("encryptedDoubleNullablePreference")
fun encryptedDoublePreference(name: String, defaultValue: Double?) = encryptedPreferenceImpl(name, defaultValue)
@JvmName("encryptedStringSetNullablePreference")
fun encryptedStringSetPreference(name: String, defaultValue: Set<String>?) = encryptedPreferenceImpl(name, defaultValue)
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import de.charlex.settings.datastore.stringPreference
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.map
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,13 @@ object EncryptedPreferences {
val PreferenceLong = encryptedLongPreference("preference_long", 1L)
val PreferenceBoolean = encryptedBooleanPreference("preference_boolean", true)

val PreferenceIntNullable = encryptedIntPreference("preference_int_nullable", null)
val PreferenceStringNullable = encryptedStringPreference("preference_string_nullable", null)
val PreferenceFloatNullable = encryptedFloatPreference("preference_float_nullable", null)
val PreferenceDoubleNullable = encryptedDoublePreference("preference_double_nullable", null)
val PreferenceLongNullable = encryptedLongPreference("preference_long_nullable", null)
val PreferenceBooleanNullable = encryptedBooleanPreference("preference_boolean_nullable", null)

val PreferenceEnumStringKey = encryptedEnumPreference("key", StringKeyEnum.Value2)
val PreferenceEnumIntKey = encryptedEnumPreference("key", IntKeyEnum.Value2)
val PreferenceEnumOrdinalKey = encryptedEnumPreference("key", Enum.Value2)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -168,4 +168,58 @@ abstract class SettingsDataStoreEncryptionTest {

settings.get(EncryptedPreferences.PreferenceString).first()
}

@Test
fun test_Int_Nullable_Settings() = runBlocking {
settings.put(EncryptedPreferences.PreferenceIntNullable, EncryptedPreferences.PreferenceIntNullable.defaultValue)
Assert.assertNull(settings.get(EncryptedPreferences.PreferenceIntNullable).first())

settings.put(EncryptedPreferences.PreferenceIntNullable, 10)
Assert.assertEquals(10, settings.get(EncryptedPreferences.PreferenceIntNullable).first())
}

@Test
fun test_String_Nullable_Settings() = runBlocking {
settings.put(EncryptedPreferences.PreferenceStringNullable, EncryptedPreferences.PreferenceStringNullable.defaultValue)
Assert.assertNull(settings.get(EncryptedPreferences.PreferenceStringNullable).first())

settings.put(EncryptedPreferences.PreferenceStringNullable, "test")
Assert.assertEquals("test", settings.get(EncryptedPreferences.PreferenceStringNullable).first())
}

@Test
fun test_Float_Nullable_Settings() = runBlocking {
settings.put(EncryptedPreferences.PreferenceFloatNullable, EncryptedPreferences.PreferenceFloatNullable.defaultValue)
Assert.assertNull(settings.get(EncryptedPreferences.PreferenceFloatNullable).first())

settings.put(EncryptedPreferences.PreferenceFloat, 2.2f)
Assert.assertEquals(2.2f, settings.get(EncryptedPreferences.PreferenceFloat).first())
}

@Test
fun test_Double_Nullable_Settings() = runBlocking {
settings.put(EncryptedPreferences.PreferenceDoubleNullable, EncryptedPreferences.PreferenceDoubleNullable.defaultValue)
Assert.assertNull(settings.get(EncryptedPreferences.PreferenceDoubleNullable).first())

settings.put(EncryptedPreferences.PreferenceDoubleNullable, 2.2)
Assert.assertEquals(2.2, settings.get(EncryptedPreferences.PreferenceDoubleNullable).first()!!, 0.0)
}

@Test
fun test_Long_Nullable_Settings() = runBlocking {
settings.put(EncryptedPreferences.PreferenceLongNullable, EncryptedPreferences.PreferenceLongNullable.defaultValue)
Assert.assertNull(settings.get(EncryptedPreferences.PreferenceLongNullable).first())

settings.put(EncryptedPreferences.PreferenceLongNullable, 2L)
Assert.assertEquals(2L, settings.get(EncryptedPreferences.PreferenceLongNullable).first())
}

@Test
fun test_Boolean_Nullable_Settings() = runBlocking {
settings.put(EncryptedPreferences.PreferenceBooleanNullable, EncryptedPreferences.PreferenceBooleanNullable.defaultValue)
Assert.assertNull(settings.get(EncryptedPreferences.PreferenceBooleanNullable).first())

settings.put(EncryptedPreferences.PreferenceBooleanNullable, false)
Assert.assertEquals(false, settings.get(EncryptedPreferences.PreferenceBooleanNullable).first())
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package de.charlex.settings.datastore

import androidx.datastore.preferences.core.Preferences
import kotlin.reflect.KProperty
import kotlin.reflect.KCallable

interface IDataStorePreference<T> {
val preferenceKey: Preferences.Key<T>
Expand All @@ -11,5 +11,5 @@ interface IDataStorePreference<T> {
interface IDataStoreEnumPreference<T, U> {
val preferenceKey: Preferences.Key<U>
val defaultValue: T
val keyProperty: KProperty<U>
val keyProperty: KCallable<U>
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,34 @@ import androidx.datastore.preferences.core.intPreferencesKey
import androidx.datastore.preferences.core.longPreferencesKey
import androidx.datastore.preferences.core.stringPreferencesKey
import androidx.datastore.preferences.core.stringSetPreferencesKey
import kotlin.reflect.KCallable
import kotlin.reflect.KProperty

internal data class Preference<T> (
private data class Preference<T> (
override val preferenceKey: Preferences.Key<T>,
override val defaultValue: T,
) : IDataStorePreference<T>

internal data class EnumPreference<T, U> (
private data class EnumPreference<T, U> (
override val preferenceKey: Preferences.Key<U>,
override val defaultValue: T,
override val keyProperty: KProperty<U>,
override val keyProperty: KCallable<U>,
) : IDataStoreEnumPreference<T, U>

fun stringPreference(name: String, defaultValue: String): IDataStorePreference<String> =
Preference(preferenceKey = stringPreferencesKey(name), defaultValue = defaultValue)
@Suppress("UNCHECKED_CAST")
private inline fun <reified T> preferenceImpl(name: String, defaultValue: T): IDataStorePreference<T> {
val key: Preferences.Key<T> = when (T::class) {
String::class -> stringPreferencesKey(name) as Preferences.Key<T>
Int::class -> intPreferencesKey(name) as Preferences.Key<T>
Double::class -> doublePreferencesKey(name) as Preferences.Key<T>
Boolean::class -> booleanPreferencesKey(name) as Preferences.Key<T>
Float::class -> floatPreferencesKey(name) as Preferences.Key<T>
Long::class -> longPreferencesKey(name) as Preferences.Key<T>
Set::class -> stringSetPreferencesKey(name) as Preferences.Key<T>
else -> error("Invalid type for preference: ${T::class}")
}
return Preference(preferenceKey = key, defaultValue = defaultValue)
}

@Suppress("UNCHECKED_CAST")
fun <T : Enum<T>, U> enumPreference(name: String, defaultValue: T, keyProperty: KProperty<U>): IDataStoreEnumPreference<T, U> {
Expand All @@ -38,20 +51,25 @@ fun <T : Enum<T>, U> enumPreference(name: String, defaultValue: T, keyProperty:
return EnumPreference(preferenceKey = preferenceKey, defaultValue = defaultValue, keyProperty = keyProperty)
}

fun booleanPreference(name: String, defaultValue: Boolean): IDataStorePreference<Boolean> =
Preference(preferenceKey = booleanPreferencesKey(name), defaultValue = defaultValue)

fun intPreference(name: String, defaultValue: Int): IDataStorePreference<Int> =
Preference(preferenceKey = intPreferencesKey(name), defaultValue = defaultValue)

fun floatPreference(name: String, defaultValue: Float): IDataStorePreference<Float> =
Preference(preferenceKey = floatPreferencesKey(name), defaultValue = defaultValue)

fun longPreference(name: String, defaultValue: Long): IDataStorePreference<Long> =
Preference(preferenceKey = longPreferencesKey(name), defaultValue = defaultValue)

fun doublePreference(name: String, defaultValue: Double): IDataStorePreference<Double> =
Preference(preferenceKey = doublePreferencesKey(name), defaultValue = defaultValue)
fun stringPreference(name: String, defaultValue: String) = preferenceImpl(name, defaultValue)
fun booleanPreference(name: String, defaultValue: Boolean) = preferenceImpl(name, defaultValue)
fun intPreference(name: String, defaultValue: Int) = preferenceImpl(name, defaultValue)
fun floatPreference(name: String, defaultValue: Float) = preferenceImpl(name, defaultValue)
fun longPreference(name: String, defaultValue: Long) = preferenceImpl(name, defaultValue)
fun doublePreference(name: String, defaultValue: Double) = preferenceImpl(name, defaultValue)
fun stringSetPreference(name: String, defaultValue: Set<String>) = preferenceImpl(name, defaultValue)

fun stringSetPreference(name: String, defaultValue: Set<String>): IDataStorePreference<Set<String>> =
Preference(preferenceKey = stringSetPreferencesKey(name), defaultValue = defaultValue)
@JvmName("stringNullablePreference")
fun stringPreference(name: String, defaultValue: String?) = preferenceImpl(name, defaultValue)
@JvmName("booleanNullablePreference")
fun booleanPreference(name: String, defaultValue: Boolean?) = preferenceImpl(name, defaultValue)
@JvmName("intNullablePreference")
fun intPreference(name: String, defaultValue: Int?) = preferenceImpl(name, defaultValue)
@JvmName("floatNullablePreference")
fun floatPreference(name: String, defaultValue: Float?) = preferenceImpl(name, defaultValue)
@JvmName("longNullablePreference")
fun longPreference(name: String, defaultValue: Long?) = preferenceImpl(name, defaultValue)
@JvmName("doubleNullablePreference")
fun doublePreference(name: String, defaultValue: Double?) = preferenceImpl(name, defaultValue)
@JvmName("stringSetNullablePreference")
fun stringSetPreference(name: String, defaultValue: Set<String>?) = preferenceImpl(name, defaultValue)
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,20 @@ class SettingsDataStoreInMemoryImpl internal constructor(
override val security: Security
) : SettingsDataStore, SecurityProvider {

private val flows = mutableMapOf<Preferences.Key<*>, MutableStateFlow<Any>>()
private val flows = mutableMapOf<Preferences.Key<*>, MutableStateFlow<*>>()

override fun <T> get(key: IDataStorePreference<T>): Flow<T> {
val stateFlow = flows.getOrPut(key.preferenceKey, { MutableStateFlow(key.defaultValue as Any) }) as Flow<T>
val stateFlow = flows.getOrPut(key.preferenceKey, { MutableStateFlow(key.defaultValue as Any?) }) as Flow<T>
return stateFlow.map { it }
}

override suspend fun <T> put(key: IDataStorePreference<T>, value: T) {
val stateFlow = flows.getOrPut(key.preferenceKey, { MutableStateFlow(key.defaultValue as Any) }) as MutableStateFlow<T>
stateFlow.value = value
if (value != null) {
val stateFlow = flows.getOrPut(key.preferenceKey, { MutableStateFlow(key.defaultValue as Any?) }) as MutableStateFlow<T>
stateFlow.value = value
} else {
flows.remove(key.preferenceKey)
}
}

override suspend fun <T : Enum<T>, U> put(key: IDataStoreEnumPreference<T, U>, value: T) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,26 @@ object Preferences {

val PreferenceInt = intPreference("preference_int", 1)
val PreferenceString = stringPreference("preference_string", "default")

val PreferenceString_WithIntKey = stringPreference("preference_int", "default")
val PreferenceFloat = floatPreference("preference_float", 1.1f)
val PreferenceDouble = doublePreference("preference_double", 1.1)
val PreferenceLong = longPreference("preference_long", 1L)
val PreferenceBoolean = booleanPreference("preference_boolean", true)

val PreferenceStringSet = stringSetPreference("preference_stringset", setOf())

val PreferenceIntNullable = intPreference("preference_int_nullable", null)
val PreferenceStringNullable = stringPreference("preference_string_nullable", null)
val PreferenceFloatNullable = floatPreference("preference_float_nullable", null)
val PreferenceDoubleNullable = doublePreference("preference_double_nullable", null)
val PreferenceLongNullable = longPreference("preference_long_nullable", null)
val PreferenceBooleanNullable = booleanPreference("preference_boolean_nullable", null)
val PreferenceStringSetNullable = stringSetPreference("preference_stringset_nullable", null)

val PreferenceEnumStringKey = enumPreference("key", StringKeyEnum.Value2, StringKeyEnum::key)
val PreferenceEnumIntKey = enumPreference("key", IntKeyEnum.Value2, IntKeyEnum::key)
val PreferenceEnumOrdinalKey = enumPreference("key", Enum.Value2, Enum::ordinal)
val PreferenceEnumNameKey = enumPreference("key", Enum.Value2, Enum::name)
val PreferenceEnumOrdinalKey = enumPreference("key", TestEnum.Value2, TestEnum::ordinal)
val PreferenceEnumNameKey = enumPreference("key", TestEnum.Value2, TestEnum::name)
}

enum class StringKeyEnum(val key: String) {
Expand All @@ -30,7 +38,7 @@ enum class IntKeyEnum(val key: Int) {
Value3(2)
}

enum class Enum {
enum class TestEnum {
Value1,
Value2,
Value3
Expand Down
Loading

0 comments on commit 7421a2c

Please sign in to comment.