How to click button in settings using AccessibilityService?

The selected answer works on API 18 and above since it relays on findAccessibilityNodeInfosByViewId which was added in API 18. I ended up writing this class to support API 17 and below.

ResourcesCompat class finds the resources identified with the given activty which in our case should be Android's Setting activity. You can get the ComponentName of settings activity by calling this function when handling the accessibility event in you Accessibility Service.

    public static ComponentName getForegroundActivity(Context context) {
        ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
        List<ActivityManager.RunningTaskInfo> taskInfo = am.getRunningTasks(1);
        ComponentName topActivity = taskInfo.get(0).topActivity;
        return topActivity;
    }

A good place to call init(...) is in onAccessibilityEvent when you're first handling the TYPE_WINDOW_STATE_CHANGED event as thecr0w described.

public class ResourcesCompat {
    private final static String RESOURCE_TYPE = "string";

    private String mResourcesPackageName;
    private Resources mResources;

    /**
     * Find the resource file for a specific activity
     *
     * @param context
     * @param settingsPackageName
     * @param settingsClassName
     */
    public void init(Context context, String settingsPackageName, String settingsClassName) {
        try {
            mResourcesPackageName = settingsPackageName;
            ComponentName settingsComponentName = new ComponentName(settingsPackageName, settingsPackageName + settingsClassName);
            mResources = context.getPackageManager().getResourcesForActivity(settingsComponentName);
        } catch (PackageManager.NameNotFoundException e) {
        }
    }

    /**
     * Return the localised string for the given resource name.
     * @param resourceName The name of the resource definition in strings.xml
     */
    public String getString(String resourceName) {
        int resourceId = getIdentifier(resourceName);
        return resourceId > 0 ? mResources.getString(resourceId) : null;
    }

    /**
     * Return a resource identifier for the given resource name.
     * @param resourceName The name of the desired resource.
     * @return int The associated resource identifier. Returns 0 if no such resource was found. (0 is not a valid resource ID.)
     */
    private int getIdentifier(String resourceName) {
        return mResources.getIdentifier(resourceName, RESOURCE_TYPE, mResourcesPackageName);
    }
}

Some manufacturers like to move classes around and rename default strings (cough Samsung cough Xiomi cough) So make sure you cover all cases and handle errors and exceptions.

Finally, find your view by name. here, id can be 'force_stop' for example

private List<AccessibilityNodeInfo> findAccessibilityNodeInfosByName(AccessibilityNodeInfo source, String id) {
        String nodeText = mResourcesCompat.getString(id);
        if (nodeText != null) {
            return source.findAccessibilityNodeInfosByText(nodeText);
        }

        return null;
}

Open one app's Appinfo with force close button enabled to test:

public class MyAccessibilityService extends AccessibilityService {
    private static final String TAG = MyAccessibilityService.class
            .getSimpleName();

    @Override
    public void onAccessibilityEvent(AccessibilityEvent event) {
        Log.i(TAG, "ACC::onAccessibilityEvent: " + event.getEventType());

        //TYPE_WINDOW_STATE_CHANGED == 32
        if (AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED == event
                .getEventType()) {
            AccessibilityNodeInfo nodeInfo = event.getSource();
            Log.i(TAG, "ACC::onAccessibilityEvent: nodeInfo=" + nodeInfo);
            if (nodeInfo == null) {
                return;
            }

            List<AccessibilityNodeInfo> list = nodeInfo
                    .findAccessibilityNodeInfosByViewId("com.android.settings:id/left_button");
            for (AccessibilityNodeInfo node : list) {
                Log.i(TAG, "ACC::onAccessibilityEvent: left_button " + node);
                node.performAction(AccessibilityNodeInfo.ACTION_CLICK);
            }

            list = nodeInfo
                    .findAccessibilityNodeInfosByViewId("android:id/button1");
            for (AccessibilityNodeInfo node : list) {
                Log.i(TAG, "ACC::onAccessibilityEvent: button1 " + node);
                node.performAction(AccessibilityNodeInfo.ACTION_CLICK);
            }
        }

    }

    @Override
    public void onServiceConnected() {
        Log.i(TAG, "ACC::onServiceConnected: ");
    }

    @Override
    public void onInterrupt() {
        // TODO Auto-generated method stub

    }
}

This is what I've used:

    override fun onAccessibilityEvent(event: AccessibilityEvent) {
        val eventPackageName = event.packageName
        val className = event.className
        val source: AccessibilityNodeInfo? = event.source
        val targetAppPackageName=...
        val targetViewId=...
        val viewsToCheck = rootInActiveWindow?.findAccessibilityNodeInfosByViewId("$targetAppPackageName:id/targetViewId")?.getOrNull(0)
        viewsToCheck?.performAction(AccessibilityNodeInfo.ACTION_CLICK)
        ...