无障碍服务-注册无障碍事件

1,405 阅读5分钟

「这是我参与11月更文挑战的第10天,活动详情查看:2021最后一次更文挑战

注册无障碍事件

无障碍服务配置参数的最重要的一项功能就是允许您指定服务可处理哪些类型的无障碍事件。能够指定这些信息使无障碍服务能够互相协作,并使开发者能够灵活地仅处理来自特定应用的特定事件类型。事件过滤可包含以下条件:

软件包名称 - 指定您希望服务处理的无障碍事件所属的应用的软件包名称。如果省略此参数,则无障碍服务会被视为可用于处理任何应用的无障碍事件。您可以在无障碍服务配置文件中使用 android:packageNames 属性将此参数设为一个逗号分隔列表,或者使用AccessibilityServiceInfo.packageNames成员进行设置。

事件类型 - 指定您希望服务处理的无障碍事件的类型。您可以在无障碍服务配置文件中使用 android:accessibilityEventTypes 属性将此参数设为由 | 字符(例如,accessibilityEventTypes="typeViewClicked|typeViewFocused")分隔的列表,或者使用AccessibilityServiceInfo.eventTypes成员进行设置。

在设置无障碍服务时,请仔细考虑服务能够处理哪些事件,并且仅注册这些事件。由于用户一次可以激活多项无障碍服务,因此服务不得使用无法处理的事件。请注意,其他服务可能会处理这些事件,以改善用户体验。

无障碍功能音量

搭载 Android 8.0(API 级别 26)及更高版本的设备包含 STREAM_ACCESSIBILITY 音量类别,该类别可让您控制无障碍服务音频输出的音量,而不影响设备上的其他声音。

无障碍服务可以通过设置 FLAG_ENABLE_ACCESSIBILITY_VOLUME选项来使用此流类型。然后,您可以对设备的 AudioManager实例调用 adjustStreamVolume() 方法,以此来更改设备的无障碍功能音频音量。

以下代码段演示了无障碍服务如何使用 STREAM_ACCESSIBILITY 音量类别:

import static android.media.AudioManager.*;
public class MyAccessibilityService extends AccessibilityService {
    private AudioManager audioManager =
            (AudioManager) getSystemService(AUDIO_SERVICE);
    @Override
    public void onAccessibilityEvent(AccessibilityEvent accessibilityEvent) {
        AccessibilityNodeInfo interactedNodeInfo =
                accessibilityEvent.getSource();
        if (interactedNodeInfo.getText().equals("Increase volume")) {
            audioManager.adjustStreamVolume(AudioManager.STREAM_ACCESSIBILITY,ADJUST_RAISE, 0);
        }
    }
}

无障碍功能快捷方法

在搭载 Android 8.0(API 级别 26)及更高版本的设备上,用户可以通过同时长按两个音量键,从任意屏幕启用和停用他们的首选无障碍服务。虽然这种快捷方式在默认情况下会启用和停用 Talkback,但用户也可以配置该按钮来启用和停用他们设备上安装的任何服务,包括您自己的服务。 为了使用户可通过无障碍功能快捷方式使用特定的无障碍服务,当该服务启动时,它需要在运行时请求该功能。 无障碍按钮在使用软件渲染的导航区域且搭载 Android 8.0(API 级别 26)或更高版本的设备上,导航栏的右侧会显示一个“无障碍”按钮。当用户按此按钮时,他们可以调用已启用的多项无障碍功能和服务其中之一,具体取决于屏幕上当前显示的内容。

为了允许用户使用“无障碍”按钮调用指定的无障碍服务,该服务需要在AccessibilityServiceInfo对象的android:accessibilityFlags 属性中添加FLAG_REQUEST_ACCESSIBILITY_BUTTON标记。该服务随后可以使用registerAccessibilityButtonCallback()  注册回调。

注意:此功能仅在提供软件渲染的导航区域的设备上可用。服务必须始终使用 isAccessibilityButtonAvailable()并通过实现 onAvailabilityChanged() 来根据“无障碍”按钮的可用性响应更改。这样,即使在不支持“无障碍”按钮,或者“无障碍”按钮不可用的情况下,用户仍可以随时使用服务的功能。

以下代码段演示了如何配置无障碍服务,以响应用户按“无障碍”按钮操作:

private AccessibilityButtonController accessibilityButtonController;
private AccessibilityButtonController
        .AccessibilityButtonCallback accessibilityButtonCallback;
private boolean mIsAccessibilityButtonAvailable;

@Override
protected void onServiceConnected() 
    accessibilityButtonController = getAccessibilityButtonController();
    mIsAccessibilityButtonAvailable =
            accessibilityButtonController.isAccessibilityButtonAvailable()
    if (!mIsAccessibilityButtonAvailable) {
        return;
    }
    AccessibilityServiceInfo serviceInfo = getServiceInfo();
    serviceInfo.flags
            |= AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON;
    setServiceInfo(serviceInfo);

    accessibilityButtonCallback =
        new AccessibilityButtonController.AccessibilityButtonCallback() {
            @Override
            public void onClicked(AccessibilityButtonController controller) {
                Log.d("MY_APP_TAG", "Accessibility button pressed!");

                // Add custom logic for a service to react to the
                // accessibility button being pressed.
            }

            @Override
            public void onAvailabilityChanged(
              AccessibilityButtonController controller, boolean available) {
                if (controller.equals(accessibilityButtonController)) {
                    mIsAccessibilityButtonAvailable = available;
                }
            }
        };
    if (accessibilityButtonCallback != null) {
        accessibilityButtonController.registerAccessibilityButtonCallback(
                accessibilityButtonCallback, null);
    }
}

无障碍指纹手势

在使用软件渲染的导航区域且搭载 Android 8.0(API 级别 26)或更高版本的设备上,导航栏的右侧会显示一个“无障碍”按钮。当用户按此按钮时,他们可以调用已启用的多项无障碍功能和服务其中之一,具体取决于屏幕上当前显示的内容。

为了允许用户使用“无障碍”按钮调用指定的无障碍服务,该服务需要在AccessibilityServiceInfo对象的android:accessibilityFlags属性中添加FLAG_REQUEST_ACCESSIBILITY_BUTTON标记。该服务随后可以使用registerAccessibilityButtonCallback() 注册回调。

注意:您应该允许用户停用无障碍服务对指纹手势的支持。尽管多种无障碍服务可同时监听指纹手势,但这样做会导致服务互相冲突。

请注意,并非所有设备都包含指纹传感器。如需确定设备是否支持传感器,请使用isHardwareDetected() 方法。即使在具有指纹传感器的设备上,当该传感器用于进行身份验证时,您的服务也无法使用它。如需确定传感器何时可用,请调用 isGestureDetectionAvailable() 方法并实现 onGestureDetectionAvailabilityChanged() 回调。

以下代码段展示了使用指纹手势在虚拟游戏板上进行导航的示例: AndroidManifest.xml:

**<manifest ... >
    <uses-permission android:name="android.permission.USE_FINGERPRINT" />
    ...
    <application>
        <service android:name="com.example.MyFingerprintGestureService" ... >
            <meta-data
                android:name="android.accessibilityservice"
                android:resource="@xml/myfingerprintgestureservice" />
        </service>
    </application>
</manifest>**

myfingerprintgestureservice.xml:

<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
    ...
    android:accessibilityFlags=" ... |flagRequestFingerprintGestures"
    android:canRequestFingerprintGestures="true"
    ... />

MyFingerprintGestureService.java:

import static android.accessibilityservice.FingerprintGestureController.*;
public class MyFingerprintGestureService extends AccessibilityService {
    private FingerprintGestureController gestureController;
    private FingerprintGestureController
            .FingerprintGestureCallback fingerprintGestureCallback;
    private boolean mIsGestureDetectionAvailable;
    @Override
    public void onCreate() {
        gestureController = getFingerprintGestureController();
        mIsGestureDetectionAvailable =
                gestureController.isGestureDetectionAvailable();
    }

    @Override
    protected void onServiceConnected() {
        if (fingerprintGestureCallback != null
                || !mIsGestureDetectionAvailable) {
            return;
        }

        fingerprintGestureCallback =
               new FingerprintGestureController.FingerprintGestureCallback() {
            @Override
            public void onGestureDetected(int gesture) {
                switch (gesture) {
                    case FINGERPRINT_GESTURE_SWIPE_DOWN:
                        moveGameCursorDown();
                        break;
                    case FINGERPRINT_GESTURE_SWIPE_LEFT:
                        moveGameCursorLeft();
                        break;
                    case FINGERPRINT_GESTURE_SWIPE_RIGHT:
                        moveGameCursorRight();
                        break;
                    case FINGERPRINT_GESTURE_SWIPE_UP:
                        moveGameCursorUp();
                        break;
                    default:
                        Log.e(MY_APP_TAG,
                                  "Error: Unknown gesture type detected!");
                        break;
                }
            }

            @Override
            public void onGestureDetectionAvailabilityChanged(boolean available) {
                mIsGestureDetectionAvailable = available;
            }
        };
        if (fingerprintGestureCallback != null) {
            gestureController.registerFingerprintGestureCallback(
                    fingerprintGestureCallback, null);
        }
    }
}