1

一些 android 用户使用 play-store 上提供的自动点击器应用程序来多次点击。

所以我想检测/阻止尝试使用自动点击器的用户。android中有没有办法检测到这些用户?如果是,那么我们如何检测这些用户?

4

1 回答 1

5

没有 API 可以检测自动点击器是否正在运行。所有自动点击器都使用辅助功能服务来模拟点击,并且有一个 API 允许您检测是否有任何辅助功能服务正在运行。问题是,这些服务还包括屏幕阅读器和其他对残疾人有用的工具。您绝对不应该仅仅因为他们使用无障碍服务而阻止您的用户。

但是,您也可以检测“不自然”的点击和手势。没有人可以从屏幕上完全相同的点开始执行多个手势,在完全相同的点结束并在每个手势上花费完全相同的时间。

当您检测到用户
a) 使用无障碍服务
b) 执行不自然的点击
时,您可以合理地假设他正在使用自动点击器。然后,您可以阻止他的触摸或做任何您想做的事情。

为了检测不自然的触摸,您必须分析所有传入的触摸事件。获得所有这些接触的最简单方法是覆盖onInterceptTouchEvent根容器的方法。

例如,创建此类并将其用作布局的根:

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
    }

}

在分析 MotionEvents 时请记住,即使对于自动点击器,报告的坐标和时间也可能会略有不同。例如,您可以使用此实现:

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
    }

}

然后在您的主代码中,您可以检查自动点击器当前是否正在工作:

AutoclickerDetector.isAccessibilityServiceEnabled(context) 
    && AutoclickerDetector.isDetectingSimilarGestures
于 2020-07-30T14:04:10.380 回答