安卓辅助功能(无障碍)AccessibilityService实战介绍
发布日期:2021-06-29 18:33:29
浏览次数:2
分类:技术文章
本文共 8759 字,大约阅读时间需要 29 分钟。
简要介绍
AccessibilityService是安卓平台上提供的无障碍服务,用于帮助残障人士使用手机,不过通过此功能可以完成很多事情.可以进行模拟界面操作,如点击界面上某个按钮等.
使用详细步骤
- 增加service定义 在onAccessibilityEvent函数中进行相应操作(安卓界面有变化时,都会调用此函数)
class MyAccessibilityService : AccessibilityService() { private val TAG = MyAccessibilityService::class.java.simpleName private var mContext: Context? = null override fun onCreate() { super.onCreate() Log.d(TAG, "onCreate") mContext = applicationContext AccessibilityOperator.getInstance().init(this) } override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int { return Service.START_STICKY } override fun onAccessibilityEvent(event: AccessibilityEvent) { AccessibilityOperator.getInstance().updateEvent(event) val packageName = AccessibilityOperator.getInstance().rootNodeInfo?.packageName?.toString()// pasteToEditTextContent(packageName) var accessibilityService = AccessibilityOperator.getInstance() //按下返回键// accessibilityService.performGlobalAction(GLOBAL_ACTION_BACK) //向下拉出状态栏// accessibilityService.performGlobalAction(GLOBAL_ACTION_NOTIFICATIONS) //向下拉出状态栏并显示出所有的快捷操作按钮// accessibilityService.performGlobalAction(GLOBAL_ACTION_QUICK_SETTINGS) //按下HOME键// accessibilityService.performGlobalAction(GLOBAL_ACTION_HOME) //显示最近任务// accessibilityService.performGlobalAction(GLOBAL_ACTION_RECENTS) //长按电源键// accessibilityService.performGlobalAction(GLOBAL_ACTION_POWER_DIALOG) //分屏// accessibilityService.performGlobalAction(GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN) //锁屏(安卓9.0适用)// accessibilityService.performGlobalAction(GLOBAL_ACTION_LOCK_SCREEN) //截屏(安卓9.0适用)// accessibilityService.performGlobalAction(GLOBAL_ACTION_TAKE_SCREENSHOT) //打开快速设置 accessibilityService.performGlobalAction(GLOBAL_ACTION_QUICK_SETTINGS) } /** * 修改EditText输入框内容。 * 下面样例修改了QQ搜索输入框内容。 */ private fun changeEditTextContent(packageName: String?) { getNodeToOperate(packageName)?.let { val arguments = Bundle() arguments.putCharSequence(AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE, "被无障碍服务修改啦") it.performAction(AccessibilityNodeInfo.ACTION_SET_TEXT, arguments) } } /** * 读取剪贴板内容,粘贴到EditText输入框。 * 下面样例修改了QQ搜索输入框内容。 */ private fun pasteToEditTextContent(packageName: String?) { getNodeToOperate(packageName)?.let { it.performAction(AccessibilityNodeInfo.FOCUS_INPUT) it.performAction(AccessibilityNodeInfo.ACTION_PASTE) it.recycle() } } private fun getNodeToOperate(packageName: String?): AccessibilityNodeInfo? { if (packageName != null && packageName == "com.tencent.mobileqq") { val nodes = AccessibilityOperator.getInstance().findNodesById("com.tencent.mobileqq:id/et_search_keyword") if (nodes != null && nodes.isNotEmpty()) { return nodes[0] } } return null } override fun onInterrupt() { }}
- 在Manifest文件中增加service定义
......
- AccessibilityOperator工具类
class AccessibilityOperator private constructor() { private var mAccessibilityEvent: AccessibilityEvent? = null private var accessibilityService: AccessibilityService? = null val rootNodeInfo: AccessibilityNodeInfo? get() { var nodeInfo: AccessibilityNodeInfo? = null accessibilityService?.let { nodeInfo = accessibilityService!!.rootInActiveWindow } if (nodeInfo == null && mAccessibilityEvent != null) { nodeInfo = mAccessibilityEvent!!.source } return nodeInfo } fun init(service: AccessibilityService) { accessibilityService = service } fun updateEvent(event: AccessibilityEvent) { mAccessibilityEvent = event } /** * 根据Text搜索所有符合条件的节点, 模糊搜索方式 */ fun findNodesByText(text: String): List? { val nodeInfo = rootNodeInfo return nodeInfo?.findAccessibilityNodeInfosByText(text) } /** * 根据View的ID搜索符合条件的节点,精确搜索方式; * 这个只适用于自己写的界面,因为ID可能重复 * * @param viewId */ fun findNodesById(viewId: String): List ? { val nodeInfo = rootNodeInfo return nodeInfo?.findAccessibilityNodeInfosByViewId(viewId) } fun clickByText(text: String): Boolean { return performClick(findNodesByText(text)) } fun clickParentByText(text: String, depth: Int): Boolean { return this.performClick(this.findParentNodesByText(text, depth)) } fun clickParentById(viewId: String, depth: Int): Boolean { return this.performClick(this.findParentNodesById(viewId, depth)) } fun findParentNodesByText(text: String, depth: Int): List { val rootNodeInfo = this.rootNodeInfo val resultNodeList = mutableListOf () if (rootNodeInfo != null) { val nodeList = findAccessibilityNodeInfosByText(rootNodeInfo, text) val iterator = nodeList.iterator() while (iterator.hasNext()) { val accessibilityNodeInfo = iterator.next() as AccessibilityNodeInfo resultNodeList.add(getParentNode(accessibilityNodeInfo, depth)) } } return resultNodeList } fun findParentNodesById(viewId: String, depth: Int): List { val rootNodeInfo = this.rootNodeInfo val resultNodeList = mutableListOf () if (rootNodeInfo != null) { val nodeList = rootNodeInfo.findAccessibilityNodeInfosByViewId(viewId) val iterator = nodeList.iterator() while (iterator.hasNext()) { val accessibilityNodeInfo = iterator.next() as AccessibilityNodeInfo resultNodeList.add(this.getParentNode(accessibilityNodeInfo, depth)) } } return resultNodeList } private fun findAccessibilityNodeInfosByText(node: AccessibilityNodeInfo?, text: String?): List { val resultNodeList = mutableListOf () if (node != null && text != null) { val nodeList = node.findAccessibilityNodeInfosByText(text) if (nodeList != null && !nodeList.isEmpty()) { val iterator = nodeList.iterator() while (iterator.hasNext()) { val nodeInList = iterator.next() as AccessibilityNodeInfo if (TextUtils.equals(nodeInList.text, text)) { resultNodeList.add(nodeInList) } } } return resultNodeList } else { return resultNodeList } } private fun getParentNode(nodeInfo: AccessibilityNodeInfo, depth: Int): AccessibilityNodeInfo { var resultNodeInfo = nodeInfo for (i in 0 until depth) { val parentNode = resultNodeInfo.parent resultNodeInfo = parentNode } return resultNodeInfo } /** * 根据View的ID搜索符合条件的节点,精确搜索方式; * 这个只适用于自己写的界面,因为ID可能重复 * * @param viewId * @return 是否点击成功 */ fun clickById(viewId: String): Boolean { return performClick(findNodesById(viewId)) } private fun performClick(nodeInfoList: List ?): Boolean { if (nodeInfoList != null && !nodeInfoList.isEmpty()) { var node: AccessibilityNodeInfo for (i in nodeInfoList.indices) { node = nodeInfoList[i] // 进行模拟点击 if (node.isEnabled) { return node.performAction(AccessibilityNodeInfo.ACTION_CLICK) } } } return false } fun clickBackKey(): Boolean { return performGlobalAction(AccessibilityService.GLOBAL_ACTION_BACK) } fun performGlobalAction(action: Int): Boolean { return accessibilityService!!.performGlobalAction(action) } private fun getNodeInfo(nodeInfo: AccessibilityNodeInfo?): String { var result = "" if (nodeInfo != null) { result = nodeInfo.className.toString() + ";text:" + nodeInfo.text + ";id:" + nodeInfo.viewIdResourceName + ";" } return result } fun clickTextParent(text: String): Boolean { val nodeInfo = rootNodeInfo return nodeInfo?.let { clickTextParent(it, text) } ?: false } private fun clickTextParent(rootInfo: AccessibilityNodeInfo?, text: String): Boolean { if (rootInfo != null && !TextUtils.isEmpty(rootInfo.className)) { if ("android.widget.TextView" == rootInfo.className.toString()) { if (!TextUtils.isEmpty(rootInfo.text) && rootInfo.text.toString().startsWith(text)) { val result = performClick(rootInfo.parent) Log.v(TAG, rootInfo.parent.className.toString() + ":result=" + result) return result } } for (i in 0 until rootInfo.childCount) { val result = clickTextParent(rootInfo.getChild(i), text) if (result) { return result } } return false } return false } private fun performClick(targetInfo: AccessibilityNodeInfo): Boolean { return targetInfo.performAction(AccessibilityNodeInfo.ACTION_CLICK) } companion object { private val TAG = "AccessibilityOperator" val instance = AccessibilityOperator() }}
Demo源代码
安卓开发技术分享:
更多技术总结好文,请关注:「程序园中猿」转载地址:https://cxyxy.blog.csdn.net/article/details/88813268 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!
发表评论
最新留言
路过,博主的博客真漂亮。。
[***.116.15.85]2024年04月13日 14时55分39秒
关于作者
喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!
推荐文章
突破!台积电1nm芯片,有了新进展。
2019-04-29
一文读懂全系列树莓派!
2019-04-29
自制一个害羞的口罩,见人就闭嘴,戴着可以喝奶茶
2019-04-29
聊聊我是如何编程入门的
2019-04-29
J-Link该如何升级固件?
2019-04-29
485通信自动收发电路,历史上最详细的解释
2019-04-29
「第三篇」全国电子设计竞赛,这些你必须知道的比赛细节,文末附上近十年电赛题目下载...
2019-04-29
5G小科普(漫画版,So easy!)
2019-04-29
「第四篇」电赛控制题可以准备一些什么?
2019-04-29
树莓派翻车了
2019-04-29
这位电子工程师,你不能错过。
2019-04-29
干货分享 JVM 之第 3 篇 —— Java 内存结构相关
2019-04-29
UR5e机械臂运行一直阻塞在waitForServer
2019-04-29
ROS把pkg1下的某个头文件和源文件生成动态链接库供pkg2调用
2019-04-29
使用urdf_tutorial快速可视化urdf文件
2019-04-29
SQl 数据完整性(随堂博客)
2019-04-29
左连接、右连接、内连接
2019-04-29
MySQL DQL语句基础(随堂博客)
2019-04-29
利用MySQL进行数据复杂查询(1)
2019-04-29