AccessibilityService - performGlobalAction 在自己的应用程序中不起作用 [英] AccessibilityService - performGlobalAction not working in own app

查看:103
本文介绍了AccessibilityService - performGlobalAction 在自己的应用程序中不起作用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试通过 AccessibilityService 发送系统回按事件,这可以正常工作,但前提是我不在自己的应用中.

我总是从 performGlobalAction 得到 true,无论我是否在我自己的应用程序中,但我只看到事件真的被执行,如果我'不是在我自己的应用程序中,而是在任何其他应用程序中(从某种意义上说,前一个活动显示或类似)

任何想法为什么会发生这种情况?我的应用程序是一个侧边栏应用程序,在 WindowManager 顶部绘制了一个叠加层,一切正常(AccessibilityService 正在运行并处理我的自定义事件,该服务始终返回成功消息对于我的事件,但我自己的应用程序对后退按钮事件没有反应).

我的服务如下所示:

public class MyAccessibilityService extends AccessibilityService {公共静态无效sendBackIntent(上下文上下文){意图意图 = 新意图(上下文,MyAccessibilityService.class);intent.putExtra(行动",GLOBAL_ACTION_BACK);context.startService(意图);}@覆盖public int onStartCommand(意图意图,int标志,int startId){Bundle extras = intent.getExtras();整数动作 = null;如果(额外的!= null){action = extras.getInt("action");}如果(动作!= null){开关(动作){案例 GLOBAL_ACTION_BACK:布尔结果 = performGlobalAction(action);L.d("动作 %d 已执行:%b", action, result);休息;默认:L.e("未处理的动作 %d", action);休息;}}return super.onStartCommand(intent, flags, startId);}@覆盖公共无效 onAccessibilityEvent(AccessibilityEvent 事件){}@覆盖公共无效 onInterrupt() {}}

编辑

为了说明这一点:

  • 我不通过 MyAccessibilityService.sendBackIntent(context) 启动此服务,我发送的意图如下:if (isAccessibilityserviceRunning) MyAccessibilityService.sendBackIntent(context)
  • 我通过系统服务菜单启动我的服务,只需在那里启用它,然后让系统自动启动它
  • 我已经在 accessibilityservice.xml 中为 AccessibilityService 设置了所有内容,并使用它来定义我的服务设置,这也工作得很好,所有事件我想要接收的都可靠且正确地接收

编辑 2

似乎在我的情况下,我的叠加层仍在窃取焦点,使其可聚焦,并且没有有时会造成问题的计时问题.尽管如此,我的解决方案可以通过使用 BroadcastReceiver 与服务通信来改进,因为 startService 调用并不安全,如已接受的答案

解决方案

我觉得你在做一些非常奇怪的事情.您似乎将 AccessibilityService 视为普通的 Service.这部分表明这是您对方法的以下实现:

public static void sendBackIntent(Context context);@覆盖public int onStartCommand(Intent intent, int flags, int startId);

仅仅通过这两个方法的签名和你的调用

context.startService(intent);

在您的静态方法中,我可以看出您不了解 AccessibilityServices 以及它们应该如何执行其工作.您无法以您尝试的方式启动无障碍服务,也无法与之交互.当然,您可以使用辅助功能服务来执行全局操作,但除非您从辅助功能服务"菜单(您知道 TalkBack 出现的位置)正确启动它们,否则它们不会准确且全局地执行此操作.

您的代码本质上并没有在您认为它运行的上下文中运行.因此,它运行并执行某些操作.但是,AccessibilityServices 和它们各自的力量在于它们能够全局附加到操作系统.当您尝试通过以下方式启动服务时,android API 将无法正确绑定 AccessibilityService:

context.startService(intent);

<块引用>

您必须从无障碍服务设置菜单启动您的无障碍服务.

即使您的服务已经启动,这样的调用也是不安全的!无法保证您的用户会在打开您的 Activity 之前启动该服务.一旦您调用了 context.startService 并尝试以这种方式启动您的 AccessibilityService,它会阻止 Accessibility Settings Menu 启动您的服务并正确绑定到操作系统.事实上,一旦在这种情况下,用户必须:在辅助功能设置菜单中关闭您的服务的开关,强制停止(甚至卸载)您的应用程序,重新启动他们的设备,启动您的服务,然后开始您的活动,在以实现正确的行为.

如果您不这样做,它将无法正确绑定到操作系统并且其行为未定义.现在,您实际上已经在操作系统中创建了一个 hack,并且遇到了上述未定义行为,该行为可能因版本、制造商等而异,因为 AOSP 集成测试未涵盖该行为.

实际上,您无法使用 context.startService() 调用明确启动辅助功能服务.这是 Android 的一项非常重要的安全功能,因为辅助功能服务可以访问屏幕内容,而用户需要对允许此访问的提供程序和应用程序进行精细控制.因此,虽然您可能会遇到某些行为,但这是未定义且危险的行为.您想要的是以下内容:

使用以下服务配置 XML:

<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"android:description="@string/accessibility_service_description"android:accessibilityEventTypes="typeWindowContentChanged"android:accessibilityFlags="flagRequestTouchExplorationMode"android:canRetrieveWindowContent="true"android:canRequestTouchExplorationMode="true"android:accessibilityFeedbackType="feedbackGeneric"android:notificationTimeout="100"android:settingsActivity="com.service.SettingsActivity"/>

以及以下无障碍服务.

class MyA11yService 扩展 AccessibilityService {@Override public boolean onGesture(intgestureId) {开关(手势ID){案例 GESTURE_SWIPE_UP_AND_DOWN:CLog.d("执行手势.");执行全局操作(GLOBAL_ACTION_BACK);返回真;默认:返回假;}}}

performGlobalAction 调用在任何 Context 中都可以正常工作.现在,不是在 SWIPE_UP_DOWN 手势上执行此操作,您要做的是与您希望能够触发全局后退按钮"操作的部分设置某种进程间通信.但是,该信息是针对另一个问题的,不过如果您了解本文中的信息,我相信您需要如何继续操作就会很清楚.

I'm trying to send a system back press event via the AccessibilityService and this works fine, but only if I'm not in my own app.

I'm always getting true from performGlobalAction no matter if I'm in my own app or not, but I only see that the event really is executed if I'm not in my own app but in any other one (in the sense of that the previous activity is shown or similar)

Any ideas why this happens? My app is a sidebar app with an overlay drawn on top in the WindowManager and everything is working (AccessibilityService is running and is handling my custom events and the service always returns success messages for my events, but my own app does not react to the back button event).

My service looks like following:

public class MyAccessibilityService extends AccessibilityService {

    public static void sendBackIntent(Context context) {
        Intent intent = new Intent(context, MyAccessibilityService.class);
        intent.putExtra("action", GLOBAL_ACTION_BACK);
        context.startService(intent);
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Bundle extras = intent.getExtras();
        Integer action = null;
        if (extras != null) {
            action = extras.getInt("action");
        }

        if (action != null) {
            switch (action) {
                case GLOBAL_ACTION_BACK:
                    boolean result = performGlobalAction(action);
                    L.d("Action %d executed: %b", action, result);
                    break;
                default:
                    L.e("Unhandled action %d", action);
                    break;
            }
        }
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public void onAccessibilityEvent(AccessibilityEvent event) {

    }

    @Override
    public void onInterrupt() {
    }
}

Edit

To make this clear:

  • I do NOT start this service via MyAccessibilityService.sendBackIntent(context), I send the intent like following: if (isAccessibilityserviceRunning) MyAccessibilityService.sendBackIntent(context)
  • I start my service via the system service menu by simply enabling it there and let the system start it automatically afterwards
  • I've setup everything for the AccessibilityService in an accessibilityservice.xml and use this to define my services settings and this is working perfectly fine as well, all events I want to receive are received reliably and correct

EDIT 2

Seems like in my case my overlay is still stealing the focus making it focusable and not has timing problems that sometimes make problems. Still, my solution can be improved by using BroadcastReceiver to communicate with the service, as the startService call is not safe as discussed in the accepted answer

解决方案

It strikes me that you're doing some very strange things. It appears that you're treating your AccessibilityService as a normal Service. The part of this that suggests this is your implementation of the following to methods:

public static void sendBackIntent(Context context);

@Override
public int onStartCommand(Intent intent, int flags, int startId);

Just by the signatures of these two methods and your calling of

context.startService(intent);    

Within your static method, I can tell that you don't understand AccessibilityServices and how they are supposed to perform their jobs. You cannot start your accessibility service, nor interact with it, in the way that you are attempting. Certainly you can use Accessibility Services to perform global actions, but they won't do so accurately and globally, unless you launch them correctly, from the Accessibility Services menu (you know the one where TalkBack shows up).

Your code essentially, isn't running within the Context you think it's running in. So, it runs, and does things. But, AccessibilityServices and their respective power, is in their ability to attach globally to the Operating System. The android API's won't bind an AccessibilityService properly, when you attempt to launch your service with:

context.startService(intent);

You have to launch your Accessibility Service from the Accessibility Services Settings menu.

Even if your service is already launched such a call is unsafe! There's no guarantee your users are going to start the service prior to opening your Activity. Once you have called context.startService and attempted to start your AccessibilityService in this way, it will prevent the Accessibility Settings Menu from starting your service and binding to the OS properly. In fact, once in this situation a user would have to: Turn off the Switch for your service in the Accessibility Settings Menu, force stop (perhaps even uninstall) your application, restart their device, start your service and THEN start your activity, in order for the proper behavior to be achieved.

If you don't do so, it will not bind to the OS properly and its behavior is undefined. Right now, you've essentially created a hack in the OS and are running up against said undefined behavior, that could vary WIDELY across version, manufacturer, etc, because it's behavior isn't covered in the AOSP integration tests.

In fact, you explicitly CANNOT launch Accessibility Services using the context.startService() call. This is a very important security feature of Android, as Accessibility Services can gain access to screen content, and users need fine grain control over the providers and applications they allow this access. So, while you may be getting SOME behavior, it is undefined and dangerous behavior. What you want is something like the following:

With the following service config XML:

<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
    android:description="@string/accessibility_service_description"
    android:accessibilityEventTypes="typeWindowContentChanged"
    android:accessibilityFlags="flagRequestTouchExplorationMode"
    android:canRetrieveWindowContent="true"
    android:canRequestTouchExplorationMode="true"
    android:accessibilityFeedbackType="feedbackGeneric"
    android:notificationTimeout="100"
    android:settingsActivity="com.service.SettingsActivity"
    />

And the following accessibility service.

class MyA11yService extends AccessibilityService {
    @Override public boolean onGesture(int gestureId) {
        switch (gestureId) {
            case GESTURE_SWIPE_UP_AND_DOWN:
                CLog.d("Performing gesture.");
                performGlobalAction(GLOBAL_ACTION_BACK);
                return true;

            default:
                return false;
        }
    }
}

The performGlobalAction call works just fine in any Context. Now, instead of performing this action on the SWIPE_UP_DOWN gesture, what you want to do is set up some sort of inter-process communication with the part of this that you want to be able to trigger the "global back button" action. But, that information is for another question, though if you understand the information in this post, I'm sure how you need to proceed will be clear.

这篇关于AccessibilityService - performGlobalAction 在自己的应用程序中不起作用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆