From e03381b937f19ceeadb3fbd1d6080356b97187b0 Mon Sep 17 00:00:00 2001 From: Daniil Borisovskii Date: Mon, 13 Apr 2020 20:17:38 +0300 Subject: [PATCH 1/3] Add throttling in `View.setOnRippleClickListener()` implementation to prevent multiple actions during ripple delay --- .../main/java/ru/touchin/extensions/View.kt | 9 +++++-- .../java/ru/touchin/utils/ActionThrottler.kt | 26 +++++++++++++++++++ 2 files changed, 33 insertions(+), 2 deletions(-) create mode 100644 kotlin-extensions/src/main/java/ru/touchin/utils/ActionThrottler.kt diff --git a/kotlin-extensions/src/main/java/ru/touchin/extensions/View.kt b/kotlin-extensions/src/main/java/ru/touchin/extensions/View.kt index d6e2920..41217ce 100644 --- a/kotlin-extensions/src/main/java/ru/touchin/extensions/View.kt +++ b/kotlin-extensions/src/main/java/ru/touchin/extensions/View.kt @@ -2,8 +2,9 @@ package ru.touchin.extensions import android.os.Build import android.view.View +import ru.touchin.utils.ActionThrottler -private const val RIPPLE_EFFECT_DELAY = 150L +const val RIPPLE_EFFECT_DELAY = 150L /** * Sets click listener to view. On click it will call something after delay. @@ -12,7 +13,11 @@ private const val RIPPLE_EFFECT_DELAY = 150L */ fun View.setOnRippleClickListener(listener: () -> Unit) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - setOnClickListener { postDelayed({ if (hasWindowFocus()) listener() }, RIPPLE_EFFECT_DELAY) } + setOnClickListener { + ActionThrottler.throttleAction { + postDelayed({ if (hasWindowFocus()) listener() }, RIPPLE_EFFECT_DELAY) + } + } } else { setOnClickListener { listener() } } diff --git a/kotlin-extensions/src/main/java/ru/touchin/utils/ActionThrottler.kt b/kotlin-extensions/src/main/java/ru/touchin/utils/ActionThrottler.kt new file mode 100644 index 0000000..830e89e --- /dev/null +++ b/kotlin-extensions/src/main/java/ru/touchin/utils/ActionThrottler.kt @@ -0,0 +1,26 @@ +package ru.touchin.utils + +import android.os.SystemClock +import ru.touchin.extensions.RIPPLE_EFFECT_DELAY + +object ActionThrottler { + + private const val DELAY = 2 * RIPPLE_EFFECT_DELAY + private var lastActionTime = 0L + + fun throttleAction(action: () -> Unit): Boolean { + val currentTime = SystemClock.elapsedRealtime() + val diff = currentTime - lastActionTime + + return if (diff >= DELAY) { + lastActionTime = currentTime + action.invoke() + true + } else { + false + } + } + +} + + From 2df5e2e3c37ddd7da1f7010aa47526f9b152c474 Mon Sep 17 00:00:00 2001 From: Daniil Borisovskii Date: Mon, 13 Apr 2020 20:42:12 +0300 Subject: [PATCH 2/3] Add explaining comment in `ActionThrottler` --- .../src/main/java/ru/touchin/utils/ActionThrottler.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/kotlin-extensions/src/main/java/ru/touchin/utils/ActionThrottler.kt b/kotlin-extensions/src/main/java/ru/touchin/utils/ActionThrottler.kt index 830e89e..f0bc035 100644 --- a/kotlin-extensions/src/main/java/ru/touchin/utils/ActionThrottler.kt +++ b/kotlin-extensions/src/main/java/ru/touchin/utils/ActionThrottler.kt @@ -5,6 +5,8 @@ import ru.touchin.extensions.RIPPLE_EFFECT_DELAY object ActionThrottler { + // Multiplied by 2 because in interval after ripple effect finish and before + // action invoking start user may be in time to click and launch action again private const val DELAY = 2 * RIPPLE_EFFECT_DELAY private var lastActionTime = 0L From 8b2c061528fcfbdc3ab85d81afd08e390e753717 Mon Sep 17 00:00:00 2001 From: Daniil Borisovskii Date: Tue, 14 Apr 2020 13:41:51 +0300 Subject: [PATCH 3/3] Fixed comments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit https://github.com/TouchInstinct/RoboSwag/pull/121#discussion_r408021807 – prevention of click again coefficient extracted into a value https://github.com/TouchInstinct/RoboSwag/pull/121#discussion_r408025993 – empty line removed https://github.com/TouchInstinct/RoboSwag/pull/121#discussion_r408027482 – "MS" posfix was added to delay constants --- .../src/main/java/ru/touchin/extensions/View.kt | 4 ++-- .../src/main/java/ru/touchin/utils/ActionThrottler.kt | 11 +++++------ 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/kotlin-extensions/src/main/java/ru/touchin/extensions/View.kt b/kotlin-extensions/src/main/java/ru/touchin/extensions/View.kt index 41217ce..5602cef 100644 --- a/kotlin-extensions/src/main/java/ru/touchin/extensions/View.kt +++ b/kotlin-extensions/src/main/java/ru/touchin/extensions/View.kt @@ -4,7 +4,7 @@ import android.os.Build import android.view.View import ru.touchin.utils.ActionThrottler -const val RIPPLE_EFFECT_DELAY = 150L +const val RIPPLE_EFFECT_DELAY_MS = 150L /** * Sets click listener to view. On click it will call something after delay. @@ -15,7 +15,7 @@ fun View.setOnRippleClickListener(listener: () -> Unit) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { setOnClickListener { ActionThrottler.throttleAction { - postDelayed({ if (hasWindowFocus()) listener() }, RIPPLE_EFFECT_DELAY) + postDelayed({ if (hasWindowFocus()) listener() }, RIPPLE_EFFECT_DELAY_MS) } } } else { diff --git a/kotlin-extensions/src/main/java/ru/touchin/utils/ActionThrottler.kt b/kotlin-extensions/src/main/java/ru/touchin/utils/ActionThrottler.kt index f0bc035..8721e60 100644 --- a/kotlin-extensions/src/main/java/ru/touchin/utils/ActionThrottler.kt +++ b/kotlin-extensions/src/main/java/ru/touchin/utils/ActionThrottler.kt @@ -1,20 +1,21 @@ package ru.touchin.utils import android.os.SystemClock -import ru.touchin.extensions.RIPPLE_EFFECT_DELAY +import ru.touchin.extensions.RIPPLE_EFFECT_DELAY_MS object ActionThrottler { - // Multiplied by 2 because in interval after ripple effect finish and before + // It is necessary because in interval after ripple effect finish and before // action invoking start user may be in time to click and launch action again - private const val DELAY = 2 * RIPPLE_EFFECT_DELAY + private const val PREVENTION_OF_CLICK_AGAIN_COEFFICIENT = 2 + private const val DELAY_MS = PREVENTION_OF_CLICK_AGAIN_COEFFICIENT * RIPPLE_EFFECT_DELAY_MS private var lastActionTime = 0L fun throttleAction(action: () -> Unit): Boolean { val currentTime = SystemClock.elapsedRealtime() val diff = currentTime - lastActionTime - return if (diff >= DELAY) { + return if (diff >= DELAY_MS) { lastActionTime = currentTime action.invoke() true @@ -24,5 +25,3 @@ object ActionThrottler { } } - -