Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
ae03fee
Cleanup smart-debugging navigation
Nain57 Oct 10, 2026
09c6d64
Split feature:revenue module into feature & core modules
Nain57 Oct 10, 2026
db0616f
Split feature:review module into feature & core modules
Nain57 Oct 10, 2026
274b376
Declare new Gesture Action in domain
Nain57 Oct 10, 2026
d562202
Add Gesture build/execution
Nain57 Oct 10, 2026
4c8da49
Add gesture data model
Nain57 Oct 10, 2026
5767e6e
Rework gesture recorder to record multi-move & multi-fingers
Nain57 Oct 10, 2026
dc23027
Add new base components for gesture UI
Nain57 Oct 10, 2026
9119ed3
Replace Swipe by Gesture in UI
Nain57 Oct 10, 2026
a91ad3b
Use the same design system as Gestures in Click dialog
Nain57 Oct 10, 2026
87f6187
Update tutorials to fix compilation
Nain57 Oct 10, 2026
4708cbb
Update copy usecases for new gesture and click models
Nain57 Oct 10, 2026
46cdbdc
Split legacy and brief action dialogs
Nain57 Oct 10, 2026
9c7c570
Fix several minor issues:
Nain57 Oct 10, 2026
76de3e8
Add gesture to dumb scenario
Nain57 Oct 10, 2026
3f4adff
Add dumb gesture UI
Nain57 Oct 10, 2026
baf828a
Add hold unitl unfulfilled to engine
Nain57 Oct 10, 2026
74a66ec
Add UI to release when unfulfilled
Nain57 Oct 10, 2026
8c86818
Cleanup gesture management code
Nain57 Oct 10, 2026
9eee394
Bugfix & cleaanup of hold pointer in engine
Nain57 Oct 10, 2026
74f0bc4
Add held event to the Debugging engine
Nain57 Oct 10, 2026
c11a6bd
Fix countdown regression and add live debugging UI
Nain57 Oct 10, 2026
3e03f2f
Add Hold/release gesture to debug report timeline
Nain57 Oct 10, 2026
31e8266
Optimze memory management for gesture building
Nain57 Oct 10, 2026
d0f310b
Show hold & release filter only on Android 8 & above
Nain57 Oct 10, 2026
78c752d
Replace the probe by a ghost pixel catching screen touch
Nain57 Oct 10, 2026
b44a664
Add forced cooldown on held event after release due to user interruption
Nain57 Oct 10, 2026
b716196
Improve gesture renderer
Nain57 Oct 10, 2026
ce063d3
Cleanup smart gesture position model
Nain57 Oct 10, 2026
adebbf8
Add more hold end conditions
Nain57 Oct 10, 2026
505006a
Fix GestureRecorder multi tap issue
Nain57 Oct 10, 2026
fe4e65c
Add gesture specific keys and remove legacy swipe ones
Nain57 Oct 10, 2026
8e3f670
Update tutorials to replace swipes by gestures
Nain57 Oct 10, 2026
203b325
Fix offset field naming
Nain57 Oct 10, 2026
bf76405
Update localization
Nain57 Oct 10, 2026
5439e5e
Fix small bugs in action brief menu
Nain57 Oct 10, 2026
af34af8
Fix unit tests for GestureFingerState
Nain57 Oct 10, 2026
a5ffcc8
Cleanup CounterOperationValue code
Nain57 Oct 10, 2026
3ccc1b5
Add condition poition to set counters in engine & db
Nain57 Oct 10, 2026
55db54f
Add condition position in counter UI
Nain57 Oct 10, 2026
4af46a8
Add "show on keyguard" setting
Nain57 Oct 10, 2026
e6377ac
Fix position counter name behaviour
Nain57 Oct 10, 2026
0767b7c
Improve play store purchase state error management
Nain57 Oct 10, 2026
8a35798
Use billing new reconnection API instead of custom one
Nain57 Oct 10, 2026
e3ef46e
Fix possible crashes when starting an activity
Nain57 Oct 10, 2026
5036c91
Fix a possible crash after watching an ad
Nain57 Oct 10, 2026
8d39e6b
Add crashlytics keys to help identify Android 9 Webview crash cause
Nain57 Oct 10, 2026
b5a8c05
Add keys to crashlytics to help find DumbExecutor rare memory leak cause
Nain57 Oct 10, 2026
e365e1b
Remove debug artifacts from Git
Nain57 Oct 10, 2026
6867dac
Use real model instead of dupes for android tests
Nain57 Oct 10, 2026
3847687
Add help item to the action brief
Nain57 Oct 10, 2026
095eb12
Add screen condition list tutorial slideshow
Nain57 Oct 10, 2026
056139a
Improve tutorial slideshow dialog in landscape
Nain57 Oct 10, 2026
1f0dd61
Update dependencies
Nain57 Oct 10, 2026
bac87c2
Fix HoldEvent details not browsaeble in debug report
Nain57 Oct 10, 2026
79edb52
Fix lint warnings caused by gesture development
Nain57 Oct 10, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,12 @@
package com.buzbuz.smartautoclicker.core.common.actions

import android.accessibilityservice.AccessibilityService
import android.accessibilityservice.GestureDescription
import android.content.Intent

import com.buzbuz.smartautoclicker.core.base.Dumpable
import com.buzbuz.smartautoclicker.core.common.actions.model.ActionNotificationRequest
import com.buzbuz.smartautoclicker.core.common.actions.model.BuiltGesture
import com.buzbuz.smartautoclicker.core.common.actions.model.HeldPointer

interface AndroidActionExecutor: Dumpable {

Expand All @@ -45,21 +47,49 @@ interface AndroidActionExecutor: Dumpable {
fun clear()

/**
* Dispatch a gesture to the touch screen.
* The pointer a gesture holding its first finger left down on the screen, or null when nothing is held.
*
* It must be given to AndroidGestureBuilder.build for every gesture dispatched while it is held: the system only
* honours a continuation when the first step of the dispatched gesture holds exactly one stroke for each pointer
* currently down, so a gesture built without it would have the hold cancelled.
*/
fun getHeldPointer(): HeldPointer?

/**
* Dispatch a gesture built by an AndroidGestureBuilder, and keep track of the pointer it leaves down.
*
* Method will return only once the whole gesture is dispatched.
*
* @param gesture the gesture to dispatch.
*/
suspend fun dispatchBuiltGesture(gesture: BuiltGesture)

/**
* Verify the pointer left down on the screen by a gesture holding its first finger, if any, is still held.
*
* Any gestures currently in progress, whether from the user, this service, or another service, will be cancelled.
* Method will return only once the gesture is dispatched.
* The system lifts it without notice when the user touches the screen. Once lost, [getHeldPointer] returns null.
* Nothing is dispatched to the system, the user touches being watched while a pointer is held, so it can be called
* on every frame from any thread.
*
* The gesture will be dispatched as if it were performed directly on the screen by a user, so the events may be
* affected by features such as magnification and explore by touch.
* @return true if a pointer is still held, false if it has been lost or if nothing was held.
*/
fun checkHeldPointer(): Boolean

/**
* Lift the pointer left down on the screen by a gesture holding its first finger, if any.
*
* <strong>Note:</strong> In order to dispatch gestures, your service must declare the capability by setting the
* android.R.styleable.AccessibilityService_canPerformGestures property in its meta-data. For more information,
* see SERVICE_META_DATA.
* Method will return only once the pointer is lifted.
*/
suspend fun releaseHeldPointer()

/**
* Some pixel devices have an OS issue on Android 15 & 16.
* Injecting a double tap somewhere seems to fix the problem most of the time. This method will take care of
* injecting this gesture, it will avoid spamming it by skipping unblock requests to close to the previous one.
*
* @param gestureDescription The gesture to dispatch
* @see [com.buzbuz.smartautoclicker.core.base.workarounds.isImpactedByInputBlock]
*/
suspend fun dispatchGesture(gestureDescription: GestureDescription)
suspend fun dispatchPixelWorkaroundGestureIfNeeded()

/**
* Performs a global action.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,15 @@
package com.buzbuz.smartautoclicker.core.common.actions

import android.accessibilityservice.AccessibilityService
import android.accessibilityservice.GestureDescription
import android.content.ActivityNotFoundException
import android.content.Intent
import android.util.AndroidRuntimeException
import android.util.Log

import com.buzbuz.smartautoclicker.core.common.actions.gesture.GestureExecutor
import com.buzbuz.smartautoclicker.core.common.actions.model.ActionNotificationRequest
import com.buzbuz.smartautoclicker.core.common.actions.model.BuiltGesture
import com.buzbuz.smartautoclicker.core.common.actions.model.HeldPointer
import com.buzbuz.smartautoclicker.core.common.actions.notification.NotificationRequestExecutor
import com.buzbuz.smartautoclicker.core.common.actions.text.TextExecutor

Expand All @@ -33,6 +34,7 @@ import java.io.PrintWriter
import java.lang.ref.WeakReference
import javax.inject.Inject
import javax.inject.Singleton
import kotlin.time.Duration.Companion.milliseconds


@Singleton
Expand All @@ -56,13 +58,16 @@ internal class AndroidActionExecutorImpl @Inject constructor(
}
}

override fun getHeldPointer(): HeldPointer? =
gestureExecutor.getHeldPointer()

override fun init(service: AccessibilityService) {
accessibilityServiceRef = WeakReference(service)
notificationRequestExecutor.init(service)
}

override fun resetState() {
gestureExecutor.clear()
gestureExecutor.clear(accessibilityServiceRef?.get())
notificationRequestExecutor.clear()
}

Expand All @@ -71,13 +76,35 @@ internal class AndroidActionExecutorImpl @Inject constructor(
accessibilityServiceRef = null
}

override suspend fun dispatchGesture(gestureDescription: GestureDescription) {
override suspend fun dispatchBuiltGesture(gesture: BuiltGesture) {
val service = accessibilityService ?: return

if (!gestureExecutor.dispatchGesture(service, gestureDescription)) {
if (!gestureExecutor.dispatchBuiltGesture(service, gesture)) {
Log.w(TAG, "System did not execute the gesture properly, delaying processing to avoid spamming slow system")
delay(500)
delay(500.milliseconds)
}
}

override fun checkHeldPointer(): Boolean {
if (!gestureExecutor.checkHeldPointer()) {
Log.i(TAG, "Held pointer has been lifted by a user touch")
return false
}

return gestureExecutor.getHeldPointer() != null
}

override suspend fun releaseHeldPointer() {
val service = accessibilityService ?: return

if (!gestureExecutor.releaseHeldPointer(service)) {
Log.w(TAG, "System did not release the held pointer properly")
}
}

override suspend fun dispatchPixelWorkaroundGestureIfNeeded() {
val service = accessibilityService ?: return
gestureExecutor.dispatchPixelWorkaroundGestureIfNeeded(service)
}

override fun performGlobalAction(globalAction: Int) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* Copyright (C) 2026 Kevin Buzeau
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.buzbuz.smartautoclicker.core.common.actions.gesture

internal class GestureDebouncer(private val gestureMinDelayMs: Long) {

private var lastUnblockTimeMs = System.currentTimeMillis()

fun shouldTrigger(): Boolean {
val currentTime = System.currentTimeMillis()
if (currentTime >= (lastUnblockTimeMs + gestureMinDelayMs)) {
lastUnblockTimeMs = currentTime
return true
}

return false
}
}
Loading
Loading