From 7e3cc860e6eeae06c5b48c999c6a76d5002e141a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Bert?= Date: Thu, 9 Nov 2023 09:39:39 +0100 Subject: [PATCH 1/6] Change implementation to ArrayList --- .../core/GestureHandlerOrchestrator.kt | 85 +++++++++---------- 1 file changed, 41 insertions(+), 44 deletions(-) diff --git a/android/src/main/java/com/swmansion/gesturehandler/core/GestureHandlerOrchestrator.kt b/android/src/main/java/com/swmansion/gesturehandler/core/GestureHandlerOrchestrator.kt index d44513339d..ff6885aed3 100644 --- a/android/src/main/java/com/swmansion/gesturehandler/core/GestureHandlerOrchestrator.kt +++ b/android/src/main/java/com/swmansion/gesturehandler/core/GestureHandlerOrchestrator.kt @@ -7,6 +7,7 @@ import android.view.View import android.view.ViewGroup import android.widget.EditText import java.util.* +import kotlin.collections.ArrayList class GestureHandlerOrchestrator( private val wrapperView: ViewGroup, @@ -19,13 +20,10 @@ class GestureHandlerOrchestrator( * traversing view hierarchy and looking for gesture handlers. */ var minimumAlphaForTraversal = DEFAULT_MIN_ALPHA_FOR_TRAVERSAL - - private val gestureHandlers = arrayOfNulls?>(SIMULTANEOUS_GESTURE_HANDLER_LIMIT) - private val awaitingHandlers = arrayOfNulls?>(SIMULTANEOUS_GESTURE_HANDLER_LIMIT) - private val preparedHandlers = arrayOfNulls?>(SIMULTANEOUS_GESTURE_HANDLER_LIMIT) - private val handlersToCancel = arrayOfNulls?>(SIMULTANEOUS_GESTURE_HANDLER_LIMIT) - private var gestureHandlersCount = 0 - private var awaitingHandlersCount = 0 + private val gestureHandlers = arrayListOf?>() + private val awaitingHandlers = arrayListOf?>() + private val preparedHandlers = arrayListOf?>() + private val handlersToCancel = arrayListOf?>() private var isHandlingTouch = false private var handlingChangeSemaphore = 0 private var finishedHandlersCleanupScheduled = false @@ -60,9 +58,9 @@ class GestureHandlerOrchestrator( } } - private inline fun compactHandlersIf(handlers: Array?>, count: Int, predicate: (handler: GestureHandler<*>?) -> Boolean): Int { + private inline fun compactHandlersIf(handlers: ArrayList?>, predicate: (handler: GestureHandler<*>?) -> Boolean): Int { var out = 0 - for (i in 0 until count) { + for (i in 0 until handlers.size) { if (predicate(handlers[i])) { handlers[out++] = handlers[i] } @@ -72,7 +70,7 @@ class GestureHandlerOrchestrator( private fun cleanupFinishedHandlers() { var shouldCleanEmptyCells = false - for (i in gestureHandlersCount - 1 downTo 0) { + for (i in gestureHandlers.size - 1 downTo 0) { val handler = gestureHandlers[i]!! if (isFinished(handler.state) && !handler.isAwaiting) { gestureHandlers[i] = null @@ -85,18 +83,17 @@ class GestureHandlerOrchestrator( } } } + if (shouldCleanEmptyCells) { - gestureHandlersCount = compactHandlersIf(gestureHandlers, gestureHandlersCount) { handler -> - handler != null - } + gestureHandlers.removeAll(listOf(null)) } + finishedHandlersCleanupScheduled = false } private fun hasOtherHandlerToWaitFor(handler: GestureHandler<*>): Boolean { - for (i in 0 until gestureHandlersCount) { - val otherHandler = gestureHandlers[i]!! - if (!isFinished(otherHandler.state) && shouldHandlerWaitForOther(handler, otherHandler)) { + for (otherHandler in gestureHandlers) { + if (!isFinished(otherHandler!!.state) && shouldHandlerWaitForOther(handler, otherHandler)) { return true } } @@ -115,9 +112,11 @@ class GestureHandlerOrchestrator( } private fun cleanupAwaitingHandlers() { - awaitingHandlersCount = compactHandlersIf(awaitingHandlers, awaitingHandlersCount) { handler -> + val awaitingHandlersCount = compactHandlersIf(awaitingHandlers) { handler -> handler!!.isAwaiting } + + awaitingHandlers.subList(awaitingHandlersCount, awaitingHandlers.size).clear() } /*package*/ @@ -125,8 +124,7 @@ class GestureHandlerOrchestrator( handlingChangeSemaphore += 1 if (isFinished(newState)) { // if there were handlers awaiting completion of this handler, we can trigger active state - for (i in 0 until awaitingHandlersCount) { - val otherHandler = awaitingHandlers[i] + for (otherHandler in awaitingHandlers) { if (shouldHandlerWaitForOther(otherHandler!!, handler)) { if (newState == GestureHandler.STATE_END) { // gesture has ended, we need to kill the awaiting handler @@ -182,20 +180,21 @@ class GestureHandlerOrchestrator( shouldResetProgress = true activationIndex = this@GestureHandlerOrchestrator.activationIndex++ } - var toCancelCount = 0 + // Cancel all handlers that are required to be cancel upon current handler's activation - for (i in 0 until gestureHandlersCount) { - val otherHandler = gestureHandlers[i]!! - if (shouldHandlerBeCancelledBy(otherHandler, handler)) { - handlersToCancel[toCancelCount++] = otherHandler + for (otherHandler in gestureHandlers) { + if (shouldHandlerBeCancelledBy(otherHandler!!, handler)) { + handlersToCancel.add(otherHandler) } } - for (i in toCancelCount - 1 downTo 0) { + for (i in handlersToCancel.size - 1 downTo 0) { handlersToCancel[i]!!.cancel() } + handlersToCancel.clear() + // Clear all awaiting handlers waiting for the current handler to fail - for (i in awaitingHandlersCount - 1 downTo 0) { + for (i in awaitingHandlers.size - 1 downTo 0) { val otherHandler = awaitingHandlers[i]!! if (shouldHandlerBeCancelledBy(otherHandler, handler)) { otherHandler.cancel() @@ -218,31 +217,30 @@ class GestureHandlerOrchestrator( private fun deliverEventToGestureHandlers(event: MotionEvent) { // Copy handlers to "prepared handlers" array, because the list of active handlers can change // as a result of state updates - val handlersCount = gestureHandlersCount + preparedHandlers.clear() + preparedHandlers.addAll(gestureHandlers) - gestureHandlers.copyInto(preparedHandlers, 0, 0, handlersCount) // We want to deliver events to active handlers first in order of their activation (handlers // that activated first will first get event delivered). Otherwise we deliver events in the // order in which handlers has been added ("most direct" children goes first). Therefore we rely // on Arrays.sort providing a stable sort (as children are registered in order in which they // should be tested) - preparedHandlers.sortWith(handlersComparator, 0, handlersCount) - for (i in 0 until handlersCount) { - deliverEventToGestureHandler(preparedHandlers[i]!!, event) + preparedHandlers.sortWith(handlersComparator) + for (handler in preparedHandlers) { + deliverEventToGestureHandler(handler!!, event) } } private fun cancelAll() { - for (i in awaitingHandlersCount - 1 downTo 0) { + for (i in awaitingHandlers.size - 1 downTo 0) { awaitingHandlers[i]!!.cancel() } // Copy handlers to "prepared handlers" array, because the list of active handlers can change // as a result of state updates - val handlersCount = gestureHandlersCount - for (i in 0 until handlersCount) { - preparedHandlers[i] = gestureHandlers[i] - } - for (i in handlersCount - 1 downTo 0) { + preparedHandlers.clear() + preparedHandlers.addAll(gestureHandlers) + + for (i in gestureHandlers.size - 1 downTo 0) { preparedHandlers[i]!!.cancel() } } @@ -399,13 +397,13 @@ class GestureHandlerOrchestrator( } private fun addAwaitingHandler(handler: GestureHandler<*>) { - for (i in 0 until awaitingHandlersCount) { + for (i in 0 until awaitingHandlers.size) { if (awaitingHandlers[i] === handler) { return } } - check(awaitingHandlersCount < awaitingHandlers.size) { "Too many recognizers" } - awaitingHandlers[awaitingHandlersCount++] = handler + + awaitingHandlers.add(handler) with(handler) { isAwaiting = true activationIndex = this@GestureHandlerOrchestrator.activationIndex++ @@ -413,13 +411,13 @@ class GestureHandlerOrchestrator( } private fun recordHandlerIfNotPresent(handler: GestureHandler<*>, view: View) { - for (i in 0 until gestureHandlersCount) { + for (i in 0 until gestureHandlers.size) { if (gestureHandlers[i] === handler) { return } } - check(gestureHandlersCount < gestureHandlers.size) { "Too many recognizers" } - gestureHandlers[gestureHandlersCount++] = handler + + gestureHandlers.add(handler) handler.isActive = false handler.isAwaiting = false handler.activationIndex = Int.MAX_VALUE @@ -608,7 +606,6 @@ class GestureHandlerOrchestrator( companion object { // The limit doesn't necessarily need to exists, it was just simpler to implement it that way // it is also more allocation-wise efficient to have a fixed limit - private const val SIMULTANEOUS_GESTURE_HANDLER_LIMIT = 20 // Be default fully transparent views can receive touch private const val DEFAULT_MIN_ALPHA_FOR_TRAVERSAL = 0f From 6957bb1387ac38cfad1c24e4c5b5e63333f7c28f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Bert?= Date: Tue, 14 Nov 2023 08:44:37 +0100 Subject: [PATCH 2/6] Some changes in orchestrator --- .../core/GestureHandlerOrchestrator.kt | 59 ++++++++----------- 1 file changed, 24 insertions(+), 35 deletions(-) diff --git a/android/src/main/java/com/swmansion/gesturehandler/core/GestureHandlerOrchestrator.kt b/android/src/main/java/com/swmansion/gesturehandler/core/GestureHandlerOrchestrator.kt index ff6885aed3..e0c88730d5 100644 --- a/android/src/main/java/com/swmansion/gesturehandler/core/GestureHandlerOrchestrator.kt +++ b/android/src/main/java/com/swmansion/gesturehandler/core/GestureHandlerOrchestrator.kt @@ -20,10 +20,10 @@ class GestureHandlerOrchestrator( * traversing view hierarchy and looking for gesture handlers. */ var minimumAlphaForTraversal = DEFAULT_MIN_ALPHA_FOR_TRAVERSAL - private val gestureHandlers = arrayListOf?>() - private val awaitingHandlers = arrayListOf?>() - private val preparedHandlers = arrayListOf?>() - private val handlersToCancel = arrayListOf?>() + private val gestureHandlers = arrayListOf>() + private val awaitingHandlers = arrayListOf>() + private val preparedHandlers = arrayListOf>() + private val handlersToCancel = arrayListOf>() private var isHandlingTouch = false private var handlingChangeSemaphore = 0 private var finishedHandlersCleanupScheduled = false @@ -58,7 +58,7 @@ class GestureHandlerOrchestrator( } } - private inline fun compactHandlersIf(handlers: ArrayList?>, predicate: (handler: GestureHandler<*>?) -> Boolean): Int { + private inline fun compactHandlersIf(handlers: ArrayList>, predicate: (handler: GestureHandler<*>?) -> Boolean): Int { var out = 0 for (i in 0 until handlers.size) { if (predicate(handlers[i])) { @@ -69,12 +69,9 @@ class GestureHandlerOrchestrator( } private fun cleanupFinishedHandlers() { - var shouldCleanEmptyCells = false - for (i in gestureHandlers.size - 1 downTo 0) { - val handler = gestureHandlers[i]!! + for (handler in gestureHandlers.reversed()) { if (isFinished(handler.state) && !handler.isAwaiting) { - gestureHandlers[i] = null - shouldCleanEmptyCells = true + gestureHandlers.remove(handler) handler.reset() handler.apply { isActive = false @@ -84,16 +81,12 @@ class GestureHandlerOrchestrator( } } - if (shouldCleanEmptyCells) { - gestureHandlers.removeAll(listOf(null)) - } - finishedHandlersCleanupScheduled = false } private fun hasOtherHandlerToWaitFor(handler: GestureHandler<*>): Boolean { for (otherHandler in gestureHandlers) { - if (!isFinished(otherHandler!!.state) && shouldHandlerWaitForOther(handler, otherHandler)) { + if (!isFinished(otherHandler.state) && shouldHandlerWaitForOther(handler, otherHandler)) { return true } } @@ -125,7 +118,7 @@ class GestureHandlerOrchestrator( if (isFinished(newState)) { // if there were handlers awaiting completion of this handler, we can trigger active state for (otherHandler in awaitingHandlers) { - if (shouldHandlerWaitForOther(otherHandler!!, handler)) { + if (shouldHandlerWaitForOther(otherHandler, handler)) { if (newState == GestureHandler.STATE_END) { // gesture has ended, we need to kill the awaiting handler otherHandler.cancel() @@ -183,19 +176,19 @@ class GestureHandlerOrchestrator( // Cancel all handlers that are required to be cancel upon current handler's activation for (otherHandler in gestureHandlers) { - if (shouldHandlerBeCancelledBy(otherHandler!!, handler)) { + if (shouldHandlerBeCancelledBy(otherHandler, handler)) { handlersToCancel.add(otherHandler) } } - for (i in handlersToCancel.size - 1 downTo 0) { - handlersToCancel[i]!!.cancel() + + for (otherHandler in handlersToCancel.reversed()) { + otherHandler.cancel() } handlersToCancel.clear() // Clear all awaiting handlers waiting for the current handler to fail - for (i in awaitingHandlers.size - 1 downTo 0) { - val otherHandler = awaitingHandlers[i]!! + for (otherHandler in awaitingHandlers.reversed()) { if (shouldHandlerBeCancelledBy(otherHandler, handler)) { otherHandler.cancel() otherHandler.isAwaiting = false @@ -227,21 +220,21 @@ class GestureHandlerOrchestrator( // should be tested) preparedHandlers.sortWith(handlersComparator) for (handler in preparedHandlers) { - deliverEventToGestureHandler(handler!!, event) + deliverEventToGestureHandler(handler, event) } } private fun cancelAll() { - for (i in awaitingHandlers.size - 1 downTo 0) { - awaitingHandlers[i]!!.cancel() + for (handler in awaitingHandlers.reversed()) { + handler.cancel() } // Copy handlers to "prepared handlers" array, because the list of active handlers can change // as a result of state updates preparedHandlers.clear() preparedHandlers.addAll(gestureHandlers) - for (i in gestureHandlers.size - 1 downTo 0) { - preparedHandlers[i]!!.cancel() + for (handler in gestureHandlers.reversed()) { + handler.cancel() } } @@ -323,7 +316,7 @@ class GestureHandlerOrchestrator( return parent === wrapperView } - fun isAnyHandlerActive() = gestureHandlers.any { it?.state == GestureHandler.STATE_ACTIVE } + fun isAnyHandlerActive() = gestureHandlers.any { it.state == GestureHandler.STATE_ACTIVE } /** * Transforms an event in the coordinates of wrapperView into the coordinate space of the received view. @@ -397,10 +390,8 @@ class GestureHandlerOrchestrator( } private fun addAwaitingHandler(handler: GestureHandler<*>) { - for (i in 0 until awaitingHandlers.size) { - if (awaitingHandlers[i] === handler) { - return - } + if (awaitingHandlers.contains(handler)) { + return } awaitingHandlers.add(handler) @@ -411,10 +402,8 @@ class GestureHandlerOrchestrator( } private fun recordHandlerIfNotPresent(handler: GestureHandler<*>, view: View) { - for (i in 0 until gestureHandlers.size) { - if (gestureHandlers[i] === handler) { - return - } + if (gestureHandlers.contains(handler)) { + return } gestureHandlers.add(handler) From 93c8fb7a846ebf45c517c184474b2806b05008be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Bert?= Date: Tue, 14 Nov 2023 09:08:04 +0100 Subject: [PATCH 3/6] Remove compactHandlersIf --- .../core/GestureHandlerOrchestrator.kt | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/android/src/main/java/com/swmansion/gesturehandler/core/GestureHandlerOrchestrator.kt b/android/src/main/java/com/swmansion/gesturehandler/core/GestureHandlerOrchestrator.kt index e0c88730d5..dabc163e21 100644 --- a/android/src/main/java/com/swmansion/gesturehandler/core/GestureHandlerOrchestrator.kt +++ b/android/src/main/java/com/swmansion/gesturehandler/core/GestureHandlerOrchestrator.kt @@ -7,7 +7,6 @@ import android.view.View import android.view.ViewGroup import android.widget.EditText import java.util.* -import kotlin.collections.ArrayList class GestureHandlerOrchestrator( private val wrapperView: ViewGroup, @@ -58,16 +57,6 @@ class GestureHandlerOrchestrator( } } - private inline fun compactHandlersIf(handlers: ArrayList>, predicate: (handler: GestureHandler<*>?) -> Boolean): Int { - var out = 0 - for (i in 0 until handlers.size) { - if (predicate(handlers[i])) { - handlers[out++] = handlers[i] - } - } - return out - } - private fun cleanupFinishedHandlers() { for (handler in gestureHandlers.reversed()) { if (isFinished(handler.state) && !handler.isAwaiting) { @@ -105,11 +94,7 @@ class GestureHandlerOrchestrator( } private fun cleanupAwaitingHandlers() { - val awaitingHandlersCount = compactHandlersIf(awaitingHandlers) { handler -> - handler!!.isAwaiting - } - - awaitingHandlers.subList(awaitingHandlersCount, awaitingHandlers.size).clear() + awaitingHandlers.removeAll { !it.isAwaiting } } /*package*/ From 892c0950ce4331712aa5340ee8bdf2922777096f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Bert?= Date: Wed, 15 Nov 2023 14:52:41 +0100 Subject: [PATCH 4/6] Change how handlers are removed --- .../gesturehandler/core/GestureHandlerOrchestrator.kt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/android/src/main/java/com/swmansion/gesturehandler/core/GestureHandlerOrchestrator.kt b/android/src/main/java/com/swmansion/gesturehandler/core/GestureHandlerOrchestrator.kt index dabc163e21..edeeedb868 100644 --- a/android/src/main/java/com/swmansion/gesturehandler/core/GestureHandlerOrchestrator.kt +++ b/android/src/main/java/com/swmansion/gesturehandler/core/GestureHandlerOrchestrator.kt @@ -58,9 +58,8 @@ class GestureHandlerOrchestrator( } private fun cleanupFinishedHandlers() { - for (handler in gestureHandlers.reversed()) { + for (handler in gestureHandlers.asReversed()) { if (isFinished(handler.state) && !handler.isAwaiting) { - gestureHandlers.remove(handler) handler.reset() handler.apply { isActive = false @@ -70,6 +69,8 @@ class GestureHandlerOrchestrator( } } + gestureHandlers.removeAll { isFinished(it.state) && !it.isAwaiting } + finishedHandlersCleanupScheduled = false } From da2c4211da791d8b347723d90e77a0a7a4db29b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Bert?= Date: Thu, 16 Nov 2023 16:58:03 +0100 Subject: [PATCH 5/6] Remove handlersToCancel --- .../core/GestureHandlerOrchestrator.kt | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/android/src/main/java/com/swmansion/gesturehandler/core/GestureHandlerOrchestrator.kt b/android/src/main/java/com/swmansion/gesturehandler/core/GestureHandlerOrchestrator.kt index edeeedb868..e8dcac983f 100644 --- a/android/src/main/java/com/swmansion/gesturehandler/core/GestureHandlerOrchestrator.kt +++ b/android/src/main/java/com/swmansion/gesturehandler/core/GestureHandlerOrchestrator.kt @@ -22,7 +22,6 @@ class GestureHandlerOrchestrator( private val gestureHandlers = arrayListOf>() private val awaitingHandlers = arrayListOf>() private val preparedHandlers = arrayListOf>() - private val handlersToCancel = arrayListOf>() private var isHandlingTouch = false private var handlingChangeSemaphore = 0 private var finishedHandlersCleanupScheduled = false @@ -160,19 +159,12 @@ class GestureHandlerOrchestrator( activationIndex = this@GestureHandlerOrchestrator.activationIndex++ } - // Cancel all handlers that are required to be cancel upon current handler's activation - for (otherHandler in gestureHandlers) { + for (otherHandler in gestureHandlers.reversed()) { if (shouldHandlerBeCancelledBy(otherHandler, handler)) { - handlersToCancel.add(otherHandler) + otherHandler.cancel() } } - for (otherHandler in handlersToCancel.reversed()) { - otherHandler.cancel() - } - - handlersToCancel.clear() - // Clear all awaiting handlers waiting for the current handler to fail for (otherHandler in awaitingHandlers.reversed()) { if (shouldHandlerBeCancelledBy(otherHandler, handler)) { From 3d3d241d71ddaff3482299766d537002570b704f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Bert?= Date: Mon, 20 Nov 2023 08:09:19 +0100 Subject: [PATCH 6/6] reversed to asReversed --- .../swmansion/gesturehandler/core/GestureHandlerOrchestrator.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/src/main/java/com/swmansion/gesturehandler/core/GestureHandlerOrchestrator.kt b/android/src/main/java/com/swmansion/gesturehandler/core/GestureHandlerOrchestrator.kt index e8dcac983f..b92c87b9e3 100644 --- a/android/src/main/java/com/swmansion/gesturehandler/core/GestureHandlerOrchestrator.kt +++ b/android/src/main/java/com/swmansion/gesturehandler/core/GestureHandlerOrchestrator.kt @@ -159,7 +159,7 @@ class GestureHandlerOrchestrator( activationIndex = this@GestureHandlerOrchestrator.activationIndex++ } - for (otherHandler in gestureHandlers.reversed()) { + for (otherHandler in gestureHandlers.asReversed()) { if (shouldHandlerBeCancelledBy(otherHandler, handler)) { otherHandler.cancel() }