无障碍服务 - performGlobalAction 在自己的应用中无法工作

4

我正在尝试通过AccessibilityService发送系统返回按键事件,这个方法可以正常工作,但只有在我不在自己的应用程序中才有效。

无论我是否在自己的应用程序中,我总是从performGlobalAction得到true,但只有当我不在自己的应用程序中而在其他任何一个应用程序中时(例如显示上一个活动或类似情况),我才能看到事件真正被执行。

有什么想法是为什么会发生这种情况吗?我的应用程序是一个侧边栏应用程序,在WindowManager上绘制了一个覆盖层,一切都正常运行(AccessibilityService正在运行并处理我的自定义事件,并且服务始终返回成功消息以及我的事件,但我的应用程序不会对返回按钮事件做出反应)。

我的服务代码如下:

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() {
    }
}

编辑

为了让这个更清晰:

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

编辑2

看起来在我的情况下,我的覆盖层仍然会窃取焦点,使其可以聚焦,而且有时会出现时间问题。不过,我的解决方案可以通过使用BroadcastReceiver来改进,因为如已接受的答案所讨论的那样,startService调用是不安全的。

1个回答

3

我注意到你正在做一些非常奇怪的事情。看起来像是你将你的AccessibilityService作为普通的Service来对待。表明这一点的部分是你实现以下两个方法:

public static void sendBackIntent(Context context);

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

只要这两种方法的签名和你的调用

context.startService(intent);    

在你的静态方法中,我可以看出你并不理解AccessibilityServices以及它们应该如何执行任务。你不能以你试图的方式启动和交互你的无障碍服务。当然,你可以使用无障碍服务来执行全局操作,但是除非你从无障碍服务菜单(你知道那个TalkBack出现的地方)正确启动它们,否则它们不会准确且全局地执行。
实际上,你的代码并没有在你认为的上下文中运行。因此,它会运行并执行某些操作。但是,AccessibilityServices及其相应的功能在于它们能够全局地连接到操作系统。当你尝试使用以下代码启动服务时,android API无法正确绑定AccessibilityService:
context.startService(intent);

您需要从“辅助功能服务设置”菜单中启动您的辅助功能服务。即使您的服务已经启动,这样的调用也是不安全的!无法保证用户在打开您的活动之前会启动服务。一旦您调用了context.startService并尝试以这种方式启动您的AccessibilityService,它将阻止辅助功能设置菜单启动您的服务并正确地绑定到操作系统。事实上,一旦处于这种情况,用户必须:关闭辅助功能设置菜单中您服务的开关,强制停止(甚至卸载)您的应用程序,重新启动设备,启动您的服务,然后再启动您的活动,才能实现正确的行为。如果您不这样做,它将无法正确地绑定到操作系统,并且其行为是未定义的。现在,您实际上已经在操作系统中创建了一个黑客,并且正在遇到未定义的行为,这可能因版本、制造商等而大相径庭,因为其行为没有在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 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;
        }
    }
}

performGlobalAction 调用在任何 Context 中都可以正常工作。现在,不是在 SWIPE_UP_DOWN 手势上执行此操作,而是需要设置一种与您要能够触发“全局返回按钮”操作的部分进行进程间通信的方法。但是,那些信息属于另一个问题,如果您理解了本帖子中的信息,我相信您需要如何继续将变得清晰明了。


1
评论不适合进行长时间的讨论或调试会话;此对话已被移至聊天室。如果这里有重要信息,应将其编辑到问题或答案中。 - Cody Gray
所以,在那个帖子中,我发表了一个关于在移动一些内容后删除评论的评论。然而,有人把它删掉了,现在已经消失了。在删除这么多内容之前提前警告会很受欢迎。@prom85:如果你有其他问题,请在另一个帖子上提问,我认为这是正确的答案(或者至少是需要被其他人看到的信息),适用于这个特定的问题。 - MobA11y
我在删除的评论中没有看到你发布关于那个的任何内容。无论如何,信息并没有被删除;它被存档在聊天记录中。除了留下另一个嘈杂的评论之外,我们没有真正的方法来警告你,而这是我们强烈反对的。当评论线程变得太长时,我们会收到自动标记,建议采取的行动是将其移至聊天室。如果您对工作流程有建议或政策存在问题,可以在[Meta]上发布问题。您将得到的回答基本上是评论是暂时的,不适合讨论。 - Cody Gray

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接