后脑i 发表于 2023-2-15 14:43

安卓自动化之无障碍模式

本帖最后由 后脑i 于 2023-2-17 09:12 编辑

> 预计阅读时间5分钟
>
> MIUI14更新后,李跳跳等工具便出现问题,为了满足使用的需求,便开始研究实现过程,下面是学习记录,解决方案请直接跳至第6点。

#### 1.AccessibilityService无障碍服务

> 无障碍服务主要是用来协助残障用户更好的使用手机,但是各位大佬基于无障碍服务开发了很多玩法,例如抢红包、跳广告等,接下来我们新建个项目来实现跳广告功能。

##### 1.1 创建服务

```kotlin
class MyAccessibilityService : AccessibilityService() {
    private val TAG = javaClass.simpleName
    override fun onAccessibilityEvent(event: AccessibilityEvent?) {
      Log.e(TAG, "onAccessibilityEvent$event")
      if (event?.eventType == TYPE_WINDOW_CONTENT_CHANGED) {
            if (event.className == "android.widget.TextView") {
                //获取跳过按钮并点击
                try {
                  val nodeInfo = rootInActiveWindow
                  if (nodeInfo != null) {
                        skip(nodeInfo, "跳过")
                  }
                } catch (e: Error) {
                  Log.e(TAG, "$e")
                }
            }
      }
    }
   
    /**
   * 匹配跳过规则
   */
    private fun skip(nodeInfo: AccessibilityNodeInfo?, str: String?) {
      val nodeInfoList = nodeInfo?.findAccessibilityNodeInfosByText(str)
      if (nodeInfoList == null || nodeInfoList.isEmpty()) {
            //页面跳转过程中,有可能获取不到
            return
      }
      for (info in nodeInfoList) {
            val charSequence = info.text
            if (charSequence != null) {
                val msg = charSequence.toString()
                if (msg.contains("跳过")) {
                  info.performAction(ACTION_CLICK)
                  Toast.makeText(this, "跳过广告", Toast.LENGTH_SHORT).show()
                }
            }
      }
    }

    override fun onInterrupt() {
    }
}
```

##### 1.2配置服务

res目录下新建service_config.xml文件,名称可以自定义下一步会用到

```xml
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
    android:accessibilityEventTypes="typeAllMask"
    android:accessibilityFeedbackType="feedbackGeneric"
    android:canRetrieveWindowContent="true"
    android:accessibilityFlags="flagDefault"
    android:canRequestTouchExplorationMode="true"
    android:description="@string/app_name"
    android:notificationTimeout="100"/>
```

##### 1.3 添加清单文件

```xml
<service android:name=".MyAccessibilityService"
    android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"
    android:exported="false">
    <intent-filter>
      <action android:name="android.accessibilityservice.AccessibilityService" />
    </intent-filter>
    <meta-data android:name="android.accessibilityservice"
      android:resource="@xml/service_config" />
</service>
```

##### 1.4 启动服务

```kotlin
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    //跳转无障碍模式设置界面
    startActivity(Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS))
}
```

#### 2.实现无障碍功能



#### 3.获取UI布局

##### 3.1 findAccessibilityNodeInfosByText

在1.1中使用findAccessibilityNodeInfosByText来进行模糊匹配,为了满足精准匹配的需求,我们也可以通过获取id来实现(findAccessibilityNodeInfosByViewId)

##### 3.2 findAccessibilityNodeInfosByViewId

> 使用uiautomatorviewer工具分析app界面
>
> **注:工具路径 在安卓sdk中 C:\Users\XX\AppData\Local\Android\Sdk\tools\bin\uiautomatorviewer.bat**
>
> **运行的命令框请勿关闭**



```kotlin
private fun skip(nodeInfo: AccessibilityNodeInfo?) {
    val nodeInfoList = nodeInfo?.findAccessibilityNodeInfosByViewId("com.moutai.mall:id/tvSkip")
    if (nodeInfoList == null || nodeInfoList.isEmpty()) {
      //页面跳转过程中,有可能获取不到
      return
    }
    for (info in nodeInfoList) {
      info.performAction(ACTION_CLICK)
      Toast.makeText(this, "跳过广告", Toast.LENGTH_SHORT).show()
    }
}
```

##### 3.3 遍历父节点

通过获取父节点的AccessibilityWodeInfo,再通过该对象的getChild 方法来查询目标节点

```kotlin
private fun findInfo(
    info: AccessibilityNodeInfo?,
    str: String?,
): AccessibilityNodeInfo? {
    if (info == null) return null
    if (info.childCount == 0) {
      if (info.text!=null){
            if (info.text.toString().contains(str!!)) {
                info.performAction(ACTION_CLICK)
                Log.e("findInfo","$info")
            }
            return info
      }
    } else {
      for (i in 0 until info.childCount) {
            findInfo(info.getChild(i), str)
      }
    }
    return null
}
```

#### 4.遇到的问题

##### 4.1android findAccessibilityNodeInfosByViewId return null although the node id exists

遇到这个问题时排查下获取nodeInfo是不是根活动视图,如果没有解决,再看下配置文件accessibilityEventTypes中的设置

最开始出现是使用event!!.source来获取,该方法是获取当前界面的可访问节点信息,但在跳转过程中有可能获取不到。

#### 5.防范方式

> ##### 1. 重写TextView的findViewsWithText方法
>
> ##### 2. Event干扰
>
> ##### 3. 使用onTouch代替onClick

#### 6.MIUI14使用跳过功能

进入开发者选项--》启用MIUI功能--》关闭--》开启无障碍模式--》开启跳过广告的app--》重新启用MIUI功能

亲测使用正常!



以上就是无障碍服务的学习过程,如果有好的建议和想法可以留言或者私信,如果想深入了解可以访问[官方文档](https://developer.android.google.cn/guide/topics/ui/accessibility/service?hl=zh-cn),方便的话给个赞或者评分吧。

thatCbin 发表于 2023-2-15 16:36

丶汇梦 发表于 2023-2-15 15:00
李跳跳工具还有什么

看这个https://www.52pojie.cn/forum.php?mod=viewthread&tid=1736705&page=1#pid45382792

zhang7069 发表于 2023-2-15 16:27

实用技巧,已收藏

丶汇梦 发表于 2023-2-15 15:00

李跳跳工具还有什么

Pwaerm 发表于 2023-2-15 15:16

丶汇梦 发表于 2023-2-15 15:00
李跳跳工具还有什么

auto.js 也可以,但需要自己写脚本。

夏520 发表于 2023-2-15 15:51

现在的手机,无障碍服务都是不是长久开启的,重启手机或者过10分钟时间就自动关闭服务了,没得玩呢

j717013225 发表于 2023-2-15 15:55

很及时,我搞了好几次自动任务软件也没搞定,

dlovec 发表于 2023-2-15 16:10

学习基础知识爱了

后脑i 发表于 2023-2-15 16:34

夏520 发表于 2023-2-15 15:51
现在的手机,无障碍服务都是不是长久开启的,重启手机或者过10分钟时间就自动关闭服务了,没得玩呢

是的,重启后基本上需要用户手动开启,根据不同的用户使用需求,可以采取后台保活的方式。

thatCbin 发表于 2023-2-15 16:38

比如视频中间暂停后出现广告也能实现关闭
页: [1] 2 3 4 5
查看完整版本: 安卓自动化之无障碍模式