diff options
author | Allan Wang <me@allanwang.ca> | 2020-02-23 15:28:20 -0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-02-23 15:28:20 -0800 |
commit | 3fa13a3a84d34fd0d96f26d6c5dea0e0671dd6c4 (patch) | |
tree | 4e3f13ac9eafdfd02f4f0f78e0d7575f8bea0bf5 | |
parent | 0e4e82933001ab749538109210cb0940ea912db0 (diff) | |
parent | 0e7e43f7f778a206b0b30c6997c7c36494e6c142 (diff) | |
download | kau-3fa13a3a84d34fd0d96f26d6c5dea0e0671dd6c4.tar.gz kau-3fa13a3a84d34fd0d96f26d6c5dea0e0671dd6c4.tar.bz2 kau-3fa13a3a84d34fd0d96f26d6c5dea0e0671dd6c4.zip |
Merge pull request #248 from AllanWang/kpref
Kpref
18 files changed, 388 insertions, 127 deletions
diff --git a/core/src/androidTest/kotlin/ca/allanwang/kau/Utils.kt b/core/src/androidTest/kotlin/ca/allanwang/kau/Utils.kt new file mode 100644 index 0000000..33252d6 --- /dev/null +++ b/core/src/androidTest/kotlin/ca/allanwang/kau/Utils.kt @@ -0,0 +1,7 @@ +package ca.allanwang.kau + +import android.content.Context +import androidx.test.platform.app.InstrumentationRegistry + +val context: Context + get() = InstrumentationRegistry.getInstrumentation().context
\ No newline at end of file diff --git a/core/src/androidTest/kotlin/ca/allanwang/kau/kpref/KPrefTest.kt b/core/src/androidTest/kotlin/ca/allanwang/kau/kpref/KPrefTest.kt index 04c6444..222f0f5 100644 --- a/core/src/androidTest/kotlin/ca/allanwang/kau/kpref/KPrefTest.kt +++ b/core/src/androidTest/kotlin/ca/allanwang/kau/kpref/KPrefTest.kt @@ -16,14 +16,14 @@ package ca.allanwang.kau.kpref import android.annotation.SuppressLint -import android.content.Context -import androidx.test.core.app.ApplicationProvider +import android.content.SharedPreferences import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.MediumTest -import kotlin.test.assertEquals +import ca.allanwang.kau.context import org.junit.Before import org.junit.Test import org.junit.runner.RunWith +import kotlin.test.assertEquals /** * Created by Allan Wang on 2017-08-01. @@ -33,13 +33,11 @@ import org.junit.runner.RunWith class KPrefTest { lateinit var androidPref: TestPref + lateinit var androidSp: SharedPreferences lateinit var memPref: TestPref - class TestPref(builder: KPrefBuilder) : KPref(builder) { - - init { - initialize(ApplicationProvider.getApplicationContext<Context>(), "kpref_test_${System.currentTimeMillis()}") - } + class TestPref(factory: KPrefFactory) : + KPref("kpref_test_${System.currentTimeMillis()}", factory) { var postSetterCount: Int = 0 @@ -60,9 +58,10 @@ class KPrefTest { @Before fun init() { - androidPref = TestPref(KPrefBuilderAndroid) - androidPref.sp.edit().clear().commit() - memPref = TestPref(KPrefBuilderInMemory) + androidPref = TestPref(KPrefFactoryAndroid(context)) + androidSp = (androidPref.builder as KPrefBuilderAndroid).sp + androidSp.edit().clear().commit() + memPref = TestPref(KPrefFactoryInMemory) } private fun pref(action: TestPref.() -> Unit) { @@ -70,7 +69,11 @@ class KPrefTest { memPref.action() } - private fun <T> assertPrefEquals(expected: T, actual: TestPref.() -> T, message: String? = null) { + private fun <T> assertPrefEquals( + expected: T, + actual: TestPref.() -> T, + message: String? = null + ) { assertEquals(expected, androidPref.actual(), "Android KPrefs: $message") assertEquals(expected, memPref.actual(), "In Mem KPrefs: $message") } @@ -83,7 +86,7 @@ class KPrefTest { assertPrefEquals("hello", { hello }) assertPrefEquals(3, { set.size }) assertPrefEquals(setOf("po", "ta", "to"), { set }) - assertEquals(0, androidPref.sp.all.size, "Defaults should not be set automatically") + assertEquals(0, androidSp.all.size, "Defaults should not be set automatically") } @Test @@ -93,8 +96,8 @@ class KPrefTest { assertPrefEquals(2, { one }) pref { hello = "goodbye" } assertPrefEquals("goodbye", { hello }) - assertEquals(androidPref.hello, androidPref.sp.getString("hello", "badfallback")) - assertEquals(2, androidPref.sp.all.size) + assertEquals(androidPref.hello, androidSp.getString("hello", "badfallback")) + assertEquals(2, androidSp.all.size) } @SuppressLint("CommitPrefEdits") @@ -104,9 +107,10 @@ class KPrefTest { assertPrefEquals(2, { one }) assertPrefEquals(6, { prefMap.size }, "Prefmap does not have all elements") pref { reset() } // only invalidates our lazy delegate; doesn't change the actual pref - assertPrefEquals(2, { one }, "Kpref did not properly fetch from shared prefs") + assertEquals(1, memPref.one, "Memory Kpref did not invalidate value") + assertEquals(2, androidPref.one, "Android Kpref did not properly fetch from shared prefs") // Android pref only - androidPref.sp.edit().putInt("one", -1).commit() + androidSp.edit().putInt("one", -1).commit() assertEquals(2, androidPref.one, "Lazy kpref should still retain old value") androidPref.reset() assertEquals(-1, androidPref.one, "Kpref did not refetch from shared prefs upon reset") 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 fc7a76a..7f75370 100644 --- a/core/src/main/kotlin/ca/allanwang/kau/kpref/KPref.kt +++ b/core/src/main/kotlin/ca/allanwang/kau/kpref/KPref.kt @@ -15,10 +15,7 @@ */ package ca.allanwang.kau.kpref -import android.content.Context -import android.content.SharedPreferences import ca.allanwang.kau.kotlin.ILazyResettable -import ca.allanwang.kau.logging.KL /** * Created by Allan Wang on 2017-06-07. @@ -32,38 +29,36 @@ import ca.allanwang.kau.logging.KL * Furthermore, all kprefs are held in the [prefMap], * so if you wish to reset a preference, you must also invalidate the kpref * from that map - * - * You may optionally override [deleteKeys]. This will be called on initialization - * And delete all keys returned from that method */ -open class KPref(builder: KPrefBuilder = KPrefBuilderAndroid) : KPrefBuilder by builder { - - lateinit var PREFERENCE_NAME: String - lateinit var sp: SharedPreferences +open class KPref private constructor( + val preferenceName: String, + val builder: KPrefBuilder +) : KPrefBuilder by builder { - fun initialize( - c: Context, - preferenceName: String, - sharedPrefs: SharedPreferences = c.applicationContext.getSharedPreferences(preferenceName, Context.MODE_PRIVATE) - ) { - PREFERENCE_NAME = preferenceName - sp = sharedPrefs - KL.d { "Shared Preference $preferenceName has been initialized" } - val toDelete = deleteKeys() - if (toDelete.isNotEmpty()) { - val edit = sp.edit() - toDelete.forEach { edit.remove(it) } - edit.apply() - } - } + constructor(preferenceName: String, factory: KPrefFactory) : this( + preferenceName, + factory.createBuilder(preferenceName) + ) internal val prefMap: MutableMap<String, ILazyResettable<*>> = mutableMapOf() + fun add(entry: KPrefDelegate<*>) { + if (prefMap.containsKey(entry.key)) + throw KPrefException("${entry.key} is already used elsewhere in preference $preferenceName") + prefMap[entry.key] = entry + } + fun reset() { prefMap.values.forEach { it.invalidate() } } operator fun get(key: String): ILazyResettable<*>? = prefMap[key] - open fun deleteKeys(): Array<String> = arrayOf() + /** + * Exposed key deletion function from builder. + * To avoid recursion, this type uses vararg + */ + fun deleteKeys(vararg keys: String) { + deleteKeys(keys) + } } diff --git a/core/src/main/kotlin/ca/allanwang/kau/kpref/KPrefBuilder.kt b/core/src/main/kotlin/ca/allanwang/kau/kpref/KPrefBuilder.kt index 8f6d0c5..7a35ac2 100644 --- a/core/src/main/kotlin/ca/allanwang/kau/kpref/KPrefBuilder.kt +++ b/core/src/main/kotlin/ca/allanwang/kau/kpref/KPrefBuilder.kt @@ -15,22 +15,44 @@ */ package ca.allanwang.kau.kpref +import android.content.SharedPreferences + interface KPrefBuilder { - fun KPref.kpref(key: String, fallback: Boolean, postSetter: (value: Boolean) -> Unit = {}): KPrefDelegate<Boolean> + fun KPref.kpref( + key: String, + fallback: Boolean, + postSetter: (value: Boolean) -> Unit = {} + ): KPrefDelegate<Boolean> - fun KPref.kpref(key: String, fallback: Float, postSetter: (value: Float) -> Unit = {}): KPrefDelegate<Float> + fun KPref.kpref( + key: String, + fallback: Float, + postSetter: (value: Float) -> Unit = {} + ): KPrefDelegate<Float> @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<Float> = + fun KPref.kpref( + key: String, + fallback: Double, + postSetter: (value: Float) -> Unit = {} + ): KPrefDelegate<Float> = kpref(key, fallback.toFloat(), postSetter) - fun KPref.kpref(key: String, fallback: Int, postSetter: (value: Int) -> Unit = {}): KPrefDelegate<Int> + fun KPref.kpref( + key: String, + fallback: Int, + postSetter: (value: Int) -> Unit = {} + ): KPrefDelegate<Int> - fun KPref.kpref(key: String, fallback: Long, postSetter: (value: Long) -> Unit = {}): KPrefDelegate<Long> + fun KPref.kpref( + key: String, + fallback: Long, + postSetter: (value: Long) -> Unit = {} + ): KPrefDelegate<Long> fun KPref.kpref( key: String, @@ -38,32 +60,97 @@ interface KPrefBuilder { postSetter: (value: Set<String>) -> Unit = {} ): KPrefDelegate<Set<String>> - fun KPref.kpref(key: String, fallback: String, postSetter: (value: String) -> Unit = {}): KPrefDelegate<String> + fun KPref.kpref( + key: String, + fallback: String, + postSetter: (value: String) -> Unit = {} + ): KPrefDelegate<String> fun KPref.kprefSingle(key: String): KPrefSingleDelegate + + /** + * Remove keys from pref so they revert to the default + */ + fun KPref.deleteKeys(keys: Array<out String>) } -object KPrefBuilderAndroid : KPrefBuilder { +class KPrefBuilderAndroid(val sp: SharedPreferences) : KPrefBuilder { override fun KPref.kpref(key: String, fallback: Boolean, postSetter: (value: Boolean) -> Unit) = - KPrefDelegateAndroid(key, fallback, this, KPrefBooleanTransaction, postSetter) + KPrefDelegateAndroid( + key, + fallback, + this, + this@KPrefBuilderAndroid, + KPrefBooleanTransaction, + postSetter + ) override fun KPref.kpref(key: String, fallback: Float, postSetter: (value: Float) -> Unit) = - KPrefDelegateAndroid(key, fallback, this, KPrefFloatTransaction, postSetter) + KPrefDelegateAndroid( + key, + fallback, + this, + this@KPrefBuilderAndroid, + KPrefFloatTransaction, + postSetter + ) override fun KPref.kpref(key: String, fallback: Int, postSetter: (value: Int) -> Unit) = - KPrefDelegateAndroid(key, fallback, this, KPrefIntTransaction, postSetter) + KPrefDelegateAndroid( + key, + fallback, + this, + this@KPrefBuilderAndroid, + 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<String>, postSetter: (value: Set<String>) -> Unit) = - KPrefDelegateAndroid(key, fallback, this, KPrefSetTransaction, postSetter) + KPrefDelegateAndroid( + key, + fallback, + this, + this@KPrefBuilderAndroid, + KPrefLongTransaction, + postSetter + ) + + override fun KPref.kpref( + key: String, + fallback: Set<String>, + postSetter: (value: Set<String>) -> Unit + ) = + KPrefDelegateAndroid( + key, + fallback, + this, + this@KPrefBuilderAndroid, + 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) + KPrefDelegateAndroid( + key, + fallback, + this, + this@KPrefBuilderAndroid, + KPrefStringTransaction, + postSetter + ) + + override fun KPref.kprefSingle(key: String) = + KPrefSingleDelegateAndroid(key, this, this@KPrefBuilderAndroid) + + override fun KPref.deleteKeys(keys: Array<out String>) { + // Remove pref listing + sp.edit().apply { + keys.forEach { remove(it) } + }.apply() + // Clear cached values + keys.forEach { prefMap[it]?.invalidate() } + } } object KPrefBuilderInMemory : KPrefBuilder { @@ -80,11 +167,20 @@ object KPrefBuilderInMemory : KPrefBuilder { 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<String>, postSetter: (value: Set<String>) -> Unit) = + override fun KPref.kpref( + key: String, + fallback: Set<String>, + postSetter: (value: Set<String>) -> 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) + + override fun KPref.deleteKeys(keys: Array<out String>) { + // Clear cached values + keys.forEach { prefMap[it]?.invalidate() } + } } 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 684b139..c17d3df 100644 --- a/core/src/main/kotlin/ca/allanwang/kau/kpref/KPrefDelegate.kt +++ b/core/src/main/kotlin/ca/allanwang/kau/kpref/KPrefDelegate.kt @@ -15,6 +15,7 @@ */ package ca.allanwang.kau.kpref +import android.content.SharedPreferences import ca.allanwang.kau.kotlin.ILazyResettable /** @@ -26,29 +27,31 @@ import ca.allanwang.kau.kotlin.ILazyResettable */ interface KPrefDelegate<T> : ILazyResettable<T> { + val key: String operator fun setValue(any: Any, property: kotlin.reflect.KProperty<*>, t: T) } class KPrefException(message: String) : IllegalAccessException(message) class KPrefDelegateAndroid<T> internal constructor( - private val key: String, + override val key: String, private val fallback: T, private val pref: KPref, + private val prefBuilder: KPrefBuilderAndroid, private val transaction: KPrefTransaction<T>, private var postSetter: (value: T) -> Unit = {} ) : KPrefDelegate<T> { private object UNINITIALIZED + private val sp: SharedPreferences get() = prefBuilder.sp + @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@KPrefDelegateAndroid + pref.add(this) } override fun invalidate() { @@ -67,7 +70,7 @@ class KPrefDelegateAndroid<T> internal constructor( if (_v2 !== UNINITIALIZED) { _v2 as T } else { - _value = transaction.get(pref.sp, key, fallback) + _value = transaction.get(sp, key, fallback) _value as T } } @@ -75,11 +78,12 @@ class KPrefDelegateAndroid<T> internal constructor( override fun isInitialized(): Boolean = _value !== UNINITIALIZED - override fun toString(): String = if (isInitialized()) value.toString() else "Lazy kPref $key not initialized yet." + 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 - val editor = pref.sp.edit() + val editor = sp.edit() transaction.set(editor, key, t) editor.apply() postSetter(t) @@ -87,7 +91,7 @@ class KPrefDelegateAndroid<T> internal constructor( } class KPrefDelegateInMemory<T> internal constructor( - private val key: String, + override val key: String, private val fallback: T, private val pref: KPref, private var postSetter: (value: T) -> Unit = {} @@ -100,13 +104,11 @@ class KPrefDelegateInMemory<T> internal constructor( 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 + pref.add(this) } override fun invalidate() { - // No op + _value = UNINITIALIZED } @Suppress("UNCHECKED_CAST") @@ -129,7 +131,8 @@ class KPrefDelegateInMemory<T> internal constructor( override fun isInitialized(): Boolean = _value !== UNINITIALIZED - override fun toString(): String = if (isInitialized()) value.toString() else "Lazy kPref $key not initialized yet." + 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 diff --git a/core/src/main/kotlin/ca/allanwang/kau/kpref/KPrefFactory.kt b/core/src/main/kotlin/ca/allanwang/kau/kpref/KPrefFactory.kt new file mode 100644 index 0000000..0c49b88 --- /dev/null +++ b/core/src/main/kotlin/ca/allanwang/kau/kpref/KPrefFactory.kt @@ -0,0 +1,22 @@ +package ca.allanwang.kau.kpref + +import android.content.Context + +interface KPrefFactory { + fun createBuilder(preferenceName: String): KPrefBuilder +} + +/** + * Default factory for Android preferences + */ +class KPrefFactoryAndroid(context: Context) : KPrefFactory { + + val context: Context = context.applicationContext + + override fun createBuilder(preferenceName: String): KPrefBuilder = + KPrefBuilderAndroid(context.getSharedPreferences(preferenceName, Context.MODE_PRIVATE)) +} + +object KPrefFactoryInMemory : KPrefFactory { + override fun createBuilder(preferenceName: String): KPrefBuilder = KPrefBuilderInMemory +}
\ No newline at end of file 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 ae1f855..9d03a16 100644 --- a/core/src/main/kotlin/ca/allanwang/kau/kpref/KPrefSingleDelegate.kt +++ b/core/src/main/kotlin/ca/allanwang/kau/kpref/KPrefSingleDelegate.kt @@ -15,6 +15,7 @@ */ package ca.allanwang.kau.kpref +import android.content.SharedPreferences import ca.allanwang.kau.kotlin.ILazyResettable /** @@ -29,16 +30,19 @@ interface KPrefSingleDelegate : ILazyResettable<Boolean> class KPrefSingleDelegateAndroid internal constructor( private val key: String, - private val pref: KPref + private val pref: KPref, + private val prefBuilder: KPrefBuilderAndroid ) : KPrefSingleDelegate { @Volatile private var _value: Boolean? = null private val lock = this + private val sp: SharedPreferences get() = prefBuilder.sp + init { if (pref.prefMap.containsKey(key)) - throw KPrefException("$key is already used elsewhere in preference ${pref.PREFERENCE_NAME}") + throw KPrefException("$key is already used elsewhere in preference ${pref.preferenceName}") pref.prefMap[key] = this } @@ -57,9 +61,9 @@ class KPrefSingleDelegateAndroid internal constructor( if (_v2 != null) { _v2 } else { - _value = pref.sp.getBoolean(key, true) + _value = sp.getBoolean(key, true) if (_value!!) { - pref.sp.edit().putBoolean(key, false).apply() + sp.edit().putBoolean(key, false).apply() _value = false true } else false @@ -82,7 +86,7 @@ class KPrefSingleDelegateInMemory internal constructor( init { if (pref.prefMap.containsKey(key)) - throw KPrefException("$key is already used elsewhere in preference ${pref.PREFERENCE_NAME}") + throw KPrefException("$key is already used elsewhere in preference ${pref.preferenceName}") pref.prefMap[key] = this } diff --git a/docs/Changelog.md b/docs/Changelog.md index feb5e4a..576ac96 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -1,7 +1,8 @@ # Changelog -## v5.2.1 +## v5.3.0 * :color: Allow option to disable selected ring +* :core: Breaking KPref changes; see migration ## v5.2.0 * :about: Migrate about libraries to v7.x.x diff --git a/docs/Migration.md b/docs/Migration.md index fb29f7b..b0d0079 100644 --- a/docs/Migration.md +++ b/docs/Migration.md @@ -2,6 +2,18 @@ Below are some highlights on major refactoring/breaking changes +# v5.2.0 + +## KPref + +In an effort to improve testing options, KPref has some updates to allow for in memory preferences during unit tests. +While you can still use KPref like before as an object (provide the `KPrefFactoryAndroid` factory), +it is recommended that you use some sort of dependency injection/service locator to provide the factory. +An example can be found in the sample app test class. + +Also note that to better support in memory preferences, `reset()` will clear the in memory content for both variants, +meaning that in memory preferences will reset to default. Previously, in memory `reset()` did nothing. + # v5.1.0 ## KPref diff --git a/sample/build.gradle b/sample/build.gradle index 0ed7818..77165e8 100644 --- a/sample/build.gradle +++ b/sample/build.gradle @@ -26,7 +26,7 @@ android { versionName androidGitVersion.name() versionCode androidGitVersion.code() multiDexEnabled true - testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + testInstrumentationRunner "ca.allanwang.kau.sample.SampleTestRunner" } viewBinding { enabled = true @@ -137,9 +137,11 @@ dependencies { implementation project(':mediapicker') implementation Dependencies.materialDialog("input") + implementation Dependencies.koin testImplementation Dependencies.kotlinTest testImplementation Dependencies.junit + testImplementation Dependencies.koinTest androidTestImplementation Dependencies.kotlinTest androidTestImplementation Dependencies.espresso @@ -147,4 +149,5 @@ dependencies { androidTestImplementation Dependencies.espresso("contrib") androidTestImplementation Dependencies.testRules androidTestImplementation Dependencies.testRunner + androidTestImplementation Dependencies.koinTest } diff --git a/sample/src/androidTest/kotlin/ca/allanwang/kau/sample/KPrefViewTest.kt b/sample/src/androidTest/kotlin/ca/allanwang/kau/sample/KPrefViewTest.kt index 72199cf..156b66f 100644 --- a/sample/src/androidTest/kotlin/ca/allanwang/kau/sample/KPrefViewTest.kt +++ b/sample/src/androidTest/kotlin/ca/allanwang/kau/sample/KPrefViewTest.kt @@ -34,7 +34,14 @@ import org.hamcrest.Matchers.allOf import org.hamcrest.Matchers.instanceOf import org.junit.Rule import org.junit.Test +import org.junit.rules.RuleChain +import org.junit.rules.TestRule import org.junit.runner.RunWith +import org.koin.test.KoinTest +import org.koin.test.inject +import kotlin.test.BeforeTest +import kotlin.test.assertFalse +import kotlin.test.assertTrue /** * Created by Allan Wang on 21/12/2018. @@ -43,11 +50,20 @@ import org.junit.runner.RunWith */ @RunWith(AndroidJUnit4::class) @MediumTest -class KPrefViewTest { +class KPrefViewTest : KoinTest { - @get:Rule val activity: ActivityTestRule<MainActivity> = ActivityTestRule(MainActivity::class.java) + @get:Rule + val rule: TestRule = RuleChain.outerRule(SampleTestRule()).around(activity) + + private val pref: KPrefSample by inject() + + @BeforeTest + fun before() { + pref.reset() + } + fun verifyCheck(checked: Boolean): Matcher<View> { return object : BoundedMatcher<View, View>(View::class.java) { @@ -93,11 +109,11 @@ class KPrefViewTest { fun basicCheckboxToggle() { val checkbox1 = onCheckboxView(withChild(withText(R.string.checkbox_1))) - val initiallyChecked = KPrefSample.check1 + assertTrue(pref.check1, "check1 not normalized") - checkbox1.verifyCheck("checkbox1 init", initiallyChecked) + checkbox1.verifyCheck("checkbox1 init", true) checkbox1.perform(click()) - checkbox1.verifyCheck("checkbox1 after click", !initiallyChecked) + checkbox1.verifyCheck("checkbox1 after click", false) } /** @@ -107,23 +123,24 @@ class KPrefViewTest { fun dependentCheckboxToggle() { val checkbox2 = onCheckboxView(withChild(withText(R.string.checkbox_2))) val checkbox3 = - onCheckboxView(withChild(withText(R.string.checkbox_3)), withChild(withText(R.string.desc_dependent))) + onCheckboxView( + withChild(withText(R.string.checkbox_3)), + withChild(withText(R.string.desc_dependent)) + ) - // normalize so that both are checked - if (!KPrefSample.check2) - checkbox2.perform(click()) - if (!KPrefSample.check3) - checkbox3.perform(click()) + assertFalse(pref.check2, "check2 not normalized") + assertFalse(pref.check3, "check3 not normalized") - checkbox3.verifyCheck("checkbox3 init", true, true) + checkbox2.verifyCheck("checkbox2 init", checked = false, enabled = true) + checkbox3.verifyCheck("checkbox3 init", checked = false, enabled = false) checkbox3.perform(click()) - checkbox3.verifyCheck("checkbox3 after click", false, true) + checkbox3.verifyCheck("checkbox3 after disabled click", checked = false, enabled = false) checkbox2.perform(click()) - checkbox2.verifyCheck("checkbox2 after click", false, true) - checkbox3.verifyCheck("checkbox3 after checkbox2 click", false, false) + checkbox2.verifyCheck("checkbox2 after click", checked = true, enabled = true) + checkbox3.verifyCheck("checkbox3 after checkbox2 click", checked = false, enabled = true) checkbox3.perform(click()) - checkbox3.verifyCheck("checkbox3 after disabled click", false, false) + checkbox3.verifyCheck("checkbox3 after enabled click", checked = true, enabled = true) } } diff --git a/sample/src/androidTest/kotlin/ca/allanwang/kau/sample/SampleTestApp.kt b/sample/src/androidTest/kotlin/ca/allanwang/kau/sample/SampleTestApp.kt new file mode 100644 index 0000000..8705fbc --- /dev/null +++ b/sample/src/androidTest/kotlin/ca/allanwang/kau/sample/SampleTestApp.kt @@ -0,0 +1,60 @@ +package ca.allanwang.kau.sample + +import android.app.Application +import android.content.Context +import androidx.test.runner.AndroidJUnitRunner +import ca.allanwang.kau.kpref.KPrefFactory +import ca.allanwang.kau.kpref.KPrefFactoryInMemory +import ca.allanwang.kau.logging.KL +import org.junit.rules.TestRule +import org.junit.runner.Description +import org.junit.runners.model.Statement +import org.koin.android.ext.koin.androidContext +import org.koin.android.ext.koin.androidLogger +import org.koin.core.KoinComponent +import org.koin.core.context.startKoin +import org.koin.core.get +import org.koin.dsl.module + +class SampleTestRunner : AndroidJUnitRunner() { + override fun newApplication( + cl: ClassLoader?, + className: String?, + context: Context? + ): Application { + return super.newApplication(cl, SampleTestApp::class.java.name, context) + } +} + +class SampleTestRule : TestRule { + override fun apply(base: Statement, description: Description): Statement = + object : Statement(), KoinComponent { + override fun evaluate() { + + // Reset prefs + val pref: KPrefSample = get() + pref.reset() + + base.evaluate() + } + } +} + +class SampleTestApp : Application() { + override fun onCreate() { + super.onCreate() + startKoin { + if (BuildConfig.DEBUG) { + androidLogger() + } + androidContext(this@SampleTestApp) + modules(listOf(prefFactoryModule(), SampleApp.prefModule())) + } + } + + fun prefFactoryModule() = module { + single<KPrefFactory> { + KPrefFactoryInMemory + } + } +}
\ No newline at end of file diff --git a/sample/src/androidTest/kotlin/ca/allanwang/kau/sample/utils/EspressoUtils.kt b/sample/src/androidTest/kotlin/ca/allanwang/kau/sample/utils/EspressoUtils.kt index 09ad00a..2a6f0a9 100644 --- a/sample/src/androidTest/kotlin/ca/allanwang/kau/sample/utils/EspressoUtils.kt +++ b/sample/src/androidTest/kotlin/ca/allanwang/kau/sample/utils/EspressoUtils.kt @@ -15,6 +15,8 @@ */ package ca.allanwang.kau.sample.utils +import android.content.Context +import androidx.test.platform.app.InstrumentationRegistry import org.hamcrest.BaseMatcher import org.hamcrest.Description import org.hamcrest.Matcher diff --git a/sample/src/main/kotlin/ca/allanwang/kau/sample/AnimActivity.kt b/sample/src/main/kotlin/ca/allanwang/kau/sample/AnimActivity.kt index a472f09..bd1039b 100644 --- a/sample/src/main/kotlin/ca/allanwang/kau/sample/AnimActivity.kt +++ b/sample/src/main/kotlin/ca/allanwang/kau/sample/AnimActivity.kt @@ -28,6 +28,7 @@ import ca.allanwang.kau.utils.fullLinearRecycler import ca.allanwang.kau.utils.startActivity import ca.allanwang.kau.utils.withAlpha import ca.allanwang.kau.utils.withSlideOut +import org.koin.android.ext.android.inject /** * Created by Allan Wang on 2017-06-12. @@ -37,12 +38,14 @@ import ca.allanwang.kau.utils.withSlideOut */ class AnimActivity : KauBaseActivity() { + private val pref: KPrefSample by inject() + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val adapter = SingleFastAdapter() setContentView(fullLinearRecycler(adapter).apply { setBackgroundColor( - KPrefSample.bgColor.withAlpha(255) + pref.bgColor.withAlpha(255) ) }) diff --git a/sample/src/main/kotlin/ca/allanwang/kau/sample/KPrefSample.kt b/sample/src/main/kotlin/ca/allanwang/kau/sample/KPrefSample.kt index 80a75bf..97e8075 100644 --- a/sample/src/main/kotlin/ca/allanwang/kau/sample/KPrefSample.kt +++ b/sample/src/main/kotlin/ca/allanwang/kau/sample/KPrefSample.kt @@ -17,11 +17,12 @@ package ca.allanwang.kau.sample import android.graphics.Color import ca.allanwang.kau.kpref.KPref +import ca.allanwang.kau.kpref.KPrefFactory /** * Created by Allan Wang on 2017-06-07. */ -object KPrefSample : KPref() { +class KPrefSample(factory: KPrefFactory) : KPref("pref_sample", factory = factory) { var version: Int by kpref("version", -1) var textColor: Int by kpref("TEXT_COLOR", Color.WHITE) var accentColor: Int by kpref("ACCENT_COLOR", 0xffff8900.toInt()) diff --git a/sample/src/main/kotlin/ca/allanwang/kau/sample/MainActivity.kt b/sample/src/main/kotlin/ca/allanwang/kau/sample/MainActivity.kt index 4fe941b..4cf632f 100644 --- a/sample/src/main/kotlin/ca/allanwang/kau/sample/MainActivity.kt +++ b/sample/src/main/kotlin/ca/allanwang/kau/sample/MainActivity.kt @@ -40,6 +40,7 @@ import ca.allanwang.kau.utils.withSceneTransitionAnimation import ca.allanwang.kau.xml.showChangelog import com.afollestad.materialdialogs.input.input import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial +import org.koin.android.ext.android.inject class MainActivity : KPrefActivity() { @@ -101,9 +102,11 @@ class MainActivity : KPrefActivity() { const val REQUEST_MEDIA = 27 } + private val pref: KPrefSample by inject() + override fun kPrefCoreAttributes(): CoreAttributeContract.() -> Unit = { - textColor = KPrefSample::textColor - accentColor = KPrefSample::accentColor + textColor = pref::textColor + accentColor = pref::accentColor } override fun onCreateKPrefs(savedInstanceState: Bundle?): KPrefAdapterBuilder.() -> Unit = { @@ -113,7 +116,7 @@ class MainActivity : KPrefActivity() { /** * This is how the setup looks like with all the proper tags */ - checkbox(title = R.string.checkbox_1, getter = KPrefSample::check1, setter = { KPrefSample.check1 = it }, + checkbox(title = R.string.checkbox_1, getter = pref::check1, setter = { pref.check1 = it }, builder = { descRes = R.string.desc }) @@ -123,25 +126,25 @@ class MainActivity : KPrefActivity() { */ checkbox( R.string.checkbox_2, - KPrefSample::check2, - { KPrefSample.check2 = it; reloadByTitle(R.string.checkbox_3) }) + pref::check2, + { pref.check2 = it; reloadByTitle(R.string.checkbox_3) }) /** * Since the builder is the last argument and is a lambda, we may write the setup cleanly like so: */ - checkbox(R.string.checkbox_3, KPrefSample::check3, { KPrefSample.check3 = it }) { + checkbox(R.string.checkbox_3, pref::check3, { pref.check3 = it }) { descRes = R.string.desc_dependent - enabler = { KPrefSample.check2 } + enabler = { pref.check2 } onDisabledClick = { itemView.context.toast("I am still disabled") } } - colorPicker(R.string.text_color, KPrefSample::textColor, { KPrefSample.textColor = it; reload() }) { + colorPicker(R.string.text_color, pref::textColor, { pref.textColor = it; reload() }) { descRes = R.string.color_custom allowCustom = true } - colorPicker(R.string.accent_color, KPrefSample::accentColor, { - KPrefSample.accentColor = it + colorPicker(R.string.accent_color, pref::accentColor, { + pref.accentColor = it reload() this@MainActivity.navigationBarColor = it toolbarCanvas.ripple(it, RippleCanvas.MIDDLE, RippleCanvas.END, duration = 500L) @@ -150,8 +153,8 @@ class MainActivity : KPrefActivity() { allowCustom = false } - colorPicker(R.string.background_color, KPrefSample::bgColor, { - KPrefSample.bgColor = it; bgCanvas.ripple(it, duration = 500L) + colorPicker(R.string.background_color, pref::bgColor, { + pref.bgColor = it; bgCanvas.ripple(it, duration = 500L) }) { iicon = GoogleMaterial.Icon.gmd_colorize descRes = R.string.color_custom_alpha @@ -159,7 +162,7 @@ class MainActivity : KPrefActivity() { allowCustom = true } - text(R.string.text, KPrefSample::text, { KPrefSample.text = it }) { + text(R.string.text, pref::text, { pref.text = it }) { descRes = R.string.text_desc onClick = { itemView.context.materialDialog { @@ -171,7 +174,7 @@ class MainActivity : KPrefActivity() { } } - seekbar(R.string.seekbar, KPrefSample::seekbar, { KPrefSample.seekbar = it }) { + seekbar(R.string.seekbar, pref::seekbar, { pref.seekbar = it }) { descRes = R.string.kau_lorem_ipsum increments = 5 textViewConfigs = { @@ -215,30 +218,30 @@ class MainActivity : KPrefActivity() { */ checkbox( R.string.checkbox_2, - KPrefSample::check2, - { KPrefSample.check2 = it; reloadByTitle(R.string.checkbox_3) }) { + pref::check2, + { pref.check2 = it; reloadByTitle(R.string.checkbox_3) }) { titleFun = { R.string.checkbox_3 } descRes = R.string.kau_lorem_ipsum } - text(R.string.text, KPrefSample::text, { KPrefSample.text = it }) { + text(R.string.text, pref::text, { pref.text = it }) { descRes = R.string.kau_lorem_ipsum textGetter = { string(R.string.kau_lorem_ipsum) } } - timePicker(R.string.time, KPrefSample::time12, { KPrefSample.time12 = it }) { + timePicker(R.string.time, pref::time12, { pref.time12 = it }) { descRes = R.string.time_desc_12 use24HourFormat = false } - timePicker(R.string.time, KPrefSample::time24, { KPrefSample.time24 = it }) { + timePicker(R.string.time, pref::time24, { pref.time24 = it }) { descRes = R.string.time_desc_24 use24HourFormat = true } } fun subPrefs(): KPrefAdapterBuilder.() -> Unit = { - text(R.string.text, { KPrefSample.text }, { KPrefSample.text = it }) { + text(R.string.text, { pref.text }, { pref.text = it }) { descRes = R.string.text_desc onClick = { itemView.context.materialDialog { @@ -254,11 +257,11 @@ class MainActivity : KPrefActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - bgCanvas.set(KPrefSample.bgColor) - toolbarCanvas.set(KPrefSample.accentColor) - this.navigationBarColor = KPrefSample.accentColor - if (KPrefSample.version < BuildConfig.VERSION_CODE) { - KPrefSample.version = BuildConfig.VERSION_CODE + bgCanvas.set(pref.bgColor) + toolbarCanvas.set(pref.accentColor) + this.navigationBarColor = pref.accentColor + if (pref.version < BuildConfig.VERSION_CODE) { + pref.version = BuildConfig.VERSION_CODE if (!BuildConfig.DEBUG) showChangelog(R.xml.kau_changelog) } diff --git a/sample/src/main/kotlin/ca/allanwang/kau/sample/SampleApp.kt b/sample/src/main/kotlin/ca/allanwang/kau/sample/SampleApp.kt index 6e6d718..2537090 100644 --- a/sample/src/main/kotlin/ca/allanwang/kau/sample/SampleApp.kt +++ b/sample/src/main/kotlin/ca/allanwang/kau/sample/SampleApp.kt @@ -16,6 +16,13 @@ package ca.allanwang.kau.sample import android.app.Application +import ca.allanwang.kau.kpref.KPrefFactory +import ca.allanwang.kau.kpref.KPrefFactoryAndroid +import org.koin.android.ext.koin.androidContext +import org.koin.android.ext.koin.androidLogger +import org.koin.core.context.startKoin +import org.koin.core.module.Module +import org.koin.dsl.module /** * Created by Allan Wang on 2017-06-08. @@ -23,6 +30,27 @@ import android.app.Application class SampleApp : Application() { override fun onCreate() { super.onCreate() - KPrefSample.initialize(this, "pref_sample") + + startKoin { + if (BuildConfig.DEBUG) { + androidLogger() + } + androidContext(this@SampleApp) + modules(listOf(prefFactoryModule(), prefModule())) + } + } + + companion object { + fun prefFactoryModule(): Module = module { + single<KPrefFactory> { + KPrefFactoryAndroid(get()) + } + } + + fun prefModule(): Module = module { + single { + KPrefSample(get()) + } + } } } diff --git a/sample/src/main/res/xml/kau_changelog.xml b/sample/src/main/res/xml/kau_changelog.xml index d61fe63..55adabc 100644 --- a/sample/src/main/res/xml/kau_changelog.xml +++ b/sample/src/main/res/xml/kau_changelog.xml @@ -6,9 +6,9 @@ <item text="" /> --> - <version title="v5.2.1" /> + <version title="v5.3.0" /> <item text=":color: Allow option to disable selected ring" /> - <item text="" /> + <item text=":core: Breaking KPref changes; see migration" /> <item text="" /> <item text="" /> |