Skip to content

Commit

Permalink
Enhancement/kpref (#170)
Browse files Browse the repository at this point in the history
* Deprecate kpref double

* Update kpref tests and migration

* Fix kpref file

* Update changelog

* Replace changelog angle brackets
  • Loading branch information
AllanWang authored Oct 8, 2018
1 parent 81518f7 commit 1902007
Show file tree
Hide file tree
Showing 6 changed files with 137 additions and 48 deletions.
26 changes: 17 additions & 9 deletions core/src/androidTest/kotlin/ca/allanwang/kau/kpref/KPrefTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,40 +16,46 @@ import kotlin.test.assertTrue
*/
@RunWith(AndroidJUnit4::class)
@MediumTest
class ChangelogTest {
class KPrefTest {

lateinit var pref: TestPref

class TestPref : KPref() {

init {
initialize(InstrumentationRegistry.getTargetContext(), "kpref_test_${System.currentTimeMillis()}")
}

var one: Int by kpref("one", 1)
var one by kpref("one", 1)

var two by kpref("two", 2f)

var `true`: Boolean by kpref("true", true)
var `true` by kpref("true", true)

var hello: String by kpref("hello", "hello")
var hello by kpref("hello", "hello")

var set: StringSet by kpref("set", setOf("po", "ta", "to"))
var set by kpref("set", setOf("po", "ta", "to"))

val oneShot: Boolean by kprefSingle("asdf")
val oneShot by kprefSingle("asdf")
}

@Before
fun init() {
pref = TestPref()
pref.sp.edit().clear().commit()
}

@Test
fun getDefaults() {
assertEquals(1, pref.one)
assertEquals(2f, pref.two)
assertEquals(true, pref.`true`)
assertEquals("hello", pref.hello)
assertEquals(3, pref.set.size)
assertTrue(pref.set.contains("po"))
assertTrue(pref.set.contains("ta"))
assertTrue(pref.set.contains("to"))
assertEquals(0, pref.sp.all.size, "Defaults should not be set automatically")
}

@Test
Expand All @@ -60,19 +66,21 @@ class ChangelogTest {
pref.hello = "goodbye"
assertEquals("goodbye", pref.hello)
assertEquals(pref.hello, pref.sp.getString("hello", "hello"))
assertEquals(2, pref.sp.all.size)
}

@SuppressLint("CommitPrefEdits")
@Test
fun reset() {
pref.one = 2
assertEquals(2, pref.one)
assertEquals(6, pref.prefMap.size, "Prefmap does not have all elements")
pref.reset() //only invalidates our lazy delegate; doesn't change the actual pref
assertEquals(2, pref.one)
assertEquals(2, pref.one, "Kpref did not properly fetch from shared prefs")
pref.sp.edit().putInt("one", -1).commit()
assertEquals(2, pref.one) //our lazy delegate still retains the old value
assertEquals(2, pref.one, "Lazy kpref should still retain old value")
pref.reset()
assertEquals(-1, pref.one) //back in sync with sp
assertEquals(-1, pref.one, "Kpref did not refetch from shared prefs upon reset")
}


Expand Down
7 changes: 6 additions & 1 deletion core/src/main/kotlin/ca/allanwang/kau/email/EmailBuilder.kt
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,14 @@ class EmailBuilder(val email: String, val subject: String) {
if (appInfo) {
try {
val appInfo = context.packageManager.getPackageInfo(context.packageName, 0)
val versionCode = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
appInfo.longVersionCode.toString()
} else {
appInfo.versionCode.toString()
}
emailBuilder.append("\nApp: ").append(context.packageName)
.append("\nApp Version Name: ").append(appInfo.versionName)
.append("\nApp Version Code: ").append(appInfo.versionCode).append("\n")
.append("\nApp Version Code: ").append(versionCode).append("\n")
} catch (e: PackageManager.NameNotFoundException) {
KL.e { "EmailBuilder packageInfo not found" }
}
Expand Down
69 changes: 34 additions & 35 deletions core/src/main/kotlin/ca/allanwang/kau/kpref/KPrefDelegate.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,29 @@ 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, postSetter)
fun KPref.kpref(key: String, fallback: Double, postSetter: (value: Float) -> Unit = {}) = KPrefDelegate(key, fallback.toFloat(), this, postSetter)
fun KPref.kpref(key: String, fallback: Float, postSetter: (value: Float) -> Unit = {}) = KPrefDelegate(key, fallback, this, postSetter)
fun KPref.kpref(key: String, fallback: Int, postSetter: (value: Int) -> Unit = {}) = KPrefDelegate(key, fallback, this, postSetter)
fun KPref.kpref(key: String, fallback: Long, postSetter: (value: Long) -> Unit = {}) = KPrefDelegate(key, fallback, this, postSetter)
fun KPref.kpref(key: String, fallback: Set<String>, postSetter: (value: Set<String>) -> Unit = {}) = KPrefDelegate(key, StringSet(fallback), this, postSetter)
fun KPref.kpref(key: String, fallback: String, postSetter: (value: String) -> Unit = {}) = KPrefDelegate(key, fallback, this, postSetter)
fun KPref.kpref(key: String, fallback: Boolean, postSetter: (value: Boolean) -> Unit = {}) =
KPrefDelegate(key, fallback, this, KPrefBooleanTransaction, postSetter)

class StringSet(set: Collection<String>) : LinkedHashSet<String>(set)
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<String>, postSetter: (value: Set<String>) -> 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.
Expand All @@ -20,20 +34,24 @@ class StringSet(set: Collection<String>) : LinkedHashSet<String>(set)
* 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<T : Any> internal constructor(
private val key: String, private val fallback: T, private val pref: KPref, private var postSetter: (value: T) -> Unit = {}, lock: Any? = null
class KPrefDelegate<T> internal constructor(
private val key: String,
private val fallback: T,
private val pref: KPref,
private val transaction: KPrefTransaction<T>,
private var postSetter: (value: T) -> Unit = {}
) : ILazyResettable<T> {

private object UNINITIALIZED

@Volatile
private var _value: Any = UNINITIALIZED
private val lock = lock ?: this
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.put(key, this@KPrefDelegate)
pref.prefMap[key] = this@KPrefDelegate
}

override fun invalidate() {
Expand All @@ -53,15 +71,7 @@ class KPrefDelegate<T : Any> internal constructor(
@Suppress("UNCHECKED_CAST")
_v2 as T
} else {
_value = when (fallback) {
is Boolean -> pref.sp.getBoolean(key, fallback)
is Float -> pref.sp.getFloat(key, fallback)
is Int -> pref.sp.getInt(key, fallback)
is Long -> pref.sp.getLong(key, fallback)
is StringSet -> StringSet(pref.sp.getStringSet(key, fallback))
is String -> pref.sp.getString(key, fallback)
else -> throw KPrefException(fallback)
}
_value = transaction.get(pref.sp, key, fallback)
@Suppress("UNCHECKED_CAST")
_value as T
}
Expand All @@ -75,21 +85,10 @@ class KPrefDelegate<T : Any> internal constructor(
operator fun setValue(any: Any, property: kotlin.reflect.KProperty<*>, t: T) {
_value = t
val editor = pref.sp.edit()
when (t) {
is Boolean -> editor.putBoolean(key, t)
is Float -> editor.putFloat(key, t)
is Int -> editor.putInt(key, t)
is Long -> editor.putLong(key, t)
is StringSet -> editor.putStringSet(key, t)
is String -> editor.putString(key, t)
else -> throw KPrefException(t)
}
transaction.set(editor, key, t)
editor.apply()
postSetter(t)
}
}

class KPrefException(message: String) : IllegalAccessException(message) {
constructor(element: Any?) : this("Invalid type in pref cache: ${element?.javaClass?.simpleName
?: "null"}")
}
class KPrefException(message: String) : IllegalAccessException(message)
64 changes: 64 additions & 0 deletions core/src/main/kotlin/ca/allanwang/kau/kpref/KPrefTransaction.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package ca.allanwang.kau.kpref

import android.content.SharedPreferences

internal interface KPrefTransaction<T> {
fun get(prefs: SharedPreferences, key: String, fallback: T): T
fun set(editor: SharedPreferences.Editor, key: String, data: T)
}

internal object KPrefBooleanTransaction : KPrefTransaction<Boolean> {
override fun get(prefs: SharedPreferences, key: String, fallback: Boolean) =
prefs.getBoolean(key, fallback)

override fun set(editor: SharedPreferences.Editor, key: String, data: Boolean) {
editor.putBoolean(key, data)
}
}

internal object KPrefIntTransaction : KPrefTransaction<Int> {
override fun get(prefs: SharedPreferences, key: String, fallback: Int) =
prefs.getInt(key, fallback)

override fun set(editor: SharedPreferences.Editor, key: String, data: Int) {
editor.putInt(key, data)
}
}

internal object KPrefLongTransaction : KPrefTransaction<Long> {
override fun get(prefs: SharedPreferences, key: String, fallback: Long) =
prefs.getLong(key, fallback)

override fun set(editor: SharedPreferences.Editor, key: String, data: Long) {
editor.putLong(key, data)
}
}

internal object KPrefFloatTransaction : KPrefTransaction<Float> {
override fun get(prefs: SharedPreferences, key: String, fallback: Float) =
prefs.getFloat(key, fallback)

override fun set(editor: SharedPreferences.Editor, key: String, data: Float) {
editor.putFloat(key, data)
}
}

internal object KPrefStringTransaction : KPrefTransaction<String> {
override fun get(prefs: SharedPreferences, key: String, fallback: String) =
prefs.getString(key, fallback)

override fun set(editor: SharedPreferences.Editor, key: String, data: String) {
editor.putString(key, data)
}
}

internal object KPrefSetTransaction : KPrefTransaction<Set<String>> {
override fun get(prefs: SharedPreferences, key: String, fallback: Set<String>) =
prefs.getStringSet(key, fallback)!!

override fun set(editor: SharedPreferences.Editor, key: String, data: Set<String>) {
editor.putStringSet(key, data)
}
}


15 changes: 14 additions & 1 deletion docs/Migration.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,22 @@ Below are some highlights on major refactoring/breaking changes

Along with the update to support Android Studio 3.1, a lot of changes have occurred with other dependencies and with lint.

* Resource ids can be negatives due to the bit overflow. Instead, `INVALID_ID` has been introduced to signify an unset or invalid id.
## Invalid Resource Id Equality

Resource ids can be negatives due to the bit overflow.
Instead, `INVALID_ID` has been introduced to signify an unset or invalid id.
Methods such as `Context.string(id, fallback)` now check against `INVALID_ID` through equality rather than using an inequality to address this.

## Deprecate Kotterknife

Kotterknife has been deprecated in favour of `kotlin-android-extensions`.
See [official docs](https://kotlinlang.org/docs/tutorials/android-plugin.html#view-binding).

## Update KPref

KPref has been slightly refactored internally.
Preferences backed by `StringSet` can now go back to `Set<String>`

# v3.6.0

## startActivity
Expand Down
4 changes: 2 additions & 2 deletions sample/src/main/res/xml/kau_changelog.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@
<item text="Update everything to Android Studio 3.1" />
<item text="Fix new lint issues (see Migration for resource related methods)" />
<item text=":adapter: Add more IAdapter functions to help retrieve selections" />
<item text=":core: Add deprecation warning to bindView for fragment based extensions; use bindViewResettable instead" />
<item text=":core: Deprecate Kotterknife; use kotlin_android_extensions" />
<item text=":kpref-activity: Fix seekbar increment" />
<item text="" />
<item text=":core: Make KPref use Set&lt;String&gt; vs StringSet" />
<item text="" />

<version title="v3.7.1" />
Expand Down

0 comments on commit 1902007

Please sign in to comment.