如何使用AccessibilityService单击设置中的按钮?

时间:2014-10-29 10:42:12

标签: android accessibility

我想在使用AccessibilityService的android设置中单击按钮,就像greenify一样,但我找不到具体的按钮。请帮帮我。

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);
            }

编辑:

只有当type为TYPE_WINDOW_STATE_CHANGED时,我才能得到nodeInfo对象。

3 个答案:

答案 0 :(得分:13)

打开一个应用程序的Appinfo,启用强制关闭按钮进行测试:

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

    }
}

答案 1 :(得分:3)

所选答案适用于API 18及更高版本,因为它会在API 18中添加的findAccessibilityNodeInfosByViewId中继。 我最后编写了这个类来支持API 17及更低版本。

ResourcesCompat类找到用给定的activty标识的资源,在我们的例子中应该是Android的Setting活动。在处理辅助功能服务中的辅助功能事件时,可以通过调用此函数来获取设置活动的ComponentName。

    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;
    }

当您第一次将TYPE_WINDOW_STATE_CHANGED事件视为thecr0w所述时,init(...)就是onAccessibilityEvent的好地方。

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);
    }
}

有些制造商喜欢移动课程并重命名默认字符串(咳嗽三星咳嗽 Xiomi 咳嗽)所以请确保涵盖所有案例和处理错误和例外。

最后,按名称查找您的视图。在这里,id可以是'force_stop',例如

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

        return null;
}

答案 2 :(得分:0)

这是我用过的:

    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)
        ...