Search code examples
androidaccessibilityaccessibilityservice

Android AccessibilityService performAction() method not working


I am developing an accessibility service for Android. The service calls an app, and that app has a RecyclerView. Then I want to click on an element of the RecyclerView with performAction(AccessibilityNodeInfo.ACTION_CLICK) but it is not working. I know there are a few similar questions but none of them works for me. Also I checked the official documentation for the class of the performAction method https://developer.android.com/reference/android/view/accessibility/AccessibilityNodeInfo

This is my code:

@Override
public void onAccessibilityEvent(Accessibility event){
    AccessibilityNodeInfo source = event.getSource();
    if(source != null){
        List<AccessibilityNodeInfo> list = source.findAccessibilityNOdeInfosByText("mystring");

        list.get(0).performAction(AccessibilityNodeInfo.ACTION_CLICK);
    }
}

This is my configuration xml file:

<accessibility-srvice xmlns...
    android:accessibilityFeedbackType = "feedbackGeneric"
    android:AccessibilityFlags = "flagDefault"
    android:canPerformGestures = "true"
    android:canRetrieveWIndowCOntent = "true"

I think I misunderstood something, but i don't know what can be. Any help is appreciated.


Solution

  • The simple answer is that while finding the node by text is fine, that particular node was not the node with the desired onClick event. The solution is to call

    list.get(0).getParent().performAction(AccessibilityNodeInfo.ACTION_CLICK)
    

    The clarifying discussion is below


    I think .performAction(AccessibilityNodeInfo.ACTION_CLICK) is right, but there might be some other concerns. Sorry for posting as an answer but a comment is too small.

    1. Are you sure the onAccessibilityEvent is being called? I don't think that is the right event, but I can't be sure. Maybe put a log in there to ensure it's calling the event when you expect it to be called.

    2. Also, looking at the source might restrict your search, maybe instead of event.getSource() try using rootInActiveWindow (I use Kotlin so it might have a method, see https://developer.android.com/reference/android/accessibilityservice/AccessibilityService#getRootInActiveWindow(int))

    EDIT: 28 March 2022

    I have run this code on my own accessibility service and it does click the button. But it's very prone to overflow.

        var ranOnce = false // prevent overflow
        override fun onAccessibilityEvent(event: AccessibilityEvent?) {
            if (event == null) return
    
            if (event.eventType == TYPE_WINDOW_STATE_CHANGED) return
    
            if (event.source != null && !ranOnce) {
                val nodeList = rootInActiveWindow.findAccessibilityNodeInfosByText("Menu")
                //event.source.findAccessibilityNodeInfosByText("Menu") // <-- always nothing in list
    
                Log.d("onAccessibilityEvent", "List of nodes: $nodeList")
                if (nodeList.size > 0) {
                    android.util.Log.d("onAccessibilityEvent", "Node info: ${nodeList[0]}")
                    ranOnce = true
                    nodeList[0].performAction(AccessibilityNodeInfo.ACTION_CLICK) //<-- caused an infinite loop!
                } else {
                    Log.d("onAccessibilityEvent", "No nodes found")
                }
            } else {
                Log.d("onAccessibilityEvent", "event.source is null!")
            }
        }