安卓无障碍自动点击
安卓无障碍自动点击
·
提示:如果你的项目名称不是"test"
这行package com.example.test就会报错改正即可
build.gradle(Project:test)
pluginManagement {
repositories {
google()
mavenCentral()
gradlePluginPortal()
maven { url 'https://jitpack.io' }
}
}
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
repositories {
google()
mavenCentral()
// 下面这两个允许引入第三方库
jcenter()
maven { url 'https://jitpack.io' }
}
}
}
rootProject.name = "test"
include ':app'
build.gradle(Module:app)
plugins {
···
}
android {
···
// 开启data binding
dataBinding {
enabled = true
}
···
}
dependencies {
···
}
主界面 activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<LinearLayout
android:id="@+id/linear"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#D65B5B"
android:backgroundTint="#D65B5B"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center">
<Button
android:id="@+id/btn01"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="3dp"
android:minHeight="48dp"
android:text="Toast" />
<Button
android:id="@+id/btn02"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="3dp"
android:minHeight="48dp"
android:text="开启无障碍" />
</LinearLayout>
</LinearLayout>
</layout>
主界面MainActivity.tk
package com.example.test
import android.Manifest
import android.content.Intent
import android.content.pm.PackageManager
import android.os.Bundle
import android.os.Environment
import android.view.View
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.databinding.DataBindingUtil
import com.example.test.databinding.ActivityMainBinding
import kotlin.system.exitProcess
class MainActivity : AppCompatActivity() {
private var id = R.layout.activity_main
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// databinding对象自己上网搜索一下即可,不用findViewByMe了
binding = DataBindingUtil.setContentView(this, id)
// 将自动点击该按钮
binding.btn01.setOnClickListener{
print("点击了Toast")
Log.i("tag","点击了Toast")
}
binding.btn02.setOnClickListener {
// 挑转到无障碍服务
if (!ASHelper.isAccessibilitySettingsOn(this, TheAccessibilityService::class.java.name)) {
ASHelper.jumpToSettingPage(this)
}
}
}
}
上面需要用到的ASHelper.kt类
package com.example.test
import android.app.ActivityManager
import android.content.Context
import android.content.Intent
import android.provider.Settings
object ASHelper {
//跳转到设置页面无障碍服务开启自定义辅助功能服务
fun jumpToSettingPage(context: Context) {
val intent = Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS)
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
context.startActivity(intent)
}
//判断自定义辅助功能服务是否开启
fun isAccessibilitySettingsOn(context: Context?, className: String): Boolean {
if (context == null) {
return false
}
val activityManager = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
if (activityManager != null) {
val runningServices = activityManager.getRunningServices(100) // 获取正在运行的服务列表
if (runningServices.size < 0) {
return false
}
for (i in runningServices.indices) {
val service = runningServices[i].service
if (service.className == className) {
return true
}
}
}
return false
}
}
接下来准备一个acuton.xml文件放入layout文件夹,用于在桌面上悬浮
acuton.xml
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="text"
type="String" />
</data>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
<Button
android:id="@+id/start"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="模拟点击" />
</LinearLayout>
</layout>
配置无障碍服务的xml
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
android:accessibilityEventTypes="typeAllMask"
android:accessibilityFeedbackType="feedbackGeneric"
android:accessibilityFlags="flagReportViewIds"
android:canRetrieveWindowContent="true"
android:canPerformGestures="true"
android:notificationTimeout="100"
android:description="描述"
<!-- 这个是你要操作的APP的包名比如QQ的包名就是com.tencent.mobileqq -->
<!-- 包名都可以在网上搜索到,这里用我自己的项目包名去点击本程序 -->
android:packageNames="com.example.test" />
配置自己的无障碍程序
package com.example.test
import android.accessibilityservice.AccessibilityService
import android.annotation.SuppressLint
import android.graphics.PixelFormat
import android.graphics.Rect
import android.os.Build
import android.os.Environment
import android.util.Log
import android.view.Gravity
import android.view.LayoutInflater
import android.view.WindowManager
import android.view.accessibility.AccessibilityEvent
import android.view.accessibility.AccessibilityNodeInfo
import android.widget.Button
import android.widget.FrameLayout
import android.widget.TextView
import androidx.annotation.RequiresApi
import com.alibaba.fastjson.JSON
import java.io.File
class TheAccessibilityService : AccessibilityService() {
private lateinit var mLayout: FrameLayout
@RequiresApi(Build.VERSION_CODES.R)
@SuppressLint("ServiceCast")
override fun onServiceConnected() {
// 在屏幕顶部添加一个 View
val wm = getSystemService(WINDOW_SERVICE) as? WindowManager
mLayout = FrameLayout(this)
// 一个视图对象
val lp = WindowManager.LayoutParams().apply {
type =
WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY // 因为此权限才能展示处理, 只能配合 AccessibilityService 使用
format = PixelFormat.TRANSLUCENT
flags = flags or WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
width = WindowManager.LayoutParams.WRAP_CONTENT
height = WindowManager.LayoutParams.WRAP_CONTENT
gravity = Gravity.TOP
}
// 加入布局到View
val inflater = LayoutInflater.from(this@TheAccessibilityService)
inflater.inflate(R.layout.acuton_bar, mLayout)
wm?.addView(mLayout, lp)
// 获取按钮
var start = mLayout.findViewById<Button>(R.id.start)
// 点击事件
start.setOnClickListener {
try {
// 根据控件的文本获取节点(返回一个List)
var node = rootInActiveWindow?.findAccessibilityNodeInfosByText("Toast")
// 判断是否有节点
if (node != null && node.isNotEmpty()) {
// 新建矩阵
val rect = Rect()
// 根据节点位置写入矩阵内
node[0].getBoundsInScreen(rect)
// 模拟点击
AutoClick().clickCMD(rect, this@TheAccessibilityService)
}
} catch (e: java.lang.Exception) {
Log.i("mine", "error: $e")
}
}
}
override fun onAccessibilityEvent(event: AccessibilityEvent?) {}
override fun onInterrupt() {}
}
上面用到的AutoClick.tk类
package com.example.test;
import android.accessibilityservice.AccessibilityService
import android.accessibilityservice.GestureDescription
import android.graphics.Path
import android.graphics.Point
import android.graphics.Rect
import android.os.Build
import android.util.Log
import android.view.accessibility.AccessibilityNodeInfo
import androidx.annotation.RequiresApi
import java.io.OutputStream
class AutoClick {
/**
* 使用安卓内部cmd模拟点击,需要root权限
*/
open fun clickCMD(rect: Rect, service: AccessibilityService) {
var os: OutputStream? = null
val x = (rect.left + rect.right) / 2
val y = (rect.top + rect.bottom) / 2
val cmd = "input tap $x $y"
try {
if (os == null) {
os = Runtime.getRuntime().exec("su").outputStream
}
os!!.write(cmd.toByteArray())
os!!.flush() //清空缓存
os!!.close() //停止流
} catch (e: Exception) {
// 如果没有root的设备就会异常,尝试使用无障碍手势模拟点击
gestureClick(rect,service)
}
}
/**
* 无障碍模拟手势点击
*/
@RequiresApi(api = Build.VERSION_CODES.N)
open fun gestureClick(rect: Rect, service: AccessibilityService): Boolean {
val x: Int = (rect.left + rect.right) / 2
val y: Int = (rect.top + rect.bottom) / 2
val point = Point(x, y)
val builder: GestureDescription.Builder = GestureDescription.Builder()
val path = Path()
path.moveTo(point.x.toFloat(), point.y.toFloat())
builder.addStroke(GestureDescription.StrokeDescription(path, 0L, 100L))
val gesture: GestureDescription = builder.build()
return service.dispatchGesture(
gesture,
object : AccessibilityService.GestureResultCallback() {
override fun onCompleted(gestureDescription: GestureDescription?) {
super.onCompleted(gestureDescription)
Log.i("mine", "dispatchGesture onCompleted: 完成...");
}
override fun onCancelled(gestureDescription: GestureDescription?) {
super.onCancelled(gestureDescription)
Log.i("mine", "dispatchGesture onCancelled: 取消...");
}
},
null
)
}
}
另外的,别忘记了在AndroidManifest.xml里面配置
canPerformGestures="true"允许执行手势
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<!-- // Android6.0~Android10.0添加-->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<!-- // Android11.0添加-->
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
<application
android:accessibilityFlags="flagDefault|flagRetrieveInteractiveWindows|flagIncludeNotImportantViews"
canPerformGestures="true"
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/Theme.Test"
tools:targetApi="31">
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service
android:name=".TheAccessibilityService"
android:enabled="true"
android:exported="true"
android:label="服务名称"
android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"
android:process=":BackgroundService">
<intent-filter>
<action android:name="android.accessibilityservice.AccessibilityService" />
</intent-filter>
<meta-data
android:name="android.accessibilityservice"
android:resource="@xml/accessibility_config" />
</service>
</application>
</manifest>
更多推荐
已为社区贡献4条内容
所有评论(0)