AccessibilityService is very powerful, Google’s intention is to help disabled users better use Android phones through AccessibilityService, so AccessibilityService is also called accessibility assistance service. However, because of the particularity and operability of AccessibilityService, we can do some interesting functions or products through it.
In the past two years, the wechat red envelope grab plug-in and alipay automatic energy collection plug-in are particularly popular. These functions are actually achieved by using AccessibilityService. Their principle is to traverse the view structure, find the corresponding node node to perform the action, can realize the corresponding function. When I first came into contact with auxiliary functions, I searched online and found plug-ins for wechat to grab red envelopes and Alipay to collect energy. Students who are interested can find relevant articles on the Internet by themselves.
Let’s first look at the general use of accessibility.
1. Create auxiliary XML in the XML folder
<? The XML version = "1.0" encoding = "utf-8"? > <accessibility-service xmlns:android="http://schemas.android.com/apk/res/android" android:accessibilityEventTypes="typeAllMask" android:accessibilityFeedbackType="feedbackGeneric" android:accessibilityFlags="flagReportViewIds|flagRetrieveInteractiveWindows|flagRequestEnhancedWebAccessibility" android:canPerformGestures="true" android:canRetrieveWindowContent="true" android:description="@string/accessibility" android:notificationTimeout="1000" />Copy the code
The first thing you need to do is to set up the swtemswings-service in the current XML folder.
AccessibilityEventTypes: to monitor the event or action, such as push notification bar received text messages, interface change accessibilityFeedbackType: Indicates the type of feedback, vibration or voice playback. AccessibilityFlags: indicates the method by which auxiliary functions find nodes. CanPerformGestures: indicates whether gesture distribution canRetrieveWindowContent is accepted by System 7.0 or above: Description: Indicates the description. Enable the text description below the helper function in the Settings to show the user the notificationTimeout: Indicates the shortest time in milliseconds between two accessible events of the same type to be sent to the service packageNames: indicates that the specified app takes effect, if not set, the effect is globalCopy the code
2. Integrate AccessibilityService to achieve customized auxiliary function listeners
public class MyService extends AccessibilityService { @Override protected void onServiceConnected() { super.onServiceConnected(); } @override public void onAccessibilityEvent(AccessibilityEvent event) {// event} @override public void onAccessibilityEvent(AccessibilityEvent event) OnInterrupt () {// interrupt} @override public void onDestroy() {super.ondestroy (); // Service destroyed}}Copy the code
Here we need to inherit AccessibilitySerivce in order to use the accessibility function properly. In general, we will listen for the type of the event in onAccessibilityEvent, and then do something about it.
3. Register accessibility in the manifest file
<service
android:name=".MyService"
android:enabled="true"
android:exported="true"
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>
Copy the code
The writing here is pretty much fixed, except for the XML reference and the name of the service. Accessibility is essentially a service and cannot be used if it is not registered in a manifest file.
4. Get active window or inactive window
Event. GetSource () : Gets the AccessibilityNodeInfo getWindows() of the event source that passed the event: Gets the window that is displayed by default and with which the user can interact GetRootInActiveWindow (): Gets the AccessibilityNodeInfo root node of the current active windowCopy the code
We can use these methods to get Windows and root nodes to find nodes that meet the requirements. There are currently methods for lookups by text and ID.
5. Node Node search method
* @param service * @param text Search text * @param viewId Search viewId used for auxiliary detection can not be passed * @return */ private AccessibilityNodeInfo findChildByText(AccessibilityService service,String text,String viewId) { if (service == null || TextUtils.isEmpty(text)) { return null; } AccessibilityNodeInfo rootInfo = getRootInfo(service); if (rootInfo == null) { return null; } List<AccessibilityNodeInfo> nodeInfos = rootInfo.findAccessibilityNodeInfosByText(text); if (nodeInfos ! = null && nodeInfos.size() > 0) { for (AccessibilityNodeInfo info : nodeInfos) { if (info == null) { continue; } CharSequence contentText = info.getText(); if (TextUtils.isEmpty(contentText)) { info.recycle(); continue; } if (text.equals(contentText)) { if (TextUtils.isEmpty(viewId)) { return info; }else { String viewIdResourceName = info.getViewIdResourceName(); if (viewId.equals(viewIdResourceName)) { return info; } } } } } return null; } /** * Search for node elements according to viewId similar to the above * @param service * @param viewId * @param text * @return */ private AccessibilityNodeInfo findChildByViewId(AccessibilityService service,String viewId,String text) { if (service == null || TextUtils.isEmpty(viewId)) { return null; } AccessibilityNodeInfo rootInfo = getRootInfo(service); if (rootInfo == null) { return null; } List<AccessibilityNodeInfo> nodeInfos = rootInfo.findAccessibilityNodeInfosByViewId(viewId); if (nodeInfos ! = null && nodeInfos.size() > 0) { if (TextUtils.isEmpty(text)) { return nodeInfos.get(0); }else { for (AccessibilityNodeInfo info : nodeInfos) { if (info == null) { continue; } CharSequence contentText = info.getText(); if (TextUtils.isEmpty(contentText)) { info.recycle(); continue; } if (text.equals(contentText)) { if (TextUtils.isEmpty(viewId)) { return info; }else { info.recycle(); } } } } } return null; }Copy the code
Here are two ways to find a node by text and ID. Note that you may find duplicate nodes by text and ID. Especially text. At present, some apps carry out encryption Settings for them, and the ID is variable every time, which makes it impossible to use the id search method.
After obtaining the active window, query the corresponding node node. Remember to recycle the node after using it.
The two methods here may not work for webView or custom view, and cannot be used for corresponding nodes. Later articles will show how to search the view tree by deep traversing and then matching the desired node.
6. After obtaining the Node node, perform the corresponding action, such as holding down, clicking, or sliding. Methods for the performAction
ACTION_CLICK: simulate click ACTION_SELECT: simulate select ACTION_LONG_CLICK: simulate long press ACTION_SCROLL_FORWARD: simulate scroll forward ACTION_SCROLL_BACKWARD: simulate scroll backwardCopy the code
This method has a return value that you can use to see if the action was successfully executed. You can also pass bundle arguments.
If it is for a specific app to do a separate auxiliary function detection, then this method is quite reliable. If you need to find the VIEW ID and then query the corresponding node, you can use the tool Layout Inspector of AndroidStudio. After connecting to the real computer, you can see the view tree structure and find the corresponding ID.