Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to click button in settings using AccessibilityService?

I want to click button in android settings using AccessibilityService like greenify did, but I cannot find the specific button. please help me.

MyAccessibilityService .java:

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.getText());

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

EDIT:

Only when type is TYPE_WINDOW_STATE_CHANGED , I could get the nodeInfo object.

like image 337
thecr0w Avatar asked Oct 29 '14 10:10

thecr0w


People also ask

How do you use accessibility services?

Go to Settings > Accessibility. The Global Action Bar Service is installed on your device. The accessibility service requests permission to observe user actions, retrieve window content, and perform gestures on behalf of the user! When using a third party accessibility service, make sure you really trust the source!

How do I give an app permanent accessibility?

Open your Settings app, search for "Accessibility", and tap on the first result. Next, tap one of these three buttons (the button that you see will depend on your device): Installed Services, Downloaded Services, or Downloaded apps. Tap A2U Android. Make sure the A2U Android shortcut is toggled OFF.

How do you take a screenshot on accessibility services?

If you want an easy-to-use button overlay, turn on the Accessibility Menu on your Android device. This will introduce a floating button that will give you the option to take a screenshot, navigate your screen, control the volume, power off the device, and more.


2 Answers

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

    }
}
like image 98
thecr0w Avatar answered Sep 28 '22 02:09

thecr0w


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;
}
like image 27
guy.gc Avatar answered Sep 28 '22 03:09

guy.gc