diff options
Diffstat (limited to 'core')
15 files changed, 364 insertions, 150 deletions
diff --git a/core/README.md b/core/README.md index b9a10f5..b1140bf 100644 --- a/core/README.md +++ b/core/README.md @@ -71,6 +71,14 @@ object MyPrefs : KPref() { Notice that it is a `val` and takes no default. It will return true the first time and false for all subsequent calls. +### KPref Testing + +If your android components can be tested without an emulator, you can also modify KPref to operate without shared preferences. +To do so, call `KPref(KPrefBuilderInMemory)` when creating your preferences. +This variant does not pass updates to the shared preferences. +To set the builder, you may wish to use dependency injection or service locators to supply the builder and the KPref. +In that case, your preferences would be a class instead of an object. + ## Changelog XML Create an xml resource with the following structure: 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 29f5af1..143b83f 100644 --- a/core/src/androidTest/kotlin/ca/allanwang/kau/kpref/KPrefTest.kt +++ b/core/src/androidTest/kotlin/ca/allanwang/kau/kpref/KPrefTest.kt @@ -24,8 +24,6 @@ import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import kotlin.test.assertEquals -import kotlin.test.assertFalse -import kotlin.test.assertTrue /** * Created by Allan Wang on 2017-08-01. @@ -34,19 +32,24 @@ import kotlin.test.assertTrue @MediumTest class KPrefTest { - lateinit var pref: TestPref + lateinit var androidPref: TestPref + lateinit var memPref: TestPref - class TestPref : KPref() { + class TestPref(builder: KPrefBuilder) : KPref(builder) { init { initialize(ApplicationProvider.getApplicationContext<Context>(), "kpref_test_${System.currentTimeMillis()}") } + var postSetterCount: Int = 0 + var one by kpref("one", 1) var two by kpref("two", 2f) - var `true` by kpref("true", true) + var `true` by kpref("true", true, postSetter = { + postSetterCount++ + }) var hello by kpref("hello", "hello") @@ -57,51 +60,67 @@ class KPrefTest { @Before fun init() { - pref = TestPref() - pref.sp.edit().clear().commit() + androidPref = TestPref(KPrefBuilderAndroid) + androidPref.sp.edit().clear().commit() + memPref = TestPref(KPrefBuilderInMemory) + } + + private fun pref(action: TestPref.() -> Unit) { + androidPref.action() + memPref.action() + } + + 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") } @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") + assertPrefEquals(1, { one }) + assertPrefEquals(2f, { two }) + assertPrefEquals(true, { `true` }) + 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") } @Test fun setter() { - assertEquals(1, pref.one) - pref.one = 2 - assertEquals(2, pref.one) - pref.hello = "goodbye" - assertEquals("goodbye", pref.hello) - assertEquals(pref.hello, pref.sp.getString("hello", "hello")) - assertEquals(2, pref.sp.all.size) + assertPrefEquals(1, { one }) + pref { one = 2 } + assertPrefEquals(2, { one }) + pref { hello = "goodbye" } + assertPrefEquals("goodbye", { hello }) + assertEquals(androidPref.hello, androidPref.sp.getString("hello", "badfallback")) + assertEquals(2, androidPref.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, "Kpref did not properly fetch from shared prefs") - pref.sp.edit().putInt("one", -1).commit() - assertEquals(2, pref.one, "Lazy kpref should still retain old value") - pref.reset() - assertEquals(-1, pref.one, "Kpref did not refetch from shared prefs upon reset") + pref { one = 2 } + 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") + // Android pref only + androidPref.sp.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") } @Test fun single() { - assertTrue(pref.oneShot) - assertFalse(pref.oneShot) + assertPrefEquals(true, { oneShot }) + assertPrefEquals(false, { androidPref.oneShot }) + } + + @Test + fun postSetter() { + pref { `true` = true } + assertPrefEquals(1, { postSetterCount }, "Post setter was not called") } } 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<Boolean> + + 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> = + kpref(key, fallback.toFloat(), postSetter) + + 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: Set<String>, + postSetter: (value: Set<String>) -> Unit = {} + ): KPrefDelegate<Set<String>> + + fun KPref.kpref(key: String, fallback: String, postSetter: (value: String) -> Unit = {}): KPrefDelegate<String> + + 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<String>, postSetter: (value: Set<String>) -> 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<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) +}
\ 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<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. * @@ -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<T> internal constructor( + +interface KPrefDelegate<T> : ILazyResettable<T> { + 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, private val fallback: T, private val pref: KPref, private val transaction: KPrefTransaction<T>, private var postSetter: (value: T) -> Unit = {} -) : ILazyResettable<T> { +) : KPrefDelegate<T> { private object UNINITIALIZED @@ -67,28 +48,26 @@ class KPrefDelegate<T> 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<T> 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<T> internal constructor( } } -class KPrefException(message: String) : IllegalAccessException(message) +class KPrefDelegateInMemory<T> internal constructor( + private val key: String, + private val fallback: T, + private val pref: KPref, + private var postSetter: (value: T) -> Unit = {} +) : KPrefDelegate<T> { + + 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<Boolean> { +interface KPrefSingleDelegate : ILazyResettable<Boolean> + +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 diff --git a/core/src/main/kotlin/ca/allanwang/kau/kpref/KPrefTransaction.kt b/core/src/main/kotlin/ca/allanwang/kau/kpref/KPrefTransaction.kt index 1070e11..3f8fe28 100644 --- a/core/src/main/kotlin/ca/allanwang/kau/kpref/KPrefTransaction.kt +++ b/core/src/main/kotlin/ca/allanwang/kau/kpref/KPrefTransaction.kt @@ -60,7 +60,7 @@ internal object KPrefFloatTransaction : KPrefTransaction<Float> { internal object KPrefStringTransaction : KPrefTransaction<String> { override fun get(prefs: SharedPreferences, key: String, fallback: String) = - prefs.getString(key, fallback) + prefs.getString(key, fallback) ?: "" override fun set(editor: SharedPreferences.Editor, key: String, data: String) { editor.putString(key, data) diff --git a/core/src/main/kotlin/ca/allanwang/kau/swipe/ViewDragHelper.java b/core/src/main/kotlin/ca/allanwang/kau/swipe/ViewDragHelper.java index ac7fb7e..f6e907a 100644 --- a/core/src/main/kotlin/ca/allanwang/kau/swipe/ViewDragHelper.java +++ b/core/src/main/kotlin/ca/allanwang/kau/swipe/ViewDragHelper.java @@ -25,7 +25,7 @@ import static ca.allanwang.kau.swipe.SwipeBackHelperKt.SWIPE_EDGE_TOP; * of useful operations and state tracking for allowing a user to drag and reposition * views within their parent ViewGroup. * <p> - * This is an extension of {@link androidx.core.widget.ViewDragHelper} + * This is an extension of {@link androidx.customview.widget.ViewDragHelper} * Along with additional methods defined in {@link ViewDragHelperExtras} */ class ViewDragHelper implements ViewDragHelperExtras { diff --git a/core/src/main/kotlin/ca/allanwang/kau/utils/BundleUtils.kt b/core/src/main/kotlin/ca/allanwang/kau/utils/BundleUtils.kt index 40c1c72..b857eb0 100644 --- a/core/src/main/kotlin/ca/allanwang/kau/utils/BundleUtils.kt +++ b/core/src/main/kotlin/ca/allanwang/kau/utils/BundleUtils.kt @@ -89,16 +89,6 @@ fun bundleOf(vararg params: kotlin.Pair<String, Any?>): Bundle { } /** - * Adds transition bundle if context is activity and build is lollipop+ - */ -@SuppressLint("NewApi") -fun Bundle.withSceneTransitionAnimation(context: Context) { - if (context !is Activity || !buildIsLollipopAndUp) return - val options = ActivityOptions.makeSceneTransitionAnimation(context) - putAll(options.toBundle()) -} - -/** * Given the parent view and map of view ids to tags, * create a scene transition animation */ @@ -112,7 +102,7 @@ fun Bundle.withSceneTransitionAnimation(parent: View, data: Map<Int, String>) = * create a scene transition animation */ @SuppressLint("NewApi") -fun Bundle.withSceneTransitionAnimation(context: Context, data: Map<out View, String>) { +fun Bundle.withSceneTransitionAnimation(context: Context, data: Map<out View, String> = emptyMap()) { if (context !is Activity || !buildIsLollipopAndUp) return val options = ActivityOptions.makeSceneTransitionAnimation( context, diff --git a/core/src/main/kotlin/ca/allanwang/kau/utils/ColorUtils.kt b/core/src/main/kotlin/ca/allanwang/kau/utils/ColorUtils.kt index bbb8953..3de0297 100644 --- a/core/src/main/kotlin/ca/allanwang/kau/utils/ColorUtils.kt +++ b/core/src/main/kotlin/ca/allanwang/kau/utils/ColorUtils.kt @@ -20,7 +20,6 @@ import android.content.Context import android.content.res.ColorStateList import android.graphics.Color import android.graphics.PorterDuff -import android.graphics.drawable.Drawable import android.os.Build import android.widget.CheckBox import android.widget.EditText @@ -28,13 +27,13 @@ import android.widget.ImageButton import android.widget.ProgressBar import android.widget.RadioButton import android.widget.SeekBar -import android.widget.TextView import androidx.annotation.ColorInt import androidx.annotation.FloatRange import androidx.annotation.IntRange import androidx.appcompat.widget.AppCompatEditText import androidx.appcompat.widget.Toolbar import androidx.core.graphics.drawable.DrawableCompat +import androidx.core.view.ViewCompat import com.afollestad.materialdialogs.R import java.util.Random @@ -248,37 +247,16 @@ fun Context.textColorStateList(@ColorInt color: Int): ColorStateList { return ColorStateList(states, colors) } -@SuppressLint("RestrictedApi") +/** + * Note that this does not tint the cursor, as there is no public api to do so. + */ fun EditText.tint(@ColorInt color: Int) { val editTextColorStateList = context.textColorStateList(color) if (this is AppCompatEditText) { - supportBackgroundTintList = editTextColorStateList + ViewCompat.setBackgroundTintList(this, editTextColorStateList) } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { backgroundTintList = editTextColorStateList } - tintCursor(color) -} - -fun EditText.tintCursor(@ColorInt color: Int) { - try { - val fCursorDrawableRes = TextView::class.java.getDeclaredField("mCursorDrawableRes") - fCursorDrawableRes.isAccessible = true - val mCursorDrawableRes = fCursorDrawableRes.getInt(this) - val fEditor = TextView::class.java.getDeclaredField("mEditor") - fEditor.isAccessible = true - val editor = fEditor.get(this) - val clazz = editor.javaClass - val fCursorDrawable = clazz.getDeclaredField("mCursorDrawable") - fCursorDrawable.isAccessible = true - val drawables: Array<Drawable> = Array(2, { - val drawable = context.drawable(mCursorDrawableRes) - drawable.setColorFilter(color, PorterDuff.Mode.SRC_IN) - drawable - }) - fCursorDrawable.set(editor, drawables) - } catch (e: Exception) { - e.printStackTrace() - } } fun Toolbar.tint(@ColorInt color: Int, tintTitle: Boolean = true) { diff --git a/core/src/main/kotlin/ca/allanwang/kau/utils/ContextUtils.kt b/core/src/main/kotlin/ca/allanwang/kau/utils/ContextUtils.kt index e8680dc..d002fb8 100644 --- a/core/src/main/kotlin/ca/allanwang/kau/utils/ContextUtils.kt +++ b/core/src/main/kotlin/ca/allanwang/kau/utils/ContextUtils.kt @@ -239,7 +239,7 @@ fun Context.hasPermission(permissions: String) = !buildIsMarshmallowAndUp || Con fun Context.copyToClipboard(text: String?, label: String = "Copied Text", showToast: Boolean = true) { val clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager - clipboard.primaryClip = ClipData.newPlainText(label, text ?: "") + clipboard.setPrimaryClip(ClipData.newPlainText(label, text ?: "")) if (showToast) toast(R.string.kau_text_copied) } diff --git a/core/src/main/kotlin/ca/allanwang/kau/utils/CoroutineUtils.kt b/core/src/main/kotlin/ca/allanwang/kau/utils/CoroutineUtils.kt index 57a9921..4d6ee54 100644 --- a/core/src/main/kotlin/ca/allanwang/kau/utils/CoroutineUtils.kt +++ b/core/src/main/kotlin/ca/allanwang/kau/utils/CoroutineUtils.kt @@ -18,6 +18,7 @@ package ca.allanwang.kau.utils import android.content.Context import android.os.Handler import android.os.Looper +import ca.allanwang.kau.internal.KauBaseActivity import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineStart import kotlinx.coroutines.android.asCoroutineDispatcher @@ -43,11 +44,11 @@ object ContextHelper : CoroutineScope { } /** - * Most context items implement [CoroutineScope] by default. + * Most context items implement [CoroutineScope] by default (through [KauBaseActivity]). * We will add a fallback just in case. * It is expected that the scope returned always has the Android main dispatcher as part of the context. */ -internal inline val Context.ctxCoroutine: CoroutineScope +inline val Context.ctxCoroutine: CoroutineScope get() = this as? CoroutineScope ?: ContextHelper /** diff --git a/core/src/main/res-public/values-ar-rSA/strings_commons.xml b/core/src/main/res-public/values-ar-rSA/strings_commons.xml new file mode 100644 index 0000000..efaa623 --- /dev/null +++ b/core/src/main/res-public/values-ar-rSA/strings_commons.xml @@ -0,0 +1,55 @@ +<?xml version="1.0" encoding="utf-8" standalone="no"?> +<!--Generated by crowdin.com--> +<!-- +A collection of common string values +Most resources are verbatim and x represents a formatted item +--> +<resources> + <string name="kau_about_app">عن التطبيق</string> + <string name="kau_about_x">حول %s</string> + <string name="kau_add_account">إضافة حساب</string> + <string name="kau_back">العودة</string> + <string name="kau_cancel">إلغاء</string> + <string name="kau_changelog">سجل التغييرات</string> + <string name="kau_close">إغلاق</string> + <string name="kau_contact_us">التواصل معنا</string> + <string name="kau_copy">نسخ</string> + <string name="kau_custom">مخصص</string> + <string name="kau_dark">مظلم</string> + <string name="kau_default">الافتراضي</string> + <string name="kau_do_not_show_again">لا تظهر مجدداً</string> + <string name="kau_done">تم</string> + <string name="kau_error">خطأ</string> + <string name="kau_exit">خروج</string> + <string name="kau_exit_confirmation">هل أنت متأكد بأنك تريد الخروج؟</string> + <string name="kau_exit_confirmation_x">هل أنت متأكد بأنك تريد الخروج %s؟</string> + <string name="kau_glass">زجاجي</string> + <string name="kau_got_it">فهمت</string> + <string name="kau_great">رائع</string> + <string name="kau_hide">إخفاء</string> + <string name="kau_light">فاتح</string> + <string name="kau_login">تسجيل الدخول</string> + <string name="kau_logout">تسجيل الخروج</string> + <string name="kau_logout_confirm_as_x">هل أنت متأكد بأنك تريد تسجيل الخروج %s؟</string> + <string name="kau_manage_account">إدارة الحساب</string> + <string name="kau_maybe">ربما</string> + <string name="kau_menu">القائمة</string> + <string name="kau_no">لا</string> + <string name="kau_no_results_found">لم يتم العثور على نتائج</string> + <string name="kau_none">لا يوجد</string> + <string name="kau_ok">حسناً</string> + <string name="kau_play_store">Google play</string> + <string name="kau_rate">تقييم</string> + <string name="kau_report_bug">الإبلاغ عن خطأ</string> + <string name="kau_search">البحث</string> + <string name="kau_send_feedback">إرسال ملاحظات</string> + <string name="kau_send_via">الإرسال عبر</string> + <string name="kau_settings">الاعدادات</string> + <string name="kau_share">المشاركة</string> + <string name="kau_text_copied">تم نسخ النص إلى الحافظة.</string> + <string name="kau_thank_you">شكراً</string> + <string name="kau_uh_oh">أوبس</string> + <string name="kau_warning">تحذير</string> + <string name="kau_yes">نعم</string> + <string name="kau_permission_denied">الإذن مرفوض</string> +</resources> diff --git a/core/src/main/res-public/values/public.xml b/core/src/main/res-public/values/public.xml index 9761199..0b050dc 100644 --- a/core/src/main/res-public/values/public.xml +++ b/core/src/main/res-public/values/public.xml @@ -1,20 +1,27 @@ -<resources xmlns:tools='http://schemas.android.com/tools' tools:ignore='ResourceName'> +<resources xmlns:tools='http://schemas.android.com/tools' tools:ignore='ResourceName'>
<!-- AUTO-GENERATED FILE. DO NOT MODIFY. public.xml is generated by the generatepublicxml gradle task --> + <public name='kau_enter_slide_left' type='transition' /> + <public name='kau_exit_slide_right' type='transition' /> + <public name='kau_exit_slide_bottom' type='transition' /> + <public name='kau_enter_slide_bottom' type='transition' /> + <public name='kau_enter_slide_right' type='transition' /> + <public name='kau_exit_slide_top' type='transition' /> + <public name='kau_enter_slide_top' type='transition' /> + <public name='kau_exit_slide_left' type='transition' /> + <public name='kau_selectable_white' type='drawable' /> + <public name='kau_transparent' type='drawable' /> + <public name='kau_slide_out_left_top' type='anim' /> + <public name='kau_slide_in_right' type='anim' /> + <public name='kau_slide_out_right_top' type='anim' /> <public name='kau_slide_in_top' type='anim' /> - <public name='kau_slide_in_left' type='anim' /> + <public name='kau_slide_out_left' type='anim' /> <public name='kau_slide_out_right' type='anim' /> - <public name='kau_slide_out_right_top' type='anim' /> <public name='kau_fade_in' type='anim' /> - <public name='kau_slide_out_top' type='anim' /> <public name='kau_slide_out_bottom' type='anim' /> - <public name='kau_fade_out' type='anim' /> - <public name='kau_slide_out_left' type='anim' /> - <public name='kau_slide_out_left_top' type='anim' /> <public name='kau_slide_in_bottom' type='anim' /> - <public name='kau_slide_in_right' type='anim' /> - <public name='kau_transparent' type='drawable' /> - <public name='kau_selectable_white' type='drawable' /> - <public name='kau_shadow_overlay' type='color' /> + <public name='kau_slide_out_top' type='anim' /> + <public name='kau_fade_out' type='anim' /> + <public name='kau_slide_in_left' type='anim' /> <public name='kau_activity_horizontal_margin' type='dimen' /> <public name='kau_activity_vertical_margin' type='dimen' /> <public name='kau_dialog_margin' type='dimen' /> @@ -43,6 +50,13 @@ <public name='kau_avatar_padding' type='dimen' /> <public name='kau_avatar_margin' type='dimen' /> <public name='kau_avatar_ripple_radius' type='dimen' /> + <public name='KauFadeIn' type='style' /> + <public name='KauFadeInFadeOut' type='style' /> + <public name='KauSlideInRight' type='style' /> + <public name='KauSlideInBottom' type='style' /> + <public name='KauSlideInFadeOut' type='style' /> + <public name='KauSlideInSlideOutRight' type='style' /> + <public name='KauSlideInSlideOutBottom' type='style' /> <public name='kau_about_app' type='string' /> <public name='kau_about_x' type='string' /> <public name='kau_add_account' type='string' /> @@ -98,21 +112,7 @@ <public name='kau_permission_denied' type='string' /> <public name='kau_0' type='string' /> <public name='kau_bullet_point' type='string' /> + <public name='kau_shadow_overlay' type='color' /> <public name='Kau' type='style' /> <public name='Kau.Translucent' type='style' /> - <public name='KauFadeIn' type='style' /> - <public name='KauFadeInFadeOut' type='style' /> - <public name='KauSlideInRight' type='style' /> - <public name='KauSlideInBottom' type='style' /> - <public name='KauSlideInFadeOut' type='style' /> - <public name='KauSlideInSlideOutRight' type='style' /> - <public name='KauSlideInSlideOutBottom' type='style' /> - <public name='kau_enter_slide_bottom' type='transition' /> - <public name='kau_enter_slide_top' type='transition' /> - <public name='kau_exit_slide_bottom' type='transition' /> - <public name='kau_exit_slide_top' type='transition' /> - <public name='kau_enter_slide_right' type='transition' /> - <public name='kau_exit_slide_right' type='transition' /> - <public name='kau_exit_slide_left' type='transition' /> - <public name='kau_enter_slide_left' type='transition' /> </resources>
\ No newline at end of file diff --git a/core/src/test/kotlin/ca/allanwang/kau/ui/ProgressAnimatorTest.kt b/core/src/test/kotlin/ca/allanwang/kau/ui/ProgressAnimatorTest.kt index 60f8680..6b99b14 100644 --- a/core/src/test/kotlin/ca/allanwang/kau/ui/ProgressAnimatorTest.kt +++ b/core/src/test/kotlin/ca/allanwang/kau/ui/ProgressAnimatorTest.kt @@ -1,3 +1,18 @@ +/* + * Copyright 2019 Allan Wang + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package ca.allanwang.kau.ui import org.junit.Test @@ -71,5 +86,4 @@ class ProgressAnimatorTest { assertTrue(called) assertTrue(i > 0.5f) } - -}
\ No newline at end of file +} |