android 辅助功能 AccessibilityService

2024-02-28 10:48

本文主要是介绍android 辅助功能 AccessibilityService,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

对于那些由于视力、听力或其它身体原因导致不能方便使用Android智能手机的用户,Android提供了Accessibility功能和服务帮助这些用户更加简单地操作设备,包括文字转语音(不支持中文)、触觉反馈、手势操作、轨迹球和手柄操作。开发者可以搭建自己的Accessibility服务,这可以加强可用性,例如声音提示,物理反馈,和其他可选的操作模式。

 

AccessibilityService官方简介:

The classes in this package are used for development of accessibility service that provide alternative or augmented feedback to the user. 
使用这个类可以开发用于给用户提供替换或者是增强反馈的辅助功能服务。

An AccessibilityService runs in the background and receives callbacks by the system when AccessibilityEvents are fired. Such events denote some state transition in the user interface, for example, the focus has changed, a button has been clicked, etc. Such a service can optionally request the capability for querying the content of the active window. Development of an accessibility service requires extends this class and implements its abstract methods. 
一个AccessibilityService在后台运行并接收系统AccessibilityEvents事件的回调,当用户界面的状态发生改变时会触发AccessibilityEvents事件,例如焦点的变化,点击一个按钮。这个服务可以获取到活动窗口的内容,开发一个辅助功能服务需要继承AccessibilityService并实现其中的抽象方法。

An AccessibilityServiceInfo describes an AccessibilityServiceInfo. The system notifies an AccessibilityService for AccessibilityEvents according to the information encapsulated in this class. 
一个AccessibilityService有一个用于描述AccessibilityService的AccessibilityServiceInfo对象,系统会通知AccessibilityService根据AccessibilityServiceInfo把信息装进AccessibilityEvents中。

继承AccessibilityService并实现其中的抽象方法。

下面是我Service类:

public class MyService extends AccessibilityService {private int code = INSTALL;private static final int INSTALL = 0;private static final int NEXT = 1;private static final int FINISH = 2;/*** 页面变化回调事件* @param event event.getEventType() 当前事件的类型;*              event.getClassName() 当前类的名称;*              event.getSource() 当前页面中的节点信息;*              event.getPackageName() 事件源所在的包名*/@Overridepublic void onAccessibilityEvent(AccessibilityEvent event) {// 事件页面节点信息不为空if (event.getSource() != null) {// 判断事件页面所在的包名,这里是自己if (event.getPackageName().equals(getApplicationContext().getPackageName())) {switch (code) {case INSTALL:click(event, "安装", TextView.class.getName());Log.d("test=======", "安装");code = NEXT;break;case NEXT:click(event, "下一步", Button.class.getName());Log.d("test=======", "下一步");code = FINISH;break;case FINISH:click(event, "完成", TextView.class.getName());Log.d("test=======", "完成");code = INSTALL;break;default:break;}}} else {Log.d("test=====", "the source = null");}}/*** 模拟点击* @param event 事件* @param text 按钮文字* @param widgetType 按钮类型,如android.widget.Button,android.widget.TextView*/private void click(AccessibilityEvent event, String text, String widgetType) {// 事件页面节点信息不为空if (event.getSource() != null) {// 根据Text搜索所有符合条件的节点, 模糊搜索方式; 还可以通过ID来精确搜索findAccessibilityNodeInfosByViewIdList<AccessibilityNodeInfo> stop_nodes = event.getSource().findAccessibilityNodeInfosByText(text);// 遍历节点if (stop_nodes != null && !stop_nodes.isEmpty()) {AccessibilityNodeInfo node;for (int i = 0; i < stop_nodes.size(); i++) {node = stop_nodes.get(i);// 判断按钮类型if (node.getClassName().equals(widgetType)) {// 可用则模拟点击if (node.isEnabled()) {node.performAction(AccessibilityNodeInfo.ACTION_CLICK);}}}}}}/*** 中断AccessibilityService的反馈时调用*/@Overridepublic void onInterrupt() {Log.d("test=====", "Interrupt");}
}

AccessibilityService里几个重要的方法:

  • onServiceConnected() - 可选。系统会在成功连接上你的服务的时候调用这个方法,在这个方法里你可以做一下初始化工作,例如设备的声音震动管理,也可以调用setServiceInfo()进行配置AccessibilityServiceInfo。
  • onAccessibilityEvent() - 必须。通过这个函数可以接收系统发送来的AccessibilityEvent,接收来的AccessibilityEvent是经过过滤的,过滤是在配置工作时设置的。
  • onInterrupt() - 必须。这个在系统想要中断AccessibilityService返给的响应时会调用。在整个生命周期里会被调用多次。
  • onUnbind() - 可选。在系统将要关闭这个AccessibilityService会被调用。在这个方法中进行一些释放资源的工作。

之后在AndroidManifest文件里注册并添加相应的权限:

<serviceandroid:name=".MyService"android:label="辅助功能"android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"><intent-filter><action android:name="android.accessibilityservice.AccessibilityService" /></intent-filter>...
</service>

配置AccessibilityService

  1. 可以在onServiceConnected()方法里进行,建立一个AccessibilityServiceInfo对象,通过这个对象设置监听系统事件类型,服务的反馈类型(震动,语音,声音),事件时间间隔,你想要监听的App的包名。最后调用setServiceInfo()进行设置,如:
 @Overrideprotected void onServiceConnected() {super.onServiceConnected();AccessibilityServiceInfo info = new AccessibilityServiceInfo();info.eventTypes = AccessibilityEvent.TYPES_ALL_MASK;info.packageNames = PACKAGE_NAMES; ...配置setServiceInfo(info);}

从Android4.0开始,开发者可以通过在AndroidManifest里添加标签配置AccessibilityService,在标签里指出配置文件的位置,如: 

res/xml/accessibility_service_info.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:accessibilityEventTypes="typeAllMask"android:accessibilityFeedbackType="feedbackGeneric"android:notificationTimeout="100"android:accessibilityFlags="flagDefault"android:canRetrieveWindowContent="true"android:packageNames="top.cokernut.sample"android:settingsActivity="com.example.android.accessibility.ServiceSettingsActivity"  /><!--android:description="@string/accessibility_service_description" 描述android:accessibilityEventTypes="typeAllMask"  事件类型android:accessibilityFeedbackType="feedbackGeneric" 反馈类型,声音、震动等android:canRetrieveWindowContent="true", 允许获取手机页面中的信息android:packageNames="top.cokernut.sample" 要监听的包名,过滤作用android:settingsActivity="packname.android.accessibility.ServiceSettingsActivity" packname写自己App的包名-->

事件类型(EventType):

        #TYPES_ALL_MASK:所有类型#TYPE_VIEW_CLICKED :单击#TYPE_VIEW_LONG_CLICKED :长按#TYPE_VIEW_SELECTED :选中#TYPE_VIEW_FOCUSED :获取焦点#TYPE_VIEW_TEXT_CHANGED :文字改变#TYPE_WINDOW_STATE_CHANGED :窗口状态改变#TYPE_NOTIFICATION_STATE_CHANGED :通知状态改变#TYPE_VIEW_HOVER_ENTER#TYPE_VIEW_HOVER_EXIT#TYPE_TOUCH_EXPLORATION_GESTURE_START#TYPE_TOUCH_EXPLORATION_GESTURE_END#TYPE_WINDOW_CONTENT_CHANGED#TYPE_VIEW_SCROLLED#TYPE_VIEW_TEXT_SELECTION_CHANGED#TYPE_ANNOUNCEMENT#TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY#TYPE_GESTURE_DETECTION_START#TYPE_GESTURE_DETECTION_END#TYPE_TOUCH_INTERACTION_START#TYPE_TOUCH_INTERACTION_END#TYPE_WINDOWS_CHANGED

然后在AndroidManifest文件里把配置文件配置到AccessibilityService上:

<serviceandroid:name=".MyService"android:label="辅助功能"android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"><intent-filter><action android:name="android.accessibilityservice.AccessibilityService" /></intent-filter><meta-dataandroid:name="android.accessibilityservice"android:resource="@xml/accessibility_service_info" />
</service>

到此一个AccessibilityService的开发就完成了。

其他

MainActivity:

public class MainActivity extends AppCompatActivity implements View.OnClickListener {private TextView mInstallTV;private TextView mFinishTV;private TextView mStartTV;private TextView mStopTV;private Button mInstallBT;private Button mNextBT;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);mInstallTV      = (TextView) findViewById(R.id.tv_install);mStartTV        = (TextView) findViewById(R.id.tv_start);mStopTV         = (TextView) findViewById(R.id.tv_stop);mFinishTV       = (TextView) findViewById(R.id.tv_finish);mInstallBT      = (Button) findViewById(R.id.bt_install);mNextBT         = (Button) findViewById(R.id.bt_next);mInstallTV      .setOnClickListener(this);mFinishTV       .setOnClickListener(this);mInstallBT      .setOnClickListener(this);mNextBT         .setOnClickListener(this);mStartTV        .setOnClickListener(this);mStopTV         .setOnClickListener(this);}@Overrideprotected void onRestart() {super.onRestart();}@Overridepublic void onClick(View v) {switch (v.getId()) {case R.id.tv_start:if (serviceIsRunning()) {showToast("服务已经在运行!");} else {startAccessibilityService();}break;case R.id.tv_stop:startActivity(new Intent(MainActivity.this, SecondActivity.class));break;case R.id.tv_install:showToast(((TextView)v).getText().toString());break;case R.id.tv_finish:showToast(((TextView)v).getText().toString());break;case R.id.bt_install:showToast(((Button)v).getText().toString());break;case R.id.bt_next:showToast(((Button)v).getText().toString());break;default:showToast("未知按钮");break;}}private void showToast(final String text) {//AccessibilityService触发事件是异步的,要回到UI线程改变UIrunOnUiThread(new Runnable() {@Overridepublic void run() {Toast.makeText(MainActivity.this, "点击了" + text, Toast.LENGTH_LONG).show();Log.d("text=====", "=====" + text);}});}/*** 判断自己的应用的AccessibilityService是否在运行** @return*/private boolean serviceIsRunning() {ActivityManager am = (ActivityManager) getApplicationContext().getSystemService(Context.ACTIVITY_SERVICE);List<ActivityManager.RunningServiceInfo> services = am.getRunningServices(Short.MAX_VALUE);for (ActivityManager.RunningServiceInfo info : services) {if (info.service.getClassName().equals(getPackageName() + ".MyService")) {return true;}}return false;}/*** 前往设置界面开启服务*/private void startAccessibilityService() {new AlertDialog.Builder(this).setTitle("开启辅助功能").setIcon(R.mipmap.ic_launcher).setMessage("使用此项功能需要您开启辅助功能").setPositiveButton("立即开启", new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface dialog, int which) {// 隐式调用系统设置界面Intent intent = new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS);startActivity(intent);}}).create().show();}
}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"tools:context="top.cokernut.sample.MainActivity"><LinearLayoutandroid:id="@+id/activity_main"android:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="vertical"android:gravity="center_horizontal"android:paddingBottom="@dimen/activity_vertical_margin"android:paddingLeft="@dimen/activity_horizontal_margin"android:paddingRight="@dimen/activity_horizontal_margin"android:paddingTop="@dimen/activity_vertical_margin"><TextViewandroid:id="@+id/tv_install"android:layout_width="wrap_content"android:layout_height="wrap_content"android:padding="30dp"android:background="#00AA66"android:layout_margin="10dp"android:text="安装TV" /><Buttonandroid:id="@+id/bt_install"android:layout_width="wrap_content"android:layout_height="wrap_content"android:padding="30dp"android:text="安装Btn" /><Buttonandroid:id="@+id/bt_next"android:layout_width="wrap_content"android:layout_height="wrap_content"android:padding="30dp"android:text="下一步Btn" /><TextViewandroid:id="@+id/tv_finish"android:layout_width="wrap_content"android:layout_height="wrap_content"android:padding="30dp"android:background="#00AA66"android:layout_margin="10dp"android:text="完成" /><TextViewandroid:id="@+id/tv_start"android:layout_width="match_parent"android:layout_height="wrap_content"android:gravity="center"android:padding="30dp"android:background="#0088FF"android:layout_margin="10dp"android:text="开始" /><TextViewandroid:id="@+id/tv_stop"android:layout_width="match_parent"android:layout_height="wrap_content"android:gravity="center"android:padding="30dp"android:background="#0088FF"android:layout_margin="10dp"android:text="停止" /></LinearLayout>
</ScrollView>

这篇关于android 辅助功能 AccessibilityService的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/755285

相关文章

Python使用FastAPI实现大文件分片上传与断点续传功能

《Python使用FastAPI实现大文件分片上传与断点续传功能》大文件直传常遇到超时、网络抖动失败、失败后只能重传的问题,分片上传+断点续传可以把大文件拆成若干小块逐个上传,并在中断后从已完成分片继... 目录一、接口设计二、服务端实现(FastAPI)2.1 运行环境2.2 目录结构建议2.3 serv

Debian 13升级后网络转发等功能异常怎么办? 并非错误而是管理机制变更

《Debian13升级后网络转发等功能异常怎么办?并非错误而是管理机制变更》很多朋友反馈,更新到Debian13后网络转发等功能异常,这并非BUG而是Debian13Trixie调整... 日前 Debian 13 Trixie 发布后已经有众多网友升级到新版本,只不过升级后发现某些功能存在异常,例如网络转

Android协程高级用法大全

《Android协程高级用法大全》这篇文章给大家介绍Android协程高级用法大全,本文结合实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友跟随小编一起学习吧... 目录1️⃣ 协程作用域(CoroutineScope)与生命周期绑定Activity/Fragment 中手

基于Java和FFmpeg实现视频压缩和剪辑功能

《基于Java和FFmpeg实现视频压缩和剪辑功能》在视频处理开发中,压缩和剪辑是常见的需求,本文将介绍如何使用Java结合FFmpeg实现视频压缩和剪辑功能,同时去除数据库操作,仅专注于视频处理,需... 目录引言1. 环境准备1.1 项目依赖1.2 安装 FFmpeg2. 视频压缩功能实现2.1 主要功

使用Python实现无损放大图片功能

《使用Python实现无损放大图片功能》本文介绍了如何使用Python的Pillow库进行无损图片放大,区分了JPEG和PNG格式在放大过程中的特点,并给出了示例代码,JPEG格式可能受压缩影响,需先... 目录一、什么是无损放大?二、实现方法步骤1:读取图片步骤2:无损放大图片步骤3:保存图片三、示php

深度解析Python yfinance的核心功能和高级用法

《深度解析Pythonyfinance的核心功能和高级用法》yfinance是一个功能强大且易于使用的Python库,用于从YahooFinance获取金融数据,本教程将深入探讨yfinance的核... 目录yfinance 深度解析教程 (python)1. 简介与安装1.1 什么是 yfinance?

Python脚本轻松实现检测麦克风功能

《Python脚本轻松实现检测麦克风功能》在进行音频处理或开发需要使用麦克风的应用程序时,确保麦克风功能正常是非常重要的,本文将介绍一个简单的Python脚本,能够帮助我们检测本地麦克风的功能,需要的... 目录轻松检测麦克风功能脚本介绍一、python环境准备二、代码解析三、使用方法四、知识扩展轻松检测麦

Java实现TXT文件导入功能的详细步骤

《Java实现TXT文件导入功能的详细步骤》在实际开发中,很多应用场景需要将用户上传的TXT文件进行解析,并将文件中的数据导入到数据库或其他存储系统中,本文将演示如何用Java实现一个基本的TXT文件... 目录前言1. 项目需求分析2. 示例文件格式3. 实现步骤3.1. 准备数据库(假设使用 mysql

Android 缓存日志Logcat导出与分析最佳实践

《Android缓存日志Logcat导出与分析最佳实践》本文全面介绍AndroidLogcat缓存日志的导出与分析方法,涵盖按进程、缓冲区类型及日志级别过滤,自动化工具使用,常见问题解决方案和最佳实... 目录android 缓存日志(Logcat)导出与分析全攻略为什么要导出缓存日志?按需过滤导出1. 按

Springboot项目登录校验功能实现

《Springboot项目登录校验功能实现》本文介绍了Web登录校验的重要性,对比了Cookie、Session和JWT三种会话技术,分析其优缺点,并讲解了过滤器与拦截器的统一拦截方案,推荐使用JWT... 目录引言一、登录校验的基本概念二、HTTP协议的无状态性三、会话跟android踪技术1. Cook