Skip to content

Commit 25ba4b8

Browse files
authored
Remove Gesture Handler limits on Android (#2672)
## Description While looking at PRs I've stumbled upon [this one](#2664). Turns out that `SIMULTANEOUS_GESTURE_HANDLER_LIMIT` does not refer to simultaneous gestures, but to all gesture handlers present in app. The upper limit shouldn't exist - this PR aims to remove it. ## Test plan Tested on example app.
1 parent 73533a2 commit 25ba4b8

File tree

1 file changed

+36
-72
lines changed

1 file changed

+36
-72
lines changed

android/src/main/java/com/swmansion/gesturehandler/core/GestureHandlerOrchestrator.kt

Lines changed: 36 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,9 @@ class GestureHandlerOrchestrator(
1919
* traversing view hierarchy and looking for gesture handlers.
2020
*/
2121
var minimumAlphaForTraversal = DEFAULT_MIN_ALPHA_FOR_TRAVERSAL
22-
23-
private val gestureHandlers = arrayOfNulls<GestureHandler<*>?>(SIMULTANEOUS_GESTURE_HANDLER_LIMIT)
24-
private val awaitingHandlers = arrayOfNulls<GestureHandler<*>?>(SIMULTANEOUS_GESTURE_HANDLER_LIMIT)
25-
private val preparedHandlers = arrayOfNulls<GestureHandler<*>?>(SIMULTANEOUS_GESTURE_HANDLER_LIMIT)
26-
private val handlersToCancel = arrayOfNulls<GestureHandler<*>?>(SIMULTANEOUS_GESTURE_HANDLER_LIMIT)
27-
private var gestureHandlersCount = 0
28-
private var awaitingHandlersCount = 0
22+
private val gestureHandlers = arrayListOf<GestureHandler<*>>()
23+
private val awaitingHandlers = arrayListOf<GestureHandler<*>>()
24+
private val preparedHandlers = arrayListOf<GestureHandler<*>>()
2925
private var isHandlingTouch = false
3026
private var handlingChangeSemaphore = 0
3127
private var finishedHandlersCleanupScheduled = false
@@ -60,23 +56,9 @@ class GestureHandlerOrchestrator(
6056
}
6157
}
6258

63-
private inline fun compactHandlersIf(handlers: Array<GestureHandler<*>?>, count: Int, predicate: (handler: GestureHandler<*>?) -> Boolean): Int {
64-
var out = 0
65-
for (i in 0 until count) {
66-
if (predicate(handlers[i])) {
67-
handlers[out++] = handlers[i]
68-
}
69-
}
70-
return out
71-
}
72-
7359
private fun cleanupFinishedHandlers() {
74-
var shouldCleanEmptyCells = false
75-
for (i in gestureHandlersCount - 1 downTo 0) {
76-
val handler = gestureHandlers[i]!!
60+
for (handler in gestureHandlers.asReversed()) {
7761
if (isFinished(handler.state) && !handler.isAwaiting) {
78-
gestureHandlers[i] = null
79-
shouldCleanEmptyCells = true
8062
handler.reset()
8163
handler.apply {
8264
isActive = false
@@ -85,17 +67,14 @@ class GestureHandlerOrchestrator(
8567
}
8668
}
8769
}
88-
if (shouldCleanEmptyCells) {
89-
gestureHandlersCount = compactHandlersIf(gestureHandlers, gestureHandlersCount) { handler ->
90-
handler != null
91-
}
92-
}
70+
71+
gestureHandlers.removeAll { isFinished(it.state) && !it.isAwaiting }
72+
9373
finishedHandlersCleanupScheduled = false
9474
}
9575

9676
private fun hasOtherHandlerToWaitFor(handler: GestureHandler<*>): Boolean {
97-
for (i in 0 until gestureHandlersCount) {
98-
val otherHandler = gestureHandlers[i]!!
77+
for (otherHandler in gestureHandlers) {
9978
if (!isFinished(otherHandler.state) && shouldHandlerWaitForOther(handler, otherHandler)) {
10079
return true
10180
}
@@ -115,19 +94,16 @@ class GestureHandlerOrchestrator(
11594
}
11695

11796
private fun cleanupAwaitingHandlers() {
118-
awaitingHandlersCount = compactHandlersIf(awaitingHandlers, awaitingHandlersCount) { handler ->
119-
handler!!.isAwaiting
120-
}
97+
awaitingHandlers.removeAll { !it.isAwaiting }
12198
}
12299

123100
/*package*/
124101
fun onHandlerStateChange(handler: GestureHandler<*>, newState: Int, prevState: Int) {
125102
handlingChangeSemaphore += 1
126103
if (isFinished(newState)) {
127104
// if there were handlers awaiting completion of this handler, we can trigger active state
128-
for (i in 0 until awaitingHandlersCount) {
129-
val otherHandler = awaitingHandlers[i]
130-
if (shouldHandlerWaitForOther(otherHandler!!, handler)) {
105+
for (otherHandler in awaitingHandlers) {
106+
if (shouldHandlerWaitForOther(otherHandler, handler)) {
131107
if (newState == GestureHandler.STATE_END) {
132108
// gesture has ended, we need to kill the awaiting handler
133109
otherHandler.cancel()
@@ -182,21 +158,15 @@ class GestureHandlerOrchestrator(
182158
shouldResetProgress = true
183159
activationIndex = this@GestureHandlerOrchestrator.activationIndex++
184160
}
185-
var toCancelCount = 0
186-
// Cancel all handlers that are required to be cancel upon current handler's activation
187-
for (i in 0 until gestureHandlersCount) {
188-
val otherHandler = gestureHandlers[i]!!
161+
162+
for (otherHandler in gestureHandlers.asReversed()) {
189163
if (shouldHandlerBeCancelledBy(otherHandler, handler)) {
190-
handlersToCancel[toCancelCount++] = otherHandler
164+
otherHandler.cancel()
191165
}
192166
}
193-
for (i in toCancelCount - 1 downTo 0) {
194-
handlersToCancel[i]!!.cancel()
195-
}
196167

197168
// Clear all awaiting handlers waiting for the current handler to fail
198-
for (i in awaitingHandlersCount - 1 downTo 0) {
199-
val otherHandler = awaitingHandlers[i]!!
169+
for (otherHandler in awaitingHandlers.reversed()) {
200170
if (shouldHandlerBeCancelledBy(otherHandler, handler)) {
201171
otherHandler.cancel()
202172
otherHandler.isAwaiting = false
@@ -218,32 +188,31 @@ class GestureHandlerOrchestrator(
218188
private fun deliverEventToGestureHandlers(event: MotionEvent) {
219189
// Copy handlers to "prepared handlers" array, because the list of active handlers can change
220190
// as a result of state updates
221-
val handlersCount = gestureHandlersCount
191+
preparedHandlers.clear()
192+
preparedHandlers.addAll(gestureHandlers)
222193

223-
gestureHandlers.copyInto(preparedHandlers, 0, 0, handlersCount)
224194
// We want to deliver events to active handlers first in order of their activation (handlers
225195
// that activated first will first get event delivered). Otherwise we deliver events in the
226196
// order in which handlers has been added ("most direct" children goes first). Therefore we rely
227197
// on Arrays.sort providing a stable sort (as children are registered in order in which they
228198
// should be tested)
229-
preparedHandlers.sortWith(handlersComparator, 0, handlersCount)
230-
for (i in 0 until handlersCount) {
231-
deliverEventToGestureHandler(preparedHandlers[i]!!, event)
199+
preparedHandlers.sortWith(handlersComparator)
200+
for (handler in preparedHandlers) {
201+
deliverEventToGestureHandler(handler, event)
232202
}
233203
}
234204

235205
private fun cancelAll() {
236-
for (i in awaitingHandlersCount - 1 downTo 0) {
237-
awaitingHandlers[i]!!.cancel()
206+
for (handler in awaitingHandlers.reversed()) {
207+
handler.cancel()
238208
}
239209
// Copy handlers to "prepared handlers" array, because the list of active handlers can change
240210
// as a result of state updates
241-
val handlersCount = gestureHandlersCount
242-
for (i in 0 until handlersCount) {
243-
preparedHandlers[i] = gestureHandlers[i]
244-
}
245-
for (i in handlersCount - 1 downTo 0) {
246-
preparedHandlers[i]!!.cancel()
211+
preparedHandlers.clear()
212+
preparedHandlers.addAll(gestureHandlers)
213+
214+
for (handler in gestureHandlers.reversed()) {
215+
handler.cancel()
247216
}
248217
}
249218

@@ -325,7 +294,7 @@ class GestureHandlerOrchestrator(
325294
return parent === wrapperView
326295
}
327296

328-
fun isAnyHandlerActive() = gestureHandlers.any { it?.state == GestureHandler.STATE_ACTIVE }
297+
fun isAnyHandlerActive() = gestureHandlers.any { it.state == GestureHandler.STATE_ACTIVE }
329298

330299
/**
331300
* Transforms an event in the coordinates of wrapperView into the coordinate space of the received view.
@@ -399,27 +368,23 @@ class GestureHandlerOrchestrator(
399368
}
400369

401370
private fun addAwaitingHandler(handler: GestureHandler<*>) {
402-
for (i in 0 until awaitingHandlersCount) {
403-
if (awaitingHandlers[i] === handler) {
404-
return
405-
}
371+
if (awaitingHandlers.contains(handler)) {
372+
return
406373
}
407-
check(awaitingHandlersCount < awaitingHandlers.size) { "Too many recognizers" }
408-
awaitingHandlers[awaitingHandlersCount++] = handler
374+
375+
awaitingHandlers.add(handler)
409376
with(handler) {
410377
isAwaiting = true
411378
activationIndex = this@GestureHandlerOrchestrator.activationIndex++
412379
}
413380
}
414381

415382
private fun recordHandlerIfNotPresent(handler: GestureHandler<*>, view: View) {
416-
for (i in 0 until gestureHandlersCount) {
417-
if (gestureHandlers[i] === handler) {
418-
return
419-
}
383+
if (gestureHandlers.contains(handler)) {
384+
return
420385
}
421-
check(gestureHandlersCount < gestureHandlers.size) { "Too many recognizers" }
422-
gestureHandlers[gestureHandlersCount++] = handler
386+
387+
gestureHandlers.add(handler)
423388
handler.isActive = false
424389
handler.isAwaiting = false
425390
handler.activationIndex = Int.MAX_VALUE
@@ -608,7 +573,6 @@ class GestureHandlerOrchestrator(
608573
companion object {
609574
// The limit doesn't necessarily need to exists, it was just simpler to implement it that way
610575
// it is also more allocation-wise efficient to have a fixed limit
611-
private const val SIMULTANEOUS_GESTURE_HANDLER_LIMIT = 20
612576

613577
// Be default fully transparent views can receive touch
614578
private const val DEFAULT_MIN_ALPHA_FOR_TRAVERSAL = 0f

0 commit comments

Comments
 (0)