aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--about/src/main/res-public/values/public.xml2
-rw-r--r--adapter/src/main/res-public/values/public.xml2
-rw-r--r--colorpicker/src/main/res-public/values/public.xml2
-rw-r--r--core-ui/src/main/kotlin/ca/allanwang/kau/ui/views/CutoutView.kt2
-rw-r--r--core-ui/src/main/res-public/values/public.xml2
-rw-r--r--core/src/main/kotlin/ca/allanwang/kau/email/EmailBuilder.kt3
-rw-r--r--core/src/main/kotlin/ca/allanwang/kau/ui/ProgressAnimator.kt184
-rw-r--r--core/src/main/res-public/values/public.xml2
-rw-r--r--core/src/test/kotlin/ca/allanwang/kau/ui/ProgressAnimatorTest.kt75
-rw-r--r--docs/Changelog.md3
-rw-r--r--docs/Migration.md4
-rw-r--r--kpref-activity/src/main/res-public/values/public.xml2
-rw-r--r--mediapicker/src/main/res-public/values/public.xml2
-rw-r--r--searchview/src/main/res-public/values/public.xml2
14 files changed, 226 insertions, 61 deletions
diff --git a/about/src/main/res-public/values/public.xml b/about/src/main/res-public/values/public.xml
index 3739dd1..2db68c4 100644
--- a/about/src/main/res-public/values/public.xml
+++ b/about/src/main/res-public/values/public.xml
@@ -1,4 +1,4 @@
<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 -->
+<!-- AUTO-GENERATED FILE. DO NOT MODIFY. public.xml is generated by the generatepublicxml gradle task -->
<public name='Kau.About' type='style' />
</resources> \ No newline at end of file
diff --git a/adapter/src/main/res-public/values/public.xml b/adapter/src/main/res-public/values/public.xml
index 4d16c2a..cf14680 100644
--- a/adapter/src/main/res-public/values/public.xml
+++ b/adapter/src/main/res-public/values/public.xml
@@ -1,4 +1,4 @@
<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 -->
+<!-- AUTO-GENERATED FILE. DO NOT MODIFY. public.xml is generated by the generatepublicxml gradle task -->
<public name='dummy' type='id' />
</resources> \ No newline at end of file
diff --git a/colorpicker/src/main/res-public/values/public.xml b/colorpicker/src/main/res-public/values/public.xml
index 4d16c2a..cf14680 100644
--- a/colorpicker/src/main/res-public/values/public.xml
+++ b/colorpicker/src/main/res-public/values/public.xml
@@ -1,4 +1,4 @@
<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 -->
+<!-- AUTO-GENERATED FILE. DO NOT MODIFY. public.xml is generated by the generatepublicxml gradle task -->
<public name='dummy' type='id' />
</resources> \ No newline at end of file
diff --git a/core-ui/src/main/kotlin/ca/allanwang/kau/ui/views/CutoutView.kt b/core-ui/src/main/kotlin/ca/allanwang/kau/ui/views/CutoutView.kt
index eeaac6e..f6f7864 100644
--- a/core-ui/src/main/kotlin/ca/allanwang/kau/ui/views/CutoutView.kt
+++ b/core-ui/src/main/kotlin/ca/allanwang/kau/ui/views/CutoutView.kt
@@ -82,7 +82,7 @@ class CutoutView @JvmOverloads constructor(
if (attrs != null) {
val a = context.obtainStyledAttributes(attrs, R.styleable.CutoutView, 0, 0)
if (a.hasValue(R.styleable.CutoutView_font))
- paint.typeface = context.getFont(a.getString(R.styleable.CutoutView_font))
+ paint.typeface = context.getFont(a.getString(R.styleable.CutoutView_font)!!)
foregroundColor = a.getColor(R.styleable.CutoutView_foregroundColor, foregroundColor)
text = a.getString(R.styleable.CutoutView_android_text) ?: text
minHeight = a.getDimension(R.styleable.CutoutView_android_minHeight, minHeight)
diff --git a/core-ui/src/main/res-public/values/public.xml b/core-ui/src/main/res-public/values/public.xml
index 3d0b8d2..dcaa583 100644
--- a/core-ui/src/main/res-public/values/public.xml
+++ b/core-ui/src/main/res-public/values/public.xml
@@ -1,5 +1,5 @@
<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 -->
+<!-- AUTO-GENERATED FILE. DO NOT MODIFY. public.xml is generated by the generatepublicxml gradle task -->
<public name='kau_recycler_detached_background' type='layout' />
<public name='kau_recycler_textslider' type='layout' />
<public name='kau_elastic_recycler_activity' type='layout' />
diff --git a/core/src/main/kotlin/ca/allanwang/kau/email/EmailBuilder.kt b/core/src/main/kotlin/ca/allanwang/kau/email/EmailBuilder.kt
index dbdcf09..2b9d91c 100644
--- a/core/src/main/kotlin/ca/allanwang/kau/email/EmailBuilder.kt
+++ b/core/src/main/kotlin/ca/allanwang/kau/email/EmailBuilder.kt
@@ -51,7 +51,7 @@ class EmailBuilder(val email: String, val subject: String) {
val emailBuilder = StringBuilder()
emailBuilder.append(message).append("\n\n")
if (deviceDetails) {
- val deviceItems = mutableMapOf(
+ val deviceItems = linkedMapOf(
"OS Version" to "${System.getProperty("os.version")} (${Build.VERSION.INCREMENTAL})",
"OS SDK" to Build.VERSION.SDK_INT,
"Device (Manufacturer)" to "${Build.DEVICE} (${Build.MANUFACTURER})",
@@ -72,6 +72,7 @@ class EmailBuilder(val email: String, val subject: String) {
val versionCode = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
appInfo.longVersionCode.toString()
} else {
+ @Suppress("DEPRECATION")
appInfo.versionCode.toString()
}
emailBuilder.append("\nApp: ").append(context.packageName)
diff --git a/core/src/main/kotlin/ca/allanwang/kau/ui/ProgressAnimator.kt b/core/src/main/kotlin/ca/allanwang/kau/ui/ProgressAnimator.kt
index e37e59f..b98a47e 100644
--- a/core/src/main/kotlin/ca/allanwang/kau/ui/ProgressAnimator.kt
+++ b/core/src/main/kotlin/ca/allanwang/kau/ui/ProgressAnimator.kt
@@ -3,7 +3,8 @@ package ca.allanwang.kau.ui
import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.animation.ValueAnimator
-import android.view.animation.Interpolator
+import android.support.annotation.VisibleForTesting
+import ca.allanwang.kau.kotlin.kauRemoveIf
/**
* Created by Allan Wang on 2017-11-10.
@@ -13,77 +14,160 @@ import android.view.animation.Interpolator
* This differs in that everything can be done with simple listeners, which will be bundled
* and added to the backing [ValueAnimator]
*/
-class ProgressAnimator private constructor(private vararg val values: Float) {
+class ProgressAnimator private constructor() : ValueAnimator() {
companion object {
- inline fun ofFloat(crossinline builder: ProgressAnimator.() -> Unit) = ofFloat(0f, 1f) { builder() }
- fun ofFloat(vararg values: Float, builder: ProgressAnimator.() -> Unit) = ProgressAnimator(*values).apply {
- builder()
- build()
+ fun ofFloat(builder: ProgressAnimator.() -> Unit): ProgressAnimator = ProgressAnimator().apply {
+ setFloatValues(0f, 1f)
+ addUpdateListener { apply(it.animatedValue as Float) }
+ addListener(object : AnimatorListenerAdapter() {
+ override fun onAnimationStart(animation: Animator?, isReverse: Boolean) {
+ isCancelled = false
+ startActions.runAll()
+ }
+
+ override fun onAnimationCancel(animation: Animator?) {
+ isCancelled = true
+ cancelActions.runAll()
+ }
+
+ override fun onAnimationEnd(animation: Animator?) {
+ endActions.runAll()
+ isCancelled = false
+ }
+ })
+ apply(builder)
}
+
+ /**
+ * Gets output of a linear function starting at [start] when [progress] is 0 and [end] when [progress] is 1 at point [progress].
+ */
+ fun progress(start: Float, end: Float, progress: Float): Float = start + (end - start) * progress
+
+ fun progress(start: Float, end: Float, progress: Float, min: Float, max: Float): Float = when {
+ min == max -> throw IllegalArgumentException("Progress range cannot be 0 (min == max == $min")
+ progress <= min -> start
+ progress >= max -> end
+ else -> {
+ val trueProgress = (progress - min) / (max - min)
+ start + (end - start) * trueProgress
+ }
+ }
+
}
- private val animators: MutableList<(Float) -> Unit> = mutableListOf()
- private val startActions: MutableList<() -> Unit> = mutableListOf()
- private val endActions: MutableList<() -> Unit> = mutableListOf()
+ private val animators: MutableList<ProgressDisposableAction> = mutableListOf()
+ @VisibleForTesting
+ internal val startActions: MutableList<ProgressDisposableRunnable> = mutableListOf()
+ @VisibleForTesting
+ internal val cancelActions: MutableList<ProgressDisposableRunnable> = mutableListOf()
+ @VisibleForTesting
+ internal val endActions: MutableList<ProgressDisposableRunnable> = mutableListOf()
+ var isCancelled: Boolean = false
+ private set
+
+ val animatorCount get() = animators.size
- var duration: Long = -1L
- var interpolator: Interpolator? = null
/**
- * Add more changes to the [ValueAnimator] before running
+ * Converts an action to a disposable action
*/
- var extraConfigs: ValueAnimator.() -> Unit = {}
+ private fun ProgressAction.asDisposable(): ProgressDisposableAction = { this(it); false }
+
+ private fun ProgressRunnable.asDisposable(): ProgressDisposableRunnable = { this(); false }
/**
- * Range animator. Multiples the range by the current float progress before emission
+ * If [condition] applies, run the animator.
+ * @return [condition]
*/
- fun withAnimator(from: Float, to: Float, animator: (Float) -> Unit) = animators.add {
- val range = to - from
- animator(range * it + from)
+ private fun ProgressAction.runIf(condition: Boolean, progress: Float): Boolean {
+ if (condition) this(progress)
+ return condition
}
- /**
- * Standard animator. Emits progress value as is
- */
- fun withAnimator(animator: (Float) -> Unit) = animators.add(animator)
+ @VisibleForTesting
+ internal fun MutableList<ProgressDisposableRunnable>.runAll() = kauRemoveIf { it() }
- /**
- * Start action to be called once when the animator first begins
- */
- fun withStartAction(action: () -> Unit) = startActions.add(action)
+ @VisibleForTesting
+ internal fun apply(progress: Float) {
+ animators.kauRemoveIf { it(progress) }
+ }
+
+ fun withAnimator(action: ProgressAction) =
+ withDisposableAnimator(action.asDisposable())
/**
- * End action to be called once when the animator ends
+ * Range animator. Multiples the range by the current float progress before emission
*/
- fun withEndAction(action: () -> Unit) = endActions.add(action)
-
- fun build() {
- ValueAnimator.ofFloat(*values).apply {
- if (this@ProgressAnimator.duration > 0L)
- duration = this@ProgressAnimator.duration
- if (this@ProgressAnimator.interpolator != null)
- interpolator = this@ProgressAnimator.interpolator
- addUpdateListener {
- val progress = it.animatedValue as Float
- animators.forEach { it(progress) }
+ fun withAnimator(from: Float, to: Float, action: ProgressAction) =
+ withDisposableAnimator(from, to, action.asDisposable())
+
+ fun withDisposableAnimator(action: ProgressDisposableAction) = animators.add(action)
+
+ fun withDisposableAnimator(from: Float, to: Float, action: ProgressDisposableAction) {
+ if (to != from) {
+ animators.add {
+ action(progress(from, to, it))
}
- addListener(object : AnimatorListenerAdapter() {
- override fun onAnimationStart(animation: Animator?) {
- startActions.forEach { it() }
- }
+ }
+ }
- override fun onAnimationEnd(animation: Animator?) {
- endActions.forEach { it() }
+ fun withRangeAnimator(min: Float, max: Float, start: Float, end: Float, progress: Float, action: ProgressAction) {
+ if (min >= max) {
+ throw IllegalArgumentException("Range animator must have min < max; currently min=$min, max=$max")
+ }
+ withDisposableAnimator {
+ when {
+ it > max -> true
+ it < min -> false
+ else -> {
+ action(progress(start, end, progress, min, max))
+ false
}
+ }
+ }
+ }
- override fun onAnimationCancel(animation: Animator?) {
- endActions.forEach { it() }
- }
- })
- extraConfigs()
- start()
+ fun withPointAnimator(point: Float, action: ProgressAction) {
+ animators.add {
+ action.runIf(it >= point, it)
}
}
-} \ No newline at end of file
+
+ fun withDelayedStartAction(skipCount: Int, action: ProgressAction) {
+ var count = 0
+ animators.add {
+ action.runIf(count++ >= skipCount, it)
+ }
+ }
+
+ /**
+ * Start action to be called once when the animator first begins
+ */
+ fun withStartAction(action: ProgressRunnable) = withDisposableStartAction(action.asDisposable())
+
+ fun withDisposableStartAction(action: ProgressDisposableRunnable) = startActions.add(action)
+
+ fun withCancelAction(action: ProgressRunnable) = withDisposableCancelAction(action.asDisposable())
+
+ fun withDisposableCancelAction(action: ProgressDisposableRunnable) = cancelActions.add(action)
+
+ fun withEndAction(action: ProgressRunnable) = withDisposableEndAction(action.asDisposable())
+
+ fun withDisposableEndAction(action: ProgressDisposableRunnable) = endActions.add(action)
+
+ fun reset() {
+ if (isRunning) cancel()
+ animators.clear()
+ startActions.clear()
+ cancelActions.clear()
+ endActions.clear()
+ isCancelled = false
+ }
+}
+
+private typealias ProgressAction = (Float) -> Unit
+private typealias ProgressDisposableAction = (Float) -> Boolean
+private typealias ProgressRunnable = () -> Unit
+private typealias ProgressDisposableRunnable = () -> Boolean \ No newline at end of file
diff --git a/core/src/main/res-public/values/public.xml b/core/src/main/res-public/values/public.xml
index 485be78..158ab6f 100644
--- a/core/src/main/res-public/values/public.xml
+++ b/core/src/main/res-public/values/public.xml
@@ -1,5 +1,5 @@
<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 -->
+<!-- AUTO-GENERATED FILE. DO NOT MODIFY. public.xml is generated by the generatepublicxml gradle task -->
<public name='Kau' type='style' />
<public name='Kau.Translucent' type='style' />
<public name='kau_activity_horizontal_margin' type='dimen' />
diff --git a/core/src/test/kotlin/ca/allanwang/kau/ui/ProgressAnimatorTest.kt b/core/src/test/kotlin/ca/allanwang/kau/ui/ProgressAnimatorTest.kt
new file mode 100644
index 0000000..60f8680
--- /dev/null
+++ b/core/src/test/kotlin/ca/allanwang/kau/ui/ProgressAnimatorTest.kt
@@ -0,0 +1,75 @@
+package ca.allanwang.kau.ui
+
+import org.junit.Test
+import kotlin.test.assertEquals
+import kotlin.test.assertFalse
+import kotlin.test.assertTrue
+
+class ProgressAnimatorTest {
+
+ private fun ProgressAnimator.test() {
+ startActions.runAll()
+ var value = 0f
+ while (value < 1f) {
+ apply(value)
+ value += 0.05f
+ }
+ apply(1f)
+ endActions.runAll()
+ }
+
+ @Test
+ fun `basic run`() {
+ var i = 0f
+ ProgressAnimator.ofFloat {
+ withAnimator {
+ i = it
+ }
+ }.test()
+ assertEquals(1f, i)
+ }
+
+ @Test
+ fun `start end hooks`() {
+ var i = 0
+ ProgressAnimator.ofFloat {
+ withStartAction { i = 1 }
+ withDisposableAnimator { assertEquals(1, i); true }
+ withEndAction {
+ assertEquals(0, animatorCount, "Disposable animator not removed")
+ i = 2
+ }
+ }.test()
+ assertEquals(2, i)
+ }
+
+ @Test
+ fun `disposable actions`() {
+ var i = 0f
+ ProgressAnimator.ofFloat {
+ withDisposableAnimator {
+ i = if (it < 0.5f) it else 0.5f
+ i > 0.5f
+ }
+ withAnimator {
+ assertEquals(Math.min(it, 0.5f), i)
+ }
+ }.test()
+ }
+
+ @Test
+ fun `point action`() {
+ var called = false
+ var i = 0f
+ ProgressAnimator.ofFloat {
+ withPointAnimator(0.5f) {
+ assertFalse(called)
+ i = it
+ called = true
+ }
+ }.test()
+ assertTrue(called)
+ assertTrue(i > 0.5f)
+ }
+
+} \ No newline at end of file
diff --git a/docs/Changelog.md b/docs/Changelog.md
index a93b4c8..a5f24f4 100644
--- a/docs/Changelog.md
+++ b/docs/Changelog.md
@@ -4,8 +4,9 @@
* Update everything to Android Studio 3.1
* Fix new lint issues (see Migration for resource related methods)
* :adapter: Add more IAdapter functions to help retrieve selections
-* :core: Add deprecation warning to bindView for fragment based extensions; use bindViewResettable instead
+* :core: Deprecate Kotterknife; use kotlin_android_extensions
* :kpref-activity: Fix seekbar increment
+* :core: Make KPref use Set<String> vs StringSet
## v3.7.1
* Update appcompat to 27.1.0
diff --git a/docs/Migration.md b/docs/Migration.md
index ff89f87..fe04270 100644
--- a/docs/Migration.md
+++ b/docs/Migration.md
@@ -11,6 +11,10 @@ Along with the update to support Android Studio 3.1, a lot of changes have occur
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.
+* Kotterknife has been deprecated in favour of `kotlin-android-extensions`. See [official docs](https://kotlinlang.org/docs/tutorials/android-plugin.html#view-binding).
+* `ProgressAnimator` has been completely rewritten to be an extension of `ValueAnimator`.
+This for the most part is not a breaking change, apart from the fact that creating an animator will not start it immediately.
+* KPref has been slightly refactored internally. Preferences backed by `StringSet` can now go back to `Set<String>`
## Deprecate Kotterknife
diff --git a/kpref-activity/src/main/res-public/values/public.xml b/kpref-activity/src/main/res-public/values/public.xml
index bd4f615..b5fb383 100644
--- a/kpref-activity/src/main/res-public/values/public.xml
+++ b/kpref-activity/src/main/res-public/values/public.xml
@@ -1,5 +1,5 @@
<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 -->
+<!-- AUTO-GENERATED FILE. DO NOT MODIFY. public.xml is generated by the generatepublicxml gradle task -->
<public name='kau_pref_barrier' type='id' />
<public name='kau_pref_container' type='id' />
<public name='kau_pref_desc' type='id' />
diff --git a/mediapicker/src/main/res-public/values/public.xml b/mediapicker/src/main/res-public/values/public.xml
index cadfa07..7b8064a 100644
--- a/mediapicker/src/main/res-public/values/public.xml
+++ b/mediapicker/src/main/res-public/values/public.xml
@@ -1,5 +1,5 @@
<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 -->
+<!-- AUTO-GENERATED FILE. DO NOT MODIFY. public.xml is generated by the generatepublicxml gradle task -->
<public name='Kau.MediaPicker' type='style' />
<public name='Kau.MediaPicker.Overlay' type='style' />
<public name='kau_image_minimum_size' type='dimen' />
diff --git a/searchview/src/main/res-public/values/public.xml b/searchview/src/main/res-public/values/public.xml
index 4d16c2a..cf14680 100644
--- a/searchview/src/main/res-public/values/public.xml
+++ b/searchview/src/main/res-public/values/public.xml
@@ -1,4 +1,4 @@
<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 -->
+<!-- AUTO-GENERATED FILE. DO NOT MODIFY. public.xml is generated by the generatepublicxml gradle task -->
<public name='dummy' type='id' />
</resources> \ No newline at end of file