From 1f9198a1c05223edc7abb095f483d02cda5f1122 Mon Sep 17 00:00:00 2001 From: Allan Wang Date: Mon, 1 Jul 2019 14:39:46 -0700 Subject: Add in memory kpref variant --- .../main/kotlin/ca/allanwang/kau/kpref/KPref.kt | 10 ++- .../kotlin/ca/allanwang/kau/kpref/KPrefBuilder.kt | 75 +++++++++++++++++ .../kotlin/ca/allanwang/kau/kpref/KPrefDelegate.kt | 96 ++++++++++++++-------- .../ca/allanwang/kau/kpref/KPrefSingleDelegate.kt | 58 +++++++++++-- 4 files changed, 194 insertions(+), 45 deletions(-) create mode 100644 core/src/main/kotlin/ca/allanwang/kau/kpref/KPrefBuilder.kt (limited to 'core/src/main/kotlin') diff --git a/core/src/main/kotlin/ca/allanwang/kau/kpref/KPref.kt b/core/src/main/kotlin/ca/allanwang/kau/kpref/KPref.kt index 7a6330f..fc7a76a 100644 --- a/core/src/main/kotlin/ca/allanwang/kau/kpref/KPref.kt +++ b/core/src/main/kotlin/ca/allanwang/kau/kpref/KPref.kt @@ -36,14 +36,18 @@ import ca.allanwang.kau.logging.KL * You may optionally override [deleteKeys]. This will be called on initialization * And delete all keys returned from that method */ -open class KPref { +open class KPref(builder: KPrefBuilder = KPrefBuilderAndroid) : KPrefBuilder by builder { lateinit var PREFERENCE_NAME: String lateinit var sp: SharedPreferences - fun initialize(c: Context, preferenceName: String) { + fun initialize( + c: Context, + preferenceName: String, + sharedPrefs: SharedPreferences = c.applicationContext.getSharedPreferences(preferenceName, Context.MODE_PRIVATE) + ) { PREFERENCE_NAME = preferenceName - sp = c.applicationContext.getSharedPreferences(preferenceName, Context.MODE_PRIVATE) + sp = sharedPrefs KL.d { "Shared Preference $preferenceName has been initialized" } val toDelete = deleteKeys() if (toDelete.isNotEmpty()) { diff --git a/core/src/main/kotlin/ca/allanwang/kau/kpref/KPrefBuilder.kt b/core/src/main/kotlin/ca/allanwang/kau/kpref/KPrefBuilder.kt new file mode 100644 index 0000000..4dd3012 --- /dev/null +++ b/core/src/main/kotlin/ca/allanwang/kau/kpref/KPrefBuilder.kt @@ -0,0 +1,75 @@ +package ca.allanwang.kau.kpref + +interface KPrefBuilder { + fun KPref.kpref(key: String, fallback: Boolean, postSetter: (value: Boolean) -> Unit = {}): KPrefDelegate + + fun KPref.kpref(key: String, fallback: Float, postSetter: (value: Float) -> Unit = {}): KPrefDelegate + + @Deprecated( + "Double is not supported in SharedPreferences; cast to float yourself", + ReplaceWith("kpref(key, fallback.toFloat(), postSetter)"), + DeprecationLevel.WARNING + ) + fun KPref.kpref(key: String, fallback: Double, postSetter: (value: Float) -> Unit = {}): KPrefDelegate = + kpref(key, fallback.toFloat(), postSetter) + + fun KPref.kpref(key: String, fallback: Int, postSetter: (value: Int) -> Unit = {}): KPrefDelegate + + fun KPref.kpref(key: String, fallback: Long, postSetter: (value: Long) -> Unit = {}): KPrefDelegate + + fun KPref.kpref( + key: String, + fallback: Set, + postSetter: (value: Set) -> Unit = {} + ): KPrefDelegate> + + fun KPref.kpref(key: String, fallback: String, postSetter: (value: String) -> Unit = {}): KPrefDelegate + + fun KPref.kprefSingle(key: String): KPrefSingleDelegate +} + +object KPrefBuilderAndroid : KPrefBuilder { + + override fun KPref.kpref(key: String, fallback: Boolean, postSetter: (value: Boolean) -> Unit) = + KPrefDelegateAndroid(key, fallback, this, KPrefBooleanTransaction, postSetter) + + override fun KPref.kpref(key: String, fallback: Float, postSetter: (value: Float) -> Unit) = + KPrefDelegateAndroid(key, fallback, this, KPrefFloatTransaction, postSetter) + + override fun KPref.kpref(key: String, fallback: Int, postSetter: (value: Int) -> Unit) = + KPrefDelegateAndroid(key, fallback, this, KPrefIntTransaction, postSetter) + + override fun KPref.kpref(key: String, fallback: Long, postSetter: (value: Long) -> Unit) = + KPrefDelegateAndroid(key, fallback, this, KPrefLongTransaction, postSetter) + + override fun KPref.kpref(key: String, fallback: Set, postSetter: (value: Set) -> Unit) = + KPrefDelegateAndroid(key, fallback, this, KPrefSetTransaction, postSetter) + + override fun KPref.kpref(key: String, fallback: String, postSetter: (value: String) -> Unit) = + KPrefDelegateAndroid(key, fallback, this, KPrefStringTransaction, postSetter) + + override fun KPref.kprefSingle(key: String) = KPrefSingleDelegateAndroid(key, this) +} + +object KPrefBuilderInMemory : KPrefBuilder { + + override fun KPref.kpref(key: String, fallback: Boolean, postSetter: (value: Boolean) -> Unit) = + KPrefDelegateInMemory(key, fallback, this, postSetter) + + override fun KPref.kpref(key: String, fallback: Float, postSetter: (value: Float) -> Unit) = + KPrefDelegateInMemory(key, fallback, this, postSetter) + + override fun KPref.kpref(key: String, fallback: Int, postSetter: (value: Int) -> Unit) = + KPrefDelegateInMemory(key, fallback, this, postSetter) + + override fun KPref.kpref(key: String, fallback: Long, postSetter: (value: Long) -> Unit) = + KPrefDelegateInMemory(key, fallback, this, postSetter) + + override fun KPref.kpref(key: String, fallback: Set, postSetter: (value: Set) -> Unit) = + KPrefDelegateInMemory(key, fallback, this, postSetter) + + override fun KPref.kpref(key: String, fallback: String, postSetter: (value: String) -> Unit) = + KPrefDelegateInMemory(key, fallback, this, postSetter) + + override fun KPref.kprefSingle(key: String) = KPrefSingleDelegateInMemory(key, this) +} \ No newline at end of file diff --git a/core/src/main/kotlin/ca/allanwang/kau/kpref/KPrefDelegate.kt b/core/src/main/kotlin/ca/allanwang/kau/kpref/KPrefDelegate.kt index 9813f24..684b139 100644 --- a/core/src/main/kotlin/ca/allanwang/kau/kpref/KPrefDelegate.kt +++ b/core/src/main/kotlin/ca/allanwang/kau/kpref/KPrefDelegate.kt @@ -17,32 +17,6 @@ package ca.allanwang.kau.kpref import ca.allanwang.kau.kotlin.ILazyResettable -fun KPref.kpref(key: String, fallback: Boolean, postSetter: (value: Boolean) -> Unit = {}) = - KPrefDelegate(key, fallback, this, KPrefBooleanTransaction, postSetter) - -fun KPref.kpref(key: String, fallback: Float, postSetter: (value: Float) -> Unit = {}) = - KPrefDelegate(key, fallback, this, KPrefFloatTransaction, postSetter) - -@Deprecated( - "Double is not supported in SharedPreferences; cast to float yourself", - ReplaceWith("kpref(key, fallback.toFloat(), postSetter)"), - DeprecationLevel.WARNING -) -fun KPref.kpref(key: String, fallback: Double, postSetter: (value: Float) -> Unit = {}) = - kpref(key, fallback.toFloat(), postSetter) - -fun KPref.kpref(key: String, fallback: Int, postSetter: (value: Int) -> Unit = {}) = - KPrefDelegate(key, fallback, this, KPrefIntTransaction, postSetter) - -fun KPref.kpref(key: String, fallback: Long, postSetter: (value: Long) -> Unit = {}) = - KPrefDelegate(key, fallback, this, KPrefLongTransaction, postSetter) - -fun KPref.kpref(key: String, fallback: Set, postSetter: (value: Set) -> Unit = {}) = - KPrefDelegate(key, fallback, this, KPrefSetTransaction, postSetter) - -fun KPref.kpref(key: String, fallback: String, postSetter: (value: String) -> Unit = {}) = - KPrefDelegate(key, fallback, this, KPrefStringTransaction, postSetter) - /** * Created by Allan Wang on 2017-06-07. * @@ -50,13 +24,20 @@ fun KPref.kpref(key: String, fallback: String, postSetter: (value: String) -> Un * Contains a unique key for the shared preference as well as a nonnull fallback item * Also contains an optional mutable postSetter that will be called every time a new value is given */ -class KPrefDelegate internal constructor( + +interface KPrefDelegate : ILazyResettable { + operator fun setValue(any: Any, property: kotlin.reflect.KProperty<*>, t: T) +} + +class KPrefException(message: String) : IllegalAccessException(message) + +class KPrefDelegateAndroid internal constructor( private val key: String, private val fallback: T, private val pref: KPref, private val transaction: KPrefTransaction, private var postSetter: (value: T) -> Unit = {} -) : ILazyResettable { +) : KPrefDelegate { private object UNINITIALIZED @@ -67,28 +48,26 @@ class KPrefDelegate internal constructor( init { if (pref.prefMap.containsKey(key)) throw KPrefException("$key is already used elsewhere in preference ${pref.PREFERENCE_NAME}") - pref.prefMap[key] = this@KPrefDelegate + pref.prefMap[key] = this@KPrefDelegateAndroid } override fun invalidate() { _value = UNINITIALIZED } + @Suppress("UNCHECKED_CAST") override val value: T get() { val _v1 = _value if (_v1 !== UNINITIALIZED) - @Suppress("UNCHECKED_CAST") return _v1 as T return synchronized(lock) { val _v2 = _value if (_v2 !== UNINITIALIZED) { - @Suppress("UNCHECKED_CAST") _v2 as T } else { _value = transaction.get(pref.sp, key, fallback) - @Suppress("UNCHECKED_CAST") _value as T } } @@ -98,7 +77,7 @@ class KPrefDelegate internal constructor( override fun toString(): String = if (isInitialized()) value.toString() else "Lazy kPref $key not initialized yet." - operator fun setValue(any: Any, property: kotlin.reflect.KProperty<*>, t: T) { + override operator fun setValue(any: Any, property: kotlin.reflect.KProperty<*>, t: T) { _value = t val editor = pref.sp.edit() transaction.set(editor, key, t) @@ -107,4 +86,53 @@ class KPrefDelegate internal constructor( } } -class KPrefException(message: String) : IllegalAccessException(message) +class KPrefDelegateInMemory internal constructor( + private val key: String, + private val fallback: T, + private val pref: KPref, + private var postSetter: (value: T) -> Unit = {} +) : KPrefDelegate { + + private object UNINITIALIZED + + @Volatile + private var _value: Any? = UNINITIALIZED + private val lock = this + + init { + if (pref.prefMap.containsKey(key)) + throw KPrefException("$key is already used elsewhere in preference ${pref.PREFERENCE_NAME}") + pref.prefMap[key] = this + } + + override fun invalidate() { + // No op + } + + @Suppress("UNCHECKED_CAST") + override val value: T + get() { + val _v1 = _value + if (_v1 !== UNINITIALIZED) + return _v1 as T + + return synchronized(lock) { + val _v2 = _value + if (_v2 !== UNINITIALIZED) { + _v2 as T + } else { + _value = fallback + _value as T + } + } + } + + override fun isInitialized(): Boolean = _value !== UNINITIALIZED + + override fun toString(): String = if (isInitialized()) value.toString() else "Lazy kPref $key not initialized yet." + + override operator fun setValue(any: Any, property: kotlin.reflect.KProperty<*>, t: T) { + _value = t + postSetter(t) + } +} diff --git a/core/src/main/kotlin/ca/allanwang/kau/kpref/KPrefSingleDelegate.kt b/core/src/main/kotlin/ca/allanwang/kau/kpref/KPrefSingleDelegate.kt index 204e07e..ef59e78 100644 --- a/core/src/main/kotlin/ca/allanwang/kau/kpref/KPrefSingleDelegate.kt +++ b/core/src/main/kotlin/ca/allanwang/kau/kpref/KPrefSingleDelegate.kt @@ -17,8 +17,6 @@ package ca.allanwang.kau.kpref import ca.allanwang.kau.kotlin.ILazyResettable -fun KPref.kprefSingle(key: String) = KPrefSingleDelegate(key, this) - /** * Created by Allan Wang on 2017-06-07. * @@ -27,17 +25,21 @@ fun KPref.kprefSingle(key: String) = KPrefSingleDelegate(key, this) * All subsequent retrievals will be [false] * This is useful for one time toggles such as showcasing items */ -class KPrefSingleDelegate internal constructor(private val key: String, private val pref: KPref, lock: Any? = null) : - ILazyResettable { +interface KPrefSingleDelegate : ILazyResettable + +class KPrefSingleDelegateAndroid internal constructor( + private val key: String, + private val pref: KPref +) : KPrefSingleDelegate { @Volatile private var _value: Boolean? = null - private val lock = lock ?: this + private val lock = this init { if (pref.prefMap.containsKey(key)) throw KPrefException("$key is already used elsewhere in preference ${pref.PREFERENCE_NAME}") - pref.prefMap.put(key, this@KPrefSingleDelegate) + pref.prefMap[key] = this } override fun invalidate() { @@ -47,9 +49,9 @@ class KPrefSingleDelegate internal constructor(private val key: String, private override val value: Boolean get() { val _v1 = _value - if (_v1 != null) + if (_v1 != null) { return _v1 - + } return synchronized(lock) { val _v2 = _value if (_v2 != null) { @@ -69,3 +71,43 @@ class KPrefSingleDelegate internal constructor(private val key: String, private override fun toString(): String = if (isInitialized()) value.toString() else "Lazy kPref $key not initialized yet." } + +class KPrefSingleDelegateInMemory internal constructor( + private val key: String, + private val pref: KPref +) : KPrefSingleDelegate { + @Volatile + private var _value: Boolean? = null + private val lock = this + + init { + if (pref.prefMap.containsKey(key)) + throw KPrefException("$key is already used elsewhere in preference ${pref.PREFERENCE_NAME}") + pref.prefMap[key] = this + } + + override fun invalidate() { + // No op + } + + override val value: Boolean + get() { + val _v1 = _value + if (_v1 != null) { + return _v1 + } + return synchronized(lock) { + val _v2 = _value + if (_v2 != null) { + _v2 + } else { + _value = false + true + } + } + } + + override fun isInitialized(): Boolean = _value != null + + override fun toString(): String = if (isInitialized()) value.toString() else "Lazy kPref $key not initialized yet." +} \ No newline at end of file -- cgit v1.2.3