Android Accessibility

fulvio-ambrosanio-733582-unsplash_meitu_1

公司启用钉钉打卡了,刚开始还挺不适应,总是忘记打卡。

所以就想着,这打卡能不能实现自动化,每天都要记住打卡这个动作,一点儿也不猿。


首先,分析一下,钉钉打卡,必须要在公司附近的范围内,其次,只能是拥有 GPS 定位的钉钉 APP 才行,所以,公司 WiFi 的连接断开,刚好可以作为上下班打卡的时机。

钉钉的打卡页面肯定不会允许第三方应用打开,因此要实现自动打卡功能,肯定需要模拟用户发出点击事件。

能模拟点击事件的,首先想到了Android 辅助功能


AccessibilityService

辅助功能(AccessibilityService)是 Android 系统提供给的一种服务,本身是继承 Service 类的。这个服务提供了增强的用户界面,旨在帮助残障人士或者可能暂时无法与设备充分交互的人们。

当然,现在 AccessibilityService 已经基本偏离了它设计的初衷。

借助 AccessibilityService ,可以实现对页面的监听及模拟点击控制等。


基本使用

使用 AccessibilityService 实际上只需要以下三步即可:

1.继承 AccessibilityService
class AutoPunchCardService : AccessibilityService(){
    //可选。系统会在成功连接上你的服务的时候调用这个方法,在这个方法里你可以做一下初始化工作,
    //例如设备的声音震动管理,也可以调用setServiceInfo()进行配置工作
    override fun onServiceConnected() {
        super.onServiceConnected()
        LogUtils.d("onServiceConnected")
    }

    //必须。这个在系统想要中断AccessibilityService返给的响应时会调用。在整个生命周期里会被调用多次。
    override fun onInterrupt() {
        LogUtils.d("onInterrupt")
    }

    //通过这个函数可以接收系统发送来的AccessibilityEvent,
    //接收来的AccessibilityEvent是经过过滤的,过滤是在配置工作时设置的。
    override fun onAccessibilityEvent(event: AccessibilityEvent) {
        LogUtils.d("事件--> $event.eventType ,app包名--> $event.packageName")
        when (event.eventType) {
            AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED  //收到通知栏消息
            -> LogUtils.d("=== 收到通知栏消息")
            AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED    //界面状态改变
            -> LogUtils.d("=== 界面状态改变," + event.toString())
            AccessibilityEvent.TYPE_VIEW_CLICKED   //点击事件
            ->  LogUtils.d("=== 点击事件" + event.toString())
            AccessibilityEvent.CONTENT_CHANGE_TYPE_TEXT //文本改变
            -> LogUtils.d("=== 文本改变")
        }//省略其他的一堆可以监听的事件

    }

    //可选。在系统将要关闭这个AccessibilityService会被调用。在这个方法中进行一些释放资源的工作
    override fun onUnbind(intent: Intent?): Boolean {
        LogUtils.d("onUnbind")
        return super.onUnbind(intent)
    }
}


2.新建配置文件

在资源目录 res 下新建 xml 文件夹,新建 accessibility_service_config.xml文件

<?xml version="1.0" encoding="utf-8"?>
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
    android:description="@string/accessibility_service_description"
    android:packageNames="com.alibaba.android.rimet"
    android:accessibilityEventTypes="typeAllMask"
    android:accessibilityFlags="flagDefault"
    android:accessibilityFeedbackType="feedbackSpoken"
    android:notificationTimeout="100"
    android:canRetrieveWindowContent="true"
 android:settingsActivity="com.example.android.accessibility.ServiceSettingsActivity"/>

其中 description 是描述;

packageNames 是要监控的 APP 包名;

accessibilityEventTypes 指监控的的事件,typeAllMask  /  AccessibilityEvent.TYPES_ALL_MASK:全局事件响应


3.AndroidMainifest 中注册
<service
    android:name=".AutoPunchCardService"
    android:description="@string/accessibility_service_description"
    android:label="@string/accessibility_service_label"
    android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
    <intent-filter>
        <action android:name="android.accessibilityservice.AccessibilityService"/>
    </intent-filter>

    <meta-data
        android:name="android.accessibilityservice"
        android:resource="@xml/accessibility_service_config"/>
</service>

这样基本上就算配置完成了,不过,想要运行起来,还得用户手动开启辅助设置。


相关方法

1.服务是否开启

AccessibilityService 的服务想要运行,就得让用户手动开启它,那如何判断开启呢?

这需要通过下面的方法:

private fun isAccessibilitySettingsOn(context: Context): Boolean {
    val service = context.packageName + File.separator + AutoPunchCardService::class.java.canonicalName
    val accessibilityEnabled = Settings.Secure.getInt(context.contentResolver,
        Settings.Secure.ACCESSIBILITY_ENABLED)
    val splitter = TextUtils.SimpleStringSplitter(':')
    if (accessibilityEnabled != 1) return false
    val value = Settings.Secure.getString(context.contentResolver,
        Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES) ?: return false
    splitter.setString(value)
    return splitter.contains(service)
}
2.跳转到无障碍设置界面

如果通过判断,发现用户没有开启,就得让用户去打开它, 无障碍设置界面 的设置一般非常深,用户难以到达,这时就得直接打开 无障碍设置界面 的设置页面了。

startActivity(Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS))


模拟点击事件

经过上面的步骤,此时可以正常使用 AccessibilityService,现在来模拟点击事件。

首先,需要寻找界面元素信息,

AccessibilityNodeInfo rootNode = getRootInActiveWindow() 

此方法可以获取当前 Activity 的根节点窗口信息,再通过下面两种方式获取具体的节点信息。

//通过文字,获取控件信息
List<AccessibilityNodeInfo> list = rootNode.findAccessibilityNodeInfosByText("工作");
//通过 id ,获取控件信息,注意 ID 的格式
List<AccessibilityNodeInfo> list = rootNode.findAccessibilityNodeInfosByViewId("com.alibaba.android.rimet:id/home_bottom_tab_button_work");

文本内容很容易获取,看界面就知道,但有时候文本控件不可点击,需要点击父控件,因此文本点击一般这么写:

    private fun click(viewText: String): Boolean {
        val nodeInfo = rootInActiveWindow
        try {//点击前滞留1s
            Thread.sleep(1000)
        } catch (e: InterruptedException) {
            e.printStackTrace()
        }
        if (nodeInfo == null) {
            LogUtils.d("点击失败,rootWindow为空")
            return false
        }

        val list = nodeInfo.findAccessibilityNodeInfosByText(viewText)
        if (list.isEmpty()) {//没有该文字的控件
            LogUtils.d("点击失败," + viewText + "控件列表为空")
            return false
        } else {
            for (info in list) {
                if (viewText == info?.text?.toString()) {
                    return onclick(info)  //遍历点击
                }
            }
            return false
        }
    }

    private fun onclick(view: AccessibilityNodeInfo): Boolean {
        if (view == null) {
            LogUtils.d("node 为空无法点击")
            return false
        }
        if (view.isClickable) {
            view.performAction(AccessibilityNodeInfo.ACTION_CLICK)
            LogUtils.d("view name" + view.className + "+点击成功")
            return true
        } else {
            if (view.parent == null) {
                return false
            }
            return onclick(view.parent)
        }
    }

ViewId 一般需要用到工具 Android Device Monitor 来查看,目前,这个工具在 AS 3.1中打不开了,

需要到 Android SDK/tools/monitor 运行,通过 Hierarchy View 查看 ID 后,需要注意 ID 是书写格式,前面有包名。
最后,模拟点击事件

nodeInfo.performAction(AccessibilityNodeInfo.ACTION_CLICK)

还可以模拟 Home,Back 键

//后退键
performGlobalAction(AccessibilityService.GLOBAL_ACTION_BACK);
//Home键
performGlobalAction(AccessibilityService.GLOBAL_ACTION_HOME);
//模拟左滑
performGlobalAction(AccessibilityService.GESTURE_SWIPE_LEFT);


后记

AccessibilityService 确实强大,网上一些微信抢红包,答题负责工具,都是借此完成。

但它不是万能的,钉钉上的打卡页面是 WebView ,因此无法完成节点寻找,更不可能模拟点击事件。

因此,想通过模拟点击来完成打卡, AccessibilityService 无法胜任,最后,还是得 Root 。

通过 Root 后执行 shell 命令来完成模拟点击事件。


设置页面的常量表

为了方便,这里列出设置界面所有的 Action 常量

常量字段示意
ACTION_SETTINGS系统设置界面
ACTION_APN_SETTINGSAPN设置界面
ACTION_LOCATION_SOURCE_SETTINGS定位设置界面
ACTION_AIRPLANE_MODE_SETTINGS更多连接方式设置界面
ACTION_DATA_ROAMING_SETTINGS双卡和移动网络设置界面
ACTION_ACCESSIBILITY_SETTINGS无障碍设置界面
ACTION_SYNC_SETTINGS同步设置界面
ACTION_ADD_ACCOUNT添加账户界面
ACTION_NETWORK_OPERATOR_SETTINGS选取运营商的界面
ACTION_SECURITY_SETTINGS安全设置界面
ACTION_PRIVACY_SETTINGS备份重置设置界面
ACTION_VPN_SETTINGSVPN设置界面,可能不存在
ACTION_WIFI_SETTINGS无线网设置界面
ACTION_WIFI_IP_SETTINGSWIFI的IP设置
ACTION_BLUETOOTH_SETTINGS蓝牙设置
ACTION_CAST_SETTINGS投射设置
ACTION_DATE_SETTINGS日期时间设置
ACTION_SOUND_SETTINGS声音设置
ACTION_DISPLAY_SETTINGS显示设置
ACTION_LOCALE_SETTINGS语言设置
ACTION_VOICE_INPUT_SETTINGS辅助应用和语音输入设置
ACTION_INPUT_METHOD_SETTINGS语言和输入法设置
ACTION_USER_DICTIONARY_SETTINGS个人字典设置界面
ACTION_INTERNAL_STORAGE_SETTINGS存储空间设置的界面
ACTION_SEARCH_SETTINGS搜索设置界面
ACTION_APPLICATION_DEVELOPMENT_SETTINGS开发者选项设置
ACTION_DEVICE_INFO_SETTINGS手机状态信息的界面
ACTION_DREAM_SETTINGS互动屏保设置的界面
ACTION_NOTIFICATION_LISTENER_SETTINGS通知使用权设置的界面
ACTION_NOTIFICATION_POLICY_ACCESS_SETTINGS勿扰权限设置的界面
ACTION_CAPTIONING_SETTINGS字幕设置的界面
ACTION_PRINT_SETTINGS打印设置界面
ACTION_BATTERY_SAVER_SETTINGS节电助手界面
ACTION_HOME_SETTINGS主屏幕设置界面


参考

Android AccessibilityService使用注意

(AccessibilityService) Android 辅助功能笔记

Android Accessibility辅助功能类的学习

AccessibilityService从入门到出轨

微信检查被删好友(Android Accessibility 学习实践 )

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Android无障碍服务是一种功能,可以帮助用户更轻松地使用设备,包括视觉、听觉、运动和认知障碍。它可以通过提供语音反馈、触觉反馈、放大屏幕等方式来帮助用户。无障碍服务还可以自动化一些任务,例如读取通知、填写表单等。 ### 回答2: AndroidAccessibilityService是一个系统级别的服务,可以帮助用户在Android系统上更好的使用功能,尤其是对于那些具有身体障碍、视觉障碍和听觉障碍等特殊需求的用户来说,AccessibilityService可以提供完美的解决方案,通过对系统上的事件和用户交互进行监听和分析,然后帮助用户轻松的实现他们想要达成的功能。 AccessibilityService的工作原理是通过监听Android系统上的一些事件,比如界面控件的变化、通知的出现、文本输入等等,然后对这些事件进行判断和处理,最终将结果反馈给用户。通过AccessibilityService框架,用户可以设置所需要实现的功能,并在设备中启用对应的服务,这样AccessibilityService便能够按照用户的需求来对事件进行分析,并在需要时向用户发送通知或执行指定的操作。 AccessibilityService在Android的应用场景中得到了广泛的应用,它可以用来优化屏幕导航、改善文字输入、提高阅读体验、帮助用户控制设备等等。比如对于肢体残疾的用户,他们可能难以使用传统的手持设备,但是通过使用可支持无障碍功能的设备,他们可以轻松地使用语音识别来输入文字、使用语音命令来操作设备、使用触控手势来控制屏幕等等。 总之,AccessibilityService为使用Android设备的用户提供了一个更加便捷和自然的界面和服务,有效的帮助了一部分特殊需求的用户,使得智能手持设备向着更加无障碍化、智能化、人性化的方向不断发展和完善。 ### 回答3: Android AccessibilityService是一个服务,它可以让用户在使用设备时更加方便,无论用户是否患有视力或听力障碍。通过使用Android AccessibilityService,开发人员可以改善用户体验,例如提高可访问性、方便操作等。许多应用程序都可以从Android AccessibilityService中受益,这些应用程序包括辅助浏览、读取屏幕、听屏幕等。 Android AccessibilityService的基本原理是将一个服务添加到Android系统中,该服务可以监视系统事件,并在用户发出命令时自动触发相应的功能。例如,当用户在屏幕上点击文本框时,AccessibilityService可以自动弹出一个软件键盘以便用户进行输入。 开发人员可以使用Android AccessibilityService API来开发自己的服务,并实现自己的功能。例如,开发人员可以使用这些API来获取用户在屏幕上执行的所有事件,并根据这些事件来触发相应的功能,例如高亮屏幕,增加音量等。 总之,Android AccessibilityService使得使用设备更加方便,无论用户是否具有视力或听力障碍,而且可以广泛应用于各种应用程序中。开发人员在设计应用程序时应该考虑到这一点,并尽可能地提高应用程序的可访问性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值