Some android user using auto-clicker apps available on play-store to click multiple time.
So I want to detect/block user who tries to use auto-clicker.Is there any way in android to detect such users?If yes, then how we can detect those users?
There is no API to detect if an autoclicker is running. All autoclickers use accessibility services to emulate clicks, and there is an API that allows you to detect if any accessibility service is running. The problem is, those services also include screen readers and other useful tools for disabled people. You definitely shouldn't block your users just because they're using accessibility services.
However, you can also detect "unnatural" clicks and gestures. No human can perform several gestures starting at exactly the same point on the screen, finishing at exactly the same point and spending exactly the same amount of time on each gesture.
And when you detect that user is
a) using an accessibility service
b) performing unnatural clicks
you can reasonably assume that he is using an autoclicker. You can then block his touches or do whatever you want to do.
In order to detect unnatural touches you'll have to analyze all incoming touch events. Easiest way to get all those touches is to override onInterceptTouchEvent
method of your root container.
E.g., create this class and use it as root for your layout:
class AutoclickerFrameLayout @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : FrameLayout(context, attrs, defStyleAttr) {
override fun onInterceptTouchEvent(event: MotionEvent): Boolean {
AutoclickerDetector.recordEvent(event)
// If you return true in this method touches will be blocked
return false
}
}
When analyzing MotionEvents keep in mind that even for autoclicker, reported coordinates and time can vary very slightly. For example, you can use this implementation:
object AutoclickerDetector {
fun isAccessibilityServiceEnabled(context: Context): Boolean {
val am = context.getSystemService(Context.ACCESSIBILITY_SERVICE) as AccessibilityManager
val enabledServices = am.getEnabledAccessibilityServiceList(AccessibilityServiceInfo.FEEDBACK_ALL_MASK)
return enabledServices.isNotEmpty()
}
var isDetectingSimilarGestures = false
private set
private data class Touch(
val x: Float,
val y: Float,
val time: Long
) {
fun isSimilar(other: Touch): Boolean {
return abs(this.x - other.x) < 1.0f
&& abs(this.y - other.y) < 1.0f
}
}
private data class Gesture(
val start: Touch,
val end: Touch
) {
val duration: Long = end.time - start.time
fun isSimilar(other: Gesture): Boolean {
return this.start.isSimilar(other.start)
&& this.end.isSimilar(other.end)
&& abs(this.duration - other.duration) < 50
}
}
private var gestureStart: Touch? = null
private val recentGestures = ArrayList<Gesture>()
fun recordEvent(event: MotionEvent) {
when (event.action) {
MotionEvent.ACTION_DOWN -> {
gestureStart = Touch(event.rawX, event.rawY, System.currentTimeMillis())
}
MotionEvent.ACTION_UP -> {
gestureStart?.let { gestureStart ->
val gestureEnd = Touch(event.rawX, event.rawY, System.currentTimeMillis())
recentGestures.add(Gesture(gestureStart, gestureEnd))
trimGestureHistory()
checkSimilarGestures()
}
gestureStart = null
}
}
}
private const val HISTORY_SIZE = 20
private fun trimGestureHistory() {
while (recentGestures.size > HISTORY_SIZE) {
recentGestures.removeAt(0)
}
}
private fun checkSimilarGestures() {
recentGestures.forEachIndexed { i, searchGesture ->
var similarCount = 0
recentGestures.forEachIndexed { j, compareGesture ->
if (i != j && searchGesture.isSimilar(compareGesture)) {
similarCount++
}
}
if (similarCount > 2) {
// There is no way user can physically perform almost exactly the same gesture
// 3 times amongst 20 last gestures
isDetectingSimilarGestures = true
return
}
}
isDetectingSimilarGestures = false
}
}
Then in your main code you can check if autoclicker is currently working:
AutoclickerDetector.isAccessibilityServiceEnabled(context)
&& AutoclickerDetector.isDetectingSimilarGestures