-
-
Notifications
You must be signed in to change notification settings - Fork 27
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Create debounce and update searchview (#27)
* Prepare version * Create debounce base * Add debouncer and fix transition crash * Add debounce docs * Update links * Update searchview docs * Test without a ref * Add links to core components * Update links * Update to bullet points * Test core md * Test slash * Test slash * Specify implemented dependencies
- Loading branch information
Showing
13 changed files
with
329 additions
and
122 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
124 changes: 124 additions & 0 deletions
124
core/src/main/kotlin/ca/allanwang/kau/kotlin/Debouncer.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
package ca.allanwang.kau.kotlin | ||
|
||
import ca.allanwang.kau.logging.KL | ||
import java.util.concurrent.Executors | ||
import java.util.concurrent.TimeUnit | ||
|
||
/** | ||
* Created by Allan Wang on 2017-08-05. | ||
* | ||
* Thread safe function wrapper to allow for debouncing | ||
* With reference to <a href="https://stackoverflow.com/a/20978973/4407321">Stack Overflow</a> | ||
*/ | ||
|
||
/** | ||
* The debouncer base | ||
* Implements everything except for the callback, | ||
* as the number of variables is different between implementations | ||
* You may still use this without extending it, but you'll have to pass a callback each time | ||
*/ | ||
open class Debouncer(var interval: Long) { | ||
private val sched = Executors.newScheduledThreadPool(1) | ||
private var task: DebounceTask? = null | ||
|
||
/** | ||
* Generic invocation to pass a callback to the new task | ||
* Pass a new callback for the task | ||
* If another task is pending, it will be invalidated | ||
*/ | ||
operator fun invoke(callback: () -> Unit) { | ||
synchronized(this) { | ||
task?.invalidate() | ||
val newTask = DebounceTask(callback) | ||
KL.v("Debouncer task created: $newTask in $this") | ||
sched.schedule(newTask, interval, TimeUnit.MILLISECONDS) | ||
task = newTask | ||
} | ||
} | ||
|
||
/** | ||
* Call to cancel all pending requests and shutdown the thread pool | ||
* The debouncer cannot be used after this | ||
*/ | ||
fun terminate() = sched.shutdownNow() | ||
|
||
/** | ||
* Invalidate any pending tasks | ||
*/ | ||
fun cancel() { | ||
synchronized(this) { | ||
if (task != null) KL.v("Debouncer cancelled for $task in $this") | ||
task?.invalidate() | ||
task = null | ||
} | ||
} | ||
|
||
} | ||
|
||
/* | ||
* Helper extensions for functions with 0 to 3 arguments | ||
*/ | ||
|
||
/** | ||
* The debounced task | ||
* Holds a callback to execute if the time has come and it is still valid | ||
* All methods can be viewed as synchronous as the invocation is synchronous | ||
*/ | ||
private class DebounceTask(inline val callback: () -> Unit) : Runnable { | ||
private var valid = true | ||
|
||
fun invalidate() { | ||
valid = false | ||
} | ||
|
||
override fun run() { | ||
if (!valid) return | ||
valid = false | ||
KL.v("Debouncer task executed $this") | ||
try { | ||
callback() | ||
} catch (e: Exception) { | ||
KL.e(e, "DebouncerTask exception") | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* A zero input debouncer | ||
*/ | ||
class Debouncer0 internal constructor(interval: Long, val callback: () -> Unit) : Debouncer(interval) { | ||
operator fun invoke() = invoke(callback) | ||
} | ||
|
||
fun debounce(interval: Long, callback: () -> Unit) = Debouncer0(interval, callback) | ||
fun (() -> Unit).debounce(interval: Long) = debounce(interval, this) | ||
|
||
/** | ||
* A one argument input debouncer | ||
*/ | ||
class Debouncer1<T> internal constructor(interval: Long, val callback: (T) -> Unit) : Debouncer(interval) { | ||
operator fun invoke(key: T) = invoke { callback(key) } | ||
} | ||
|
||
fun <T> debounce(interval: Long, callback: (T) -> Unit) = Debouncer1(interval, callback) | ||
fun <T> ((T) -> Unit).debounce(interval: Long) = debounce(interval, this) | ||
|
||
/** | ||
* A two argument input debouncer | ||
*/ | ||
class Debouncer2<T, V> internal constructor(interval: Long, val callback: (T, V) -> Unit) : Debouncer(interval) { | ||
operator fun invoke(arg0: T, arg1: V) = invoke { callback(arg0, arg1) } | ||
} | ||
|
||
fun <T, V> debounce(interval: Long, callback: (T, V) -> Unit) = Debouncer2(interval, callback) | ||
fun <T, V> ((T, V) -> Unit).debounce(interval: Long) = debounce(interval, this) | ||
|
||
/** | ||
* A three argument input debouncer | ||
*/ | ||
class Debouncer3<T, U, V> internal constructor(interval: Long, val callback: (T, U, V) -> Unit) : Debouncer(interval) { | ||
operator fun invoke(arg0: T, arg1: U, arg2: V) = invoke { callback(arg0, arg1, arg2) } | ||
} | ||
|
||
fun <T, U, V> debounce(interval: Long, callback: ((T, U, V) -> Unit)) = Debouncer3(interval, callback) | ||
fun <T, U, V> ((T, U, V) -> Unit).debounce(interval: Long) = debounce(interval, this) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
53 changes: 53 additions & 0 deletions
53
core/src/test/kotlin/ca/allanwang/kau/kotlin/DebounceTest.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
package ca.allanwang.kau.kotlin | ||
|
||
import org.jetbrains.anko.doAsync | ||
import org.junit.Test | ||
import kotlin.test.assertEquals | ||
|
||
/** | ||
* Created by Allan Wang on 2017-08-05. | ||
*/ | ||
class DebounceTest { | ||
|
||
@Test | ||
fun basic() { | ||
var i = 0 | ||
val debounce = debounce(20) { i++ } | ||
assertEquals(0, i, "i should start as 0") | ||
(1..5).forEach { debounce() } | ||
Thread.sleep(50) | ||
assertEquals(1, i, "Debouncing did not cancel previous requests") | ||
} | ||
|
||
@Test | ||
fun basicExtension() { | ||
var i = 0 | ||
val increment: () -> Unit = { i++ } | ||
(1..5).forEach { increment() } | ||
assertEquals(5, i, "i should be 5") | ||
val debounce = increment.debounce(50) | ||
(6..10).forEach { debounce() } | ||
assertEquals(5, i, "i should not have changed") | ||
Thread.sleep(100) | ||
assertEquals(6, i, "i should increment to 6") | ||
} | ||
|
||
@Test | ||
fun multipleDebounces() { | ||
var i = 0 | ||
val debounce = debounce<Int>(10) { i += it } | ||
debounce(1) //ignore -> i = 0 | ||
Thread.sleep(5) | ||
assertEquals(0, i) | ||
debounce(2) //accept -> i = 2 | ||
Thread.sleep(15) | ||
assertEquals(2, i) | ||
debounce(4) //ignore -> i = 2 | ||
Thread.sleep(5) | ||
assertEquals(2, i) | ||
debounce(8) //accept -> i = 10 | ||
Thread.sleep(15) | ||
assertEquals(10, i) | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.