如何检测 android 用户使用的自动点击器应用程序?
How to detect auto clicker app used by android user?
一些 android 用户使用 Play 商店中提供的自动点击器应用程序进行多次点击。
所以我想 detect/block 尝试使用自动 clicker.Is 的用户 android 有什么方法可以检测到此类用户?如果是,那么我们如何检测这些用户?
没有 API 来检测自动点击器是否为 运行。所有自动点击器都使用无障碍服务来模拟点击,并且有一个 API 允许您检测是否有任何无障碍服务 运行。问题是,这些服务还包括屏幕阅读器和其他对残疾人有用的工具。您绝对不应该仅仅因为用户使用无障碍服务就阻止他们。
但是,您也可以检测到“不自然”的点击和手势。没有人可以在屏幕上从完全相同的点开始执行多个手势,在完全相同的点结束并在每个手势上花费完全相同的时间。
当您检测到该用户是
a) 使用无障碍服务
b) 执行不自然的点击
您可以合理地假设他正在使用自动点击器。然后你可以阻止他的触摸或做任何你想做的事。
为了检测不自然的触摸,您必须分析所有传入的触摸事件。获得所有这些接触的最简单方法是覆盖根容器的 onInterceptTouchEvent
方法。
例如,创建此 class 并将其用作布局的根目录:
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
一些 android 用户使用 Play 商店中提供的自动点击器应用程序进行多次点击。
所以我想 detect/block 尝试使用自动 clicker.Is 的用户 android 有什么方法可以检测到此类用户?如果是,那么我们如何检测这些用户?
没有 API 来检测自动点击器是否为 运行。所有自动点击器都使用无障碍服务来模拟点击,并且有一个 API 允许您检测是否有任何无障碍服务 运行。问题是,这些服务还包括屏幕阅读器和其他对残疾人有用的工具。您绝对不应该仅仅因为用户使用无障碍服务就阻止他们。
但是,您也可以检测到“不自然”的点击和手势。没有人可以在屏幕上从完全相同的点开始执行多个手势,在完全相同的点结束并在每个手势上花费完全相同的时间。
当您检测到该用户是
a) 使用无障碍服务
b) 执行不自然的点击
您可以合理地假设他正在使用自动点击器。然后你可以阻止他的触摸或做任何你想做的事。
为了检测不自然的触摸,您必须分析所有传入的触摸事件。获得所有这些接触的最简单方法是覆盖根容器的 onInterceptTouchEvent
方法。
例如,创建此 class 并将其用作布局的根目录:
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