1

I need to read a resource of a 3rd party app using its resource id(which is known to me). I am referencing to this link: How to read window content (using accessibilityService) and evoking UI using draw over other app permission in Android? in order to achieve this functionality.

Problem: AccessibilityNodeInfo object source created using event.getSource() is null.

I would want to create an object of AccessibilityNodeInfo given that I know the Source class.

Chetan chadha
  • 558
  • 1
  • 4
  • 19

1 Answers1

6

Not all events are associated with a "source" node. In fact most events that occur super frequently do not. (Touch interactions, window changes, etc). You need to ensure that you are filtering by events that have a source node. This being said, I doubt that this is what you want, very rarely would events that have source nodes, contain the node that you are looking for. You probably want to, after responding to some subset of accessibility events, explore the AccessibilityNodeInfo Hierarchy starting at the root node.

First, ensure your service is configured correct. I would think that a service_config.xml that looked something like this would suffice.

<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
    android:description="@string/accessibility_service_description"
    android:accessibilityEventTypes="typeWindowsChanged|typeWindowContentChanged|typeViewScrolled"
    android:accessibilityFlags="flagReportViewIds"
    android:canRetrieveWindowContent="true"
    android:accessibilityFeedbackType="feedbackGeneric"
    android:notificationTimeout="1000"
    android:settingsActivity="com.moba11y.basicaccessibilityservice.SettingsActivity"
    />

Some highlights from this:

android:accessibilityEventTypes="typeWindowsChanged|typeWindowContentChanged|typeViewScrolled"

This line aims to catch event types that have any new content drawn on the window. Note: None of these events come with their own event.getSource() node. It will be null EVERY TIME! (IMHO: The event.getSource() for these events should be the root node, but that's just me, ask Google!!!).

android:notificationTimeout="1000"

Get each particular type of event only once per second. Very hand for throttling frequently thrown events, like scroll events.

android:canRetrieveWindowContent="true"

Don't throw exceptions when I call getRootInActiveWindow().

android:accessibilityFlags="flagReportViewIds"

Make sure the viewIDs are reported with the AccessibilityNodeInfo.

With a proper accessibility service configuration, the rest is quite simple. After a subset of accessibility events (the configuration above aims to catch new screen content), search the view hierarchy for your view id.

public static void exploreNodeHierarchy(final AccessibilityNodeInfo nodeInfo, final int depth) {

    //Super important check! AccessibilityNodes can get invalidated at ANY time.
    if (nodeInfo == null) return;

    //Log the nodeINfo to string, with some tabs for visible parent/child relationships.
    Log.d(BasicAccessibilityService.class.getName(), new String(new char[depth]).replace("\0", "\t") + nodeInfo.toString());

    if (nodeInfo.getViewIdResourceName() == "the view id you have") {
        //Do work here.
    }

    for (int i = 0; i < nodeInfo.getChildCount(); ++i) {
        logNodeHierarchy(nodeInfo.getChild(i), depth + 1);
    }
}

@Override
public void onAccessibilityEvent(AccessibilityEvent event) {

    exploreNodeHierarchy(getRootInActiveWindow(), 0);
}
MobA11y
  • 18,425
  • 3
  • 49
  • 76