亚行无障碍重点变化

ADB Accessibility Focus Change

我想知道如何让ADB在Talkback打开的时候调整accessibility focus。我试过:

adb shell input trackball roll 0 1
adb shell input [stylusdpad|keyboard|mouse|touchpad|gamepad|touchnavigation|joystick|touchscreen|stylus|trackball] swipe 180 780 540 780
adb shell input keyboard keyevent KEYCODE_TAB
adb shell input keyevent KEYCODE_NAVIGATE_NEXT
adb shell "input keyevent KEYCODE_ALT_LEFT & input keyevent KEYCODE_DPAD_LEFT"

我也尝试过使用 adb shell getevent 录制事件并回放它们但没有成功。

但我总是需要物理地滑动屏幕(即 ADB 滑动不起作用)来改变辅助功能焦点。有没有办法通过可访问性来做到这一点,只是下一个和上一个动作?

我找到了这个 article by Google:

Navigation

  • Move to next item: Alt + Right arrow Note: In continuous reading mode, this shortcut fast-forwards through the text.
  • Move to previous item: Alt + Left arrow Note: In continuous reading mode, this shortcut rewinds the text.

这意味着我只需要一次发送多个按键,对吧?我试过了,based on another SO answer:

device="/dev/input/event3"
ALT_KEY=57#18 #KEYCODE_ALT_LEFT
LEFT_KEY=21#37 #KEYCODE_DPAD_RIGHT
RIGHT_KEY=22#39 #KEYCODE_DPAD_RIGHT

device="/dev/input/event0"
adb shell "sendevent $device 1 $ALT_KEY 1 & sendevent $device 0 0 0 & sendevent $device 1 $RIGHT_KEY 1 & sendevent $device 0 0 0"
device="/dev/input/event1"
adb shell "sendevent $device 1 $ALT_KEY 1 & sendevent $device 0 0 0 & sendevent $device 1 $RIGHT_KEY 1 & sendevent $device 0 0 0"
device="/dev/input/event2"
adb shell "sendevent $device 1 $ALT_KEY 1 & sendevent $device 0 0 0 & sendevent $device 1 $RIGHT_KEY 1 & sendevent $device 0 0 0"
device="/dev/input/event3"
adb shell "sendevent $device 1 $ALT_KEY 1 & sendevent $device 0 0 0 & sendevent $device 1 $RIGHT_KEY 1 & sendevent $device 0 0 0"

device="/dev/input/event0"
adb shell "sendevent $device 1 $ALT_KEY 1 && sendevent $device 0 0 0 && sendevent $device 1 $RIGHT_KEY 1 && sendevent $device 0 0 0"
device="/dev/input/event1"
adb shell "sendevent $device 1 $ALT_KEY 1 && sendevent $device 0 0 0 && sendevent $device 1 $RIGHT_KEY 1 && sendevent $device 0 0 0"
device="/dev/input/event2"
adb shell "sendevent $device 1 $ALT_KEY 1 && sendevent $device 0 0 0 && sendevent $device 1 $RIGHT_KEY 1 && sendevent $device 0 0 0"
device="/dev/input/event3"
adb shell "sendevent $device 1 $ALT_KEY 1 && sendevent $device 0 0 0 && sendevent $device 1 $RIGHT_KEY 1 && sendevent $device 0 0 0"

device="/dev/input/event0"
adb shell "sendevent $device 1 $ALT_KEY 1 && sendevent $device 1 $RIGHT_KEY 1 && sendevent $device 0 0 0"
device="/dev/input/event1"
adb shell "sendevent $device 1 $ALT_KEY 1 && sendevent $device 1 $RIGHT_KEY 1 && sendevent $device 0 0 0"
device="/dev/input/event2"
adb shell "sendevent $device 1 $ALT_KEY 1 && sendevent $device 1 $RIGHT_KEY 1 && sendevent $device 0 0 0"
device="/dev/input/event3"
adb shell "sendevent $device 1 $ALT_KEY 1 && sendevent $device 1 $RIGHT_KEY 1 && sendevent $device 0 0 0"

(虽然我知道device0实际上是键盘设备,但我还是想尝试一下)

我在这里的回答将尽可能简洁。 GitHub.

上提供了我的完整代码

据我所知,开发人员无法通过 ADB 执行辅助功能操作,他们必须创建一个 Accessibility service in order to act on behalf of an Accessibility user and create a Broadcast Receiver so that it can take input via the ADB. This is two thirds of the answer, requiring one more component as developers cannot activate accessibility services via the ADB! This has to be done manually each time accessibility is toggled or through accessibility shortcuts - 其中只能有一个。 编辑 我也想通了这一点:)

无障碍服务

开发人员文档提供了一种机制 Accessibility Service:

An accessibility service is an application that provides user interface enhancements to assist users with disabilities, or who may temporarily be unable to fully interact with a device. For example, users who are driving, taking care of a young child or attending a very loud party might need additional or alternative interface feedback.

我按照 Google Codelab 构建了一个可以对用户执行操作的服务。这是该服务的一个片段,用于左右滑动(用户导航):

    fun swipeHorizontal(leftToRight: Boolean) {
        val swipePath = Path()
        if (leftToRight) {
            swipePath.moveTo(halfWidth - quarterWidth, halfHeight)
            swipePath.lineTo(halfWidth + quarterWidth, halfHeight)
        } else {
            swipePath.moveTo(halfWidth + quarterWidth, halfHeight)
            swipePath.lineTo(halfWidth - quarterWidth, halfHeight)
        }
        val gestureBuilder = GestureDescription.Builder()
        gestureBuilder.addStroke(StrokeDescription(swipePath, 0, 500))
        dispatchGesture(gestureBuilder.build(), GestureResultCallback(baseContext), null)
    }

广播接收器。

需要注意的是Receiver是在服务启用时注册的:

    override fun onServiceConnected() {
        IntentFilter().apply {
            addAction(ACCESSIBILITY_CONTROL_BROADCAST_ACTION)

            priority = 100

            registerReceiver(accessibilityActionReceiver, this)           
        }
   // REMEMBER TO DEREGISTER!

然后接收方可以响应意图并调用服务:

    override fun onReceive(context: Context?, intent: Intent?) {
        require(intent != null) { "Intent is required" }
        val serviceReference = //get a reference to the service somehow

        intent.getStringExtra(ACCESSIBILITY_ACTION)?.let {
            serviceReference.apply {
                when (it) {
                    ACTION_NEXT -> swipeHorizontal(true)
                    ACTION_PREV -> swipeHorizontal(false)
                    //...

adb 上的示例用法:

adb shell am broadcast -a com.balsdon.talkback.accessibility -e ACTION "ACTION_PREV"

编辑

正在通过 adb 启动服务:

感谢this comment

TALKBACK="com.google.android.marvin.talkback/com.google.android.marvin.talkback.TalkBackService"
ALLYDEV="com.balsdon.accessibilityBroadcastService/.AccessibilityBroadcastService"
adb shell settings put secure enabled_accessibility_services $TALKBACK:$ALLYDEV

辅助功能快捷方式

** 编辑:不再需要,但无论如何我都会把它留在这里 **

可能最烦人的事情是每次切换辅助功能时,服务都会关闭。所以我添加了 service as a shortcut with the VOLUME_UP and VOLUME_DOWN keys pressed. Thanks to this question.

  INPUT_DEVICE="/dev/input/event1" # VOLUME KEYS EVENT FILE
  VOLUME_DOWN=114 #0x0072
  VOLUME_UP=115   #0x0073
  BLANK_EVENT="sendevent $INPUT_DEVICE 0 0 0"

  INST_DN="sendevent $INPUT_DEVICE 1 $VOLUME_DOWN 1 && $BLANK_EVENT && sendevent $INPUT_DEVICE 1 $VOLUME_UP 1 && $BLANK_EVENT"
  INST_UP="sendevent $INPUT_DEVICE 1 $VOLUME_DOWN 0 && $BLANK_EVENT && sendevent $INPUT_DEVICE 1 $VOLUME_UP 0 && $BLANK_EVENT"
  adb shell "$INST_DN"
  sleep 3
  adb shell "$INST_UP"

我已经有了 toggling accessibility on/off 的脚本,所以我只是在上面添加了这个,每次都能得到我的服务 运行。

编辑 2

我在 AOSP 上提出了一个问题,即开发人员需要编写“滑动服务”而不是“导航服务”。这是有问题的,因为可以修改手势,然后我的系统将不会按预期运行。相反,我应该能够调用我想要执行的特定操作 NAVIGATE TO NEXT ELEMENTNAVIGATE TO PREVIOUS ELEMENT 而不是“向右滑动”和“向左滑动” - 阅读了 WCAG 指南后我不相信这是违反原则。