Skip to content

Commit f0b0ead

Browse files
Turn off Gesture Repeat for patterns (#2557)
- Disable Gesture Repeat silently before gesture pattern executors become active - Clear active repeat sessions so pattern playback gestures cannot be captured for repeat - Add targeted repeat and pattern manager coverage for silent disable behavior 🤖 Auto-generated Co-authored-by: Owen McGirr <o.a.mcgirr@gmail.com>
1 parent cd43317 commit f0b0ead

4 files changed

Lines changed: 140 additions & 0 deletions

File tree

app/src/main/java/com/enaboapps/switchify/service/gestures/GestureRepeatManager.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,10 @@ class GestureRepeatManager private constructor() {
155155
clearServiceState(showMessage = false)
156156
}
157157

158+
internal fun turnAutoRepeatOffForGesturePatternStart() {
159+
clearServiceState(showMessage = false)
160+
}
161+
158162
private fun getRepeatDelay(): Long {
159163
val delay = repeatDelayProviderForTesting?.invoke()
160164
?: context?.let {

app/src/main/java/com/enaboapps/switchify/service/gestures/patterns/GesturePatternManager.kt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package com.enaboapps.switchify.service.gestures.patterns
22

33
import android.content.Context
44
import com.enaboapps.switchify.backend.preferences.PreferenceManager
5+
import com.enaboapps.switchify.service.gestures.GestureRepeatManager
56
import java.util.concurrent.ConcurrentHashMap
67
import java.util.concurrent.atomic.AtomicInteger
78

@@ -16,6 +17,7 @@ object GesturePatternManager {
1617
}
1718

1819
fun registerExecutor(executor: GesturePatternExecutor) {
20+
GestureRepeatManager.instance.turnAutoRepeatOffForGesturePatternStart()
1921
synchronized(lock) {
2022
val id = executorIdCounter.incrementAndGet()
2123
activeExecutors[id] = executor
@@ -82,4 +84,11 @@ object GesturePatternManager {
8284
return anyAdvanced
8385
}
8486
}
87+
88+
internal fun resetForTesting() {
89+
synchronized(lock) {
90+
activeExecutors.clear()
91+
executorIdCounter.set(0)
92+
}
93+
}
8594
}

app/src/test/java/com/enaboapps/switchify/service/gestures/GestureRepeatManagerTest.kt

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,33 @@ class GestureRepeatManagerTest {
312312
assertFalse(repeatManager.isRepeatSessionActive())
313313
}
314314

315+
@Test
316+
fun turnAutoRepeatOffForGesturePatternStartClearsWaitingRepeatSilently() {
317+
repeatManager.setAutoRepeatEnabledForTesting(true)
318+
messages.clear()
319+
320+
repeatManager.turnAutoRepeatOffForGesturePatternStart()
321+
322+
assertFalse(repeatManager.isAutoRepeatEnabled())
323+
assertFalse(repeatManager.isWaitingForGesture())
324+
assertEquals(emptyList<Int>(), messages)
325+
}
326+
327+
@Test
328+
fun turnAutoRepeatOffForGesturePatternStartStopsActiveRepeatSilently() {
329+
repeatManager.setAutoRepeatEnabledForTesting(true)
330+
performGestureForRepeat()
331+
messages.clear()
332+
333+
repeatManager.turnAutoRepeatOffForGesturePatternStart()
334+
335+
assertFalse(repeatManager.isAutoRepeatEnabled())
336+
assertFalse(repeatManager.isRepeating())
337+
assertFalse(repeatManager.isRepeatSessionActive())
338+
assertNull(repeatManager.getRepeatedGestureDataForTesting())
339+
assertEquals(emptyList<Int>(), messages)
340+
}
341+
315342
private fun performGestureForRepeat(): GestureData {
316343
val gestureData = testGesture()
317344
repeatManager.onGesturePerformed(gestureData)
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
package com.enaboapps.switchify.service.gestures.patterns
2+
3+
import android.graphics.PointF
4+
import com.enaboapps.switchify.service.gestures.GestureRepeatManager
5+
import com.enaboapps.switchify.service.gestures.data.GestureData
6+
import com.enaboapps.switchify.service.gestures.data.GestureType
7+
import org.junit.After
8+
import org.junit.Assert.assertEquals
9+
import org.junit.Assert.assertFalse
10+
import org.junit.Assert.assertNull
11+
import org.junit.Assert.assertTrue
12+
import org.junit.Before
13+
import org.junit.Test
14+
import sun.misc.Unsafe
15+
16+
class GesturePatternManagerTest {
17+
private val repeatManager = GestureRepeatManager.instance
18+
private val messages = mutableListOf<Int>()
19+
20+
@Before
21+
fun setup() {
22+
repeatManager.resetForTesting()
23+
repeatManager.setSuppressHudForTesting(true)
24+
repeatManager.setMessageRecorderForTesting { messages.add(it) }
25+
messages.clear()
26+
GesturePatternManager.resetForTesting()
27+
}
28+
29+
@After
30+
fun tearDown() {
31+
repeatManager.resetForTesting()
32+
GesturePatternManager.resetForTesting()
33+
}
34+
35+
@Test
36+
fun registerExecutorTurnsGestureRepeatOffWhenWaiting() {
37+
repeatManager.setAutoRepeatEnabledForTesting(true)
38+
val executor = testExecutor()
39+
40+
GesturePatternManager.registerExecutor(executor)
41+
42+
assertFalse(repeatManager.isAutoRepeatEnabled())
43+
assertFalse(repeatManager.isWaitingForGesture())
44+
assertEquals(emptyList<Int>(), messages)
45+
}
46+
47+
@Test
48+
fun registerExecutorStopsActiveGestureRepeat() {
49+
repeatManager.setAutoRepeatEnabledForTesting(true)
50+
repeatManager.onGesturePerformed(testGesture())
51+
messages.clear()
52+
val executor = testExecutor()
53+
54+
GesturePatternManager.registerExecutor(executor)
55+
56+
assertFalse(repeatManager.isAutoRepeatEnabled())
57+
assertFalse(repeatManager.isRepeatSessionActive())
58+
assertFalse(repeatManager.isRepeating())
59+
assertNull(repeatManager.getRepeatedGestureDataForTesting())
60+
assertEquals(emptyList<Int>(), messages)
61+
}
62+
63+
@Test
64+
fun unregisterExecutorDoesNotRestoreGestureRepeat() {
65+
repeatManager.setAutoRepeatEnabledForTesting(true)
66+
val executor = testExecutor()
67+
GesturePatternManager.registerExecutor(executor)
68+
69+
GesturePatternManager.unregisterExecutor(executor)
70+
71+
assertFalse(repeatManager.isAutoRepeatEnabled())
72+
}
73+
74+
@Test
75+
fun registerExecutorKeepsPatternActive() {
76+
val executor = testExecutor()
77+
78+
GesturePatternManager.registerExecutor(executor)
79+
80+
assertTrue(GesturePatternManager.isGesturePatternActive())
81+
82+
GesturePatternManager.unregisterExecutor(executor)
83+
84+
assertFalse(GesturePatternManager.isGesturePatternActive())
85+
}
86+
87+
private fun testExecutor(): GesturePatternExecutor {
88+
val unsafeField = Unsafe::class.java.getDeclaredField("theUnsafe")
89+
unsafeField.isAccessible = true
90+
val unsafe = unsafeField.get(null) as Unsafe
91+
return unsafe.allocateInstance(GesturePatternExecutor::class.java) as GesturePatternExecutor
92+
}
93+
94+
private fun testGesture(): GestureData {
95+
return GestureData(
96+
gestureType = GestureType.TAP,
97+
startPoint = PointF(10f, 20f)
98+
)
99+
}
100+
}

0 commit comments

Comments
 (0)