Many Android users have various situations that lead them to interact with their phones in different ways. For those users who are unable to use Android smartphones due to vision, hearing or other physical reasons, Android provides Accessibility functions and services to help these users operate the device more simply, including text-to-speech, tactile feedback, gesture operation, trackball and Handle operation. Developers can build their own Accessibility services, which can enhance the usability of applications, such as sound prompts, physical feedback, and other optional operating modes.
Its specific implementation is to run in the background through the AccessibilityService service and receive callbacks for specified events through AccessibilityEvent. Such events represent some state transitions by the user in the interface, such as: focus changed, a button was clicked, etc. Such services may optionally have the ability to request the contents of the active window. Simply put, AccessibilityService is a background monitoring service. When the content you monitor changes, the callback method of the background service will be called.
Write your own Service class and override the onServiceConnected() method, onAccessibilityEvent() method and onInterrupt() method
public class QHBAccessibilityService extends AccessibilityService {
/**
* 当启动服务的时候就会被调用
*/
@ Override
protected void onServiceConnected () {
super . onServiceConnected ();
}
/**
* 监听窗口变化的回调
*/
@ Override
public void onAccessibilityEvent ( AccessibilityEvent event ) {
int eventType = event . getEventType ();
//根据事件回调类型进行处理
}
/**
* 中断服务的回调
*/
@ Override
public void onInterrupt () {
}
}
The following is an introduction to the commonly used methods in AccessibilityService
Since it is a background service, we need to configure the service information in manifests
< service
android : name = " .AccessibilityService.QHBAccessibilityService "
android : enabled = " true "
android : exported = " true "
android : label = " @string/label "
android : permission = " android.permission.BIND_ACCESSIBILITY_SERVICE " >
< intent-filter >
< action android : name = " android.accessibilityservice.AccessibilityService " />
</ intent-filter >
</ service >
We must pay attention: any information configuration error will make the service unresponsive
Configuring service parameters refers to: configuring to accept events of specified types, listen to specified packages, retrieve window content, obtain the time of event types, etc. There are two ways to configure service parameters:
Add the meta-data tag to the original manifests to specify the xml file
< service
android : name = " .AccessibilityService.QHBAccessibilityService "
android : enabled = " true "
android : exported = " true "
android : label = " @string/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 >
Next is the configuration of the accessibility_service_config file
<? xml version = " 1.0 " encoding = " utf-8 " ?>
< accessibility-service xmlns : android = " http://schemas.android.com/apk/res/android "
android : accessibilityEventTypes = " typeNotificationStateChanged|typeWindowStateChanged|typeWindowContentChanged|typeWindowsChanged "
android : accessibilityFeedbackType = " feedbackGeneric "
android : accessibilityFlags = " flagDefault "
android : canRetrieveWindowContent = " true "
android : description = " @string/description "
android : notificationTimeout = " 100 "
android : packageNames = " com.tencent.mm " />
The following is an introduction to xml parameters
accessibilityEventTypes: Indicates which changes in the interface the service is interested in, that is, which events are notified, such as window opening, sliding, focus change, long press, etc. The specific value can be found in the AccessibilityEvent class. For example, typeAllMask means accepting all event notifications.
accessibilityFeedbackType: indicates the feedback method, such as voice playback or vibration
canRetrieveWindowContent: Indicates whether the service can access the content in the active window. That is, if you want to get the form content in the service, you need to set its value to true
description: A description of the accessibility feature, as shown in the figure below
notificationTimeout: The time interval for accepting events, usually set to 100
packageNames: Indicates which package the service is used to monitor for events. Here we take the package name of WeChat as an example.
Configure AccessibilityServiceInfo information for our AccessibilityService through code. Here we can extract it into a method for setting
private void settingAccessibilityInfo () {
String [] packageNames = { "com.tencent.mm" };
AccessibilityServiceInfo mAccessibilityServiceInfo = new AccessibilityServiceInfo ();
// 响应事件的类型,这里是全部的响应事件(长按,单击,滑动等)
mAccessibilityServiceInfo . eventTypes = AccessibilityEvent . TYPES_ALL_MASK ;
// 反馈给用户的类型,这里是语音提示
mAccessibilityServiceInfo . feedbackType = AccessibilityServiceInfo . FEEDBACK_SPOKEN ;
// 过滤的包名
mAccessibilityServiceInfo . packageNames = packageNames ;
setServiceInfo ( mAccessibilityServiceInfo );
}
The AccessibilityServiceInfo class is involved here. The AccessibilityServiceInfo class is used to configure AccessibilityService information. This class contains a large number of constant fields for configuration and xml attributes. Common ones include: accessibilityEventTypes, canRequestFilterKeyEvents, packageNames, etc.
Here we need to manually turn on this function in the accessibility function, otherwise we cannot continue. You can open the system's accessibility function list through the following code
Intent intent = new Intent ( Settings . ACTION_ACCESSIBILITY_SETTINGS );
startActivity ( intent );
Since we monitor the notification bar and interface of the event, when the notification bar or interface of the specified packageNames changes, our event will be called back through onAccessibilityEvent, and then the event will be processed.
@ Override
public void onAccessibilityEvent ( AccessibilityEvent event ) {
int eventType = event . getEventType ();
//根据事件回调类型进行处理
switch ( eventType ) {
//当通知栏发生改变时
case AccessibilityEvent . TYPE_NOTIFICATION_STATE_CHANGED :
break ;
//当窗口的状态发生改变时
case AccessibilityEvent . TYPE_WINDOW_STATE_CHANGED :
break ;
}
}
When we receive a notification on WeChat, a push message will arrive in the status bar. At this time, it will be monitored by TYPE_NOTIFICATION_STATE_CHANGED and the content inside will be executed. When we switch the WeChat interface or use WeChat, this time it will be monitored by TYPE_WINDOW_STATE_CHANGED. Execute the content inside
AccessibilityEvent methods
After obtaining the interface window changes, it is necessary to obtain the control nodes at this time. The nodes of the entire window are essentially a tree structure. The node information is operated through the following
AccessibilityNodeInfo nodeInfo = getRootInActiveWindow ();
//通过文本找到对应的节点集合
List < AccessibilityNodeInfo > list = nodeInfo . findAccessibilityNodeInfosByText ( text );
//通过控件ID找到对应的节点集合,如com.tencent.mm:id/gd
List < AccessibilityNodeInfo > list = nodeInfo . findAccessibilityNodeInfosByViewId ( clickId );
After we obtain the node information, we can perform simulated clicks, long presses and other operations on the control nodes. The AccessibilityNodeInfo class provides the performAction() method to allow us to perform simulated operations. For specific operations, please refer to the official documentation. Commonly used operations are listed here.
//模拟点击
accessibilityNodeInfo . performAction ( AccessibilityNodeInfo . ACTION_CLICK );
//模拟长按
accessibilityNodeInfo . performAction ( AccessibilityNodeInfo . ACTION_LONG_CLICK );
//模拟获取焦点
accessibilityNodeInfo . performAction ( AccessibilityNodeInfo . ACTION_FOCUS );
//模拟粘贴
accessibilityNodeInfo . performAction ( AccessibilityNodeInfo . ACTION_PASTE );
When our phone is connected to the USB cable, select the device in Android Device Monitor and open the Dump View Hierarchy for UI Automator tool, through which we can obtain control information
Q:
AccessibilityNodeInfo nodeInfo = getRootInActiveWindow();
always reports a null pointer. Has the blogger encountered this problem? A: I have also encountered a problem like yours. I think it is in the "Configuration Service Parameters". I just started configuring it in the code, and it also returned null. I feel that I did not write this android:canRetrieveWindowContent="true", but it didn't. I found the setting corresponding to android:canRetrieveWindowContent="true", so I changed it to configure it in xml, and it was fine.
Q: How do you obtain these class names com.tencent.mm.ui.LauncherUI? A: String className = event.getClassName().toString(); Use Log to print it out, and open WeChat to view the Log information
Original text: Hensen_’s blog