AccessibilityService - performGlobalAction 在自己的应用程序中不起作用 [英] AccessibilityService - performGlobalAction not working in own app
问题描述
我正在尝试通过 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 anaccessibilityservice.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屋!