• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# AccessibilityExtensionAbility
2
3AccessibilityExtensionAbility基于ExtensionAbility框架,提供无障碍扩展服务,开发者可以基于AccessibilityExtensionAbility模板开发自己的辅助应用,协助用户完成一些快捷的交互过程。
4
5## AccessibilityExtensionAbility概述
6
7“信息无障碍”译自“Accessibility”,是指任何人在任何情况下都能平等、方便地获取信息并利用信息。其目的是缩小全社会不同阶层、不同地区、不同年龄、不同健康状况的人群在信息理解、信息交互、信息利用方面的数字鸿沟,使其更加方便地参与社会生活,享受数字发展带来的便利。
8
9AccessibilityExtensionAbility为无障碍扩展服务框架,允许三方开发自己的扩展服务,提供在应用程序和扩展服务之间交换信息的标准机制,以便为各种障碍人群和障碍场景提供辅助能力,增强用户的无障碍使用体验。例如在使用微信时,利用辅助应用旁白,用户可以“听见”当前屏幕内容。
10
11![AccessibilityFramework](figures/AccessibilityFramework.png)
12
131. Accessibility App:开发者基于无障碍扩展服务框架扩展出来的辅助应用,如视障用户使用的读屏App。
142. Tartget App:被Accessibility App辅助的目标应用。
153. AccessibilityAbilityManagerService(AAMS):无障碍扩展服务框架主服务,用于对Accessibility App生命周期进行管理,同时为Accessibility App和Target App提供信息交互的桥梁。
164. AccessibilityAbility(AAkit):Accessibility App利用AAkit构建扩展服务Ability运行环境,并为Accessibility App提供可查询和操作Target App的接口,如查询节点信息、对节点执行点击/长按操作等。
175. AccessibilitySystemAbilityClient(ASACkit):Target App通过ASACkit向AAMS发送无障碍事件,如内容变化事件等,同时响应Accessibility App通过AAMS请求的指令,如查询节点信息、对节点执行点击/长按操作等。
18
19## Accessibility目前提供的能力介绍
20
211. 提供辅助功能查询能力,包括获取辅助应用列表、辅助应用启用状态、辅助应用类型、发送无障碍事件等,具体API接口可参考[@ohos.accessibility](../reference/apis/js-apis-accessibility.md);
222. 提供无障碍扩展服务连接、断开连接、指定辅助事件及物理按键等事件的回调,具体API接口可参考[@ohos.application.AccessibilityExtensionAbility](../reference/apis/js-apis-application-accessibilityExtensionAbility.md);
233. 提供辅助功能扩展的上下文环境的能力,包括允许配置辅助应用关注信息类型、查询节点信息、手势注入等,具体API接口可参考[AccessibilityExtensionContext ](../reference/apis/js-apis-inner-application-accessibilityExtensionContext.md);其中,创建注入手势所需的路径信息可参考[@ohos.accessibility.GesturePath](../reference/apis/js-apis-accessibility-GesturePath.md);创建注入手势所需的手势路径的触摸点信息可参考[ohos.accessibility.GesturePoint](../reference/apis/js-apis-accessibility-GesturePoint.md)。
24
25## 如何创建一个无障碍扩展服务
26
27开发者在创建一个无障碍扩展服务时,如工程满足环境要求,开发者可自主选择是否跳过创建工程步骤,在已有工程中新增无障碍扩展服务。一个工程仅支持创建一个无障碍扩展服务。
28本指南以实现以下功能为案例,讲述如何创建无障碍扩展服务,如何调用API接口实现该功能:
29启动辅助功能后,在设备屏幕上绘画“右划后再下划”(rightThenDown)的手势,获取当前界面的全部节点;之后再绘画“左划后再下划”(leftThenDown)的手势,打印所有节点。
30
31### 创建工程
32
33如需新增独立的无障碍扩展服务应用,在DevEco Studio中新建一个API 9以上的Stage工程。
34
35### 新建无障碍扩展服务ets文件
36
37在已创建工程的ets文件夹下创建AccessibilityExtAbility文件夹,在该文件夹下创建AccessibilityExtAbility.ets文件,可在该文件中实现一些回调函数,并加入业务处理逻辑的调用:
38
39```ts
40import AccessibilityExtensionAbility, { AccessibilityEvent } from '@ohos.application.AccessibilityExtensionAbility';
41import AccessibilityManager from './AccessibilityManager';
42
43class AccessibilityExtAbility extends AccessibilityExtensionAbility {
44    onConnect() {
45        console.info(`AccessibilityExtAbility onConnect`);
46        // 执行初始化业务逻辑的操作
47        AccessibilityManager.getInstance().onStart(this.context);
48    }
49
50    onDisconnect() {
51        console.info(`AccessibilityExtAbility onDisconnect`);
52        // 执行资源回收退出业务逻辑的操作
53        AccessibilityManager.getInstance().onStop();
54    }
55
56    onAccessibilityEvent(accessibilityEvent: AccessibilityEvent) {
57        console.info(`AccessibilityExtAbility onAccessibilityEvent: ${JSON.stringify(accessibilityEvent)}`);
58        // 根据事件信息进行业务逻辑处理
59        AccessibilityManager.getInstance().onEvent(accessibilityEvent);
60    }
61}
62
63export default AccessibilityExtAbility;
64```
65
66其中,主要定义了以下接口:
67
68| 接口 | 描述 |
69| ---- | ---- |
70| onConnect(): void | 当扩展服务连接时回调。 |
71| onDisconnect(): void | 当扩展服务断开时回调。 |
72| onAccessibilityEvent(event: AccessibilityEvent): void | 当无障碍事件发生时回调。 |
73
74创建AccessibilityManager.ets文件,用于存放业务逻辑代码:
75```ts
76import {
77  AccessibilityElement,
78  AccessibilityEvent,
79  AccessibilityExtensionContext,
80  ElementAttributeKeys
81} from '@ohos.application.AccessibilityExtensionAbility';
82
83interface Rect {
84  left: number,
85  top: number,
86  width: number,
87  height: number,
88}
89
90// 想要查询的属性信息
91let wantedAttribute: ElementAttributeKeys[] = ['bundleName', 'text', 'description', 'windowId'];
92type attributeValues = string | number | boolean | AccessibilityElement | AccessibilityElement[] | string[] | Rect;
93
94export default class AccessibilityManager {
95  private static instance: AccessibilityManager;
96  accessibleContext?: AccessibilityExtensionContext;
97  currentPageElementArray: Array<AccessibilityElement> | null = null;
98
99  static getInstance(): AccessibilityManager {
100    if (!AccessibilityManager.instance) {
101      AccessibilityManager.instance = new AccessibilityManager();
102    }
103    return AccessibilityManager.instance;
104  }
105
106  onStart(context: AccessibilityExtensionContext) {
107    console.info(`AccessibilityManager onStart`);
108    this.accessibleContext = context;
109  }
110
111  onStop() {
112    console.info(`AccessibilityManager onStop`);
113    this.accessibleContext = undefined;
114  }
115
116  onEvent(accessibilityEvent: AccessibilityEvent): void {
117    console.info(`AccessibilityManager onEvent`);
118    switch (accessibilityEvent.eventType) {
119      case 'rightThenDown':
120      // 获取当前页面的所有节点
121        this.getCurrentPageAllElement();
122        break;
123      case 'leftThenDown':
124      // 打印所有节点
125        this.printAllElementInfo();
126        break;
127      default:
128        break;
129    }
130  }
131
132  async getCurrentPageAllElement(): Promise<void> {
133    console.info(`AccessibilityManager getCurrentPageAllElement`);
134    let rootElement: AccessibilityElement;
135    if(!this.accessibleContext){
136      console.error(`AccessibilityManager accessibleContext undefined`);
137      return;
138    }
139    try {
140      rootElement = await this.accessibleContext?.getWindowRootElement();
141      this.currentPageElementArray = await this.getAttributeValue(rootElement, 'children') as AccessibilityElement[];
142    } catch (error) {
143      console.error(`AccessibilityExtAbility Failed to getWindowRootElement. Cause:${JSON.stringify(error)}`);
144    }
145  }
146
147  async getElementWantedInfo(accessibilityElement: AccessibilityElement, wantedAttribute: ElementAttributeKeys[]):
148    Promise<string | null> {
149    console.info(`AccessibilityUtils getElementAllInfo`);
150    if (accessibilityElement === null) {
151      console.error(`AccessibilityUtils accessibilityElement is null`);
152      return null;
153    }
154
155    let info = '';
156    let value: attributeValues | null;
157    for (let name of wantedAttribute) {
158      value = await this.getAttributeValue(accessibilityElement, name);
159      info = info.concat(name + ': ' + value + ' ');
160    }
161    return info;
162  }
163
164  async getAttributeValue(accessibilityElement: AccessibilityElement, key: ElementAttributeKeys):
165    Promise<attributeValues | null> {
166    console.info(`AccessibilityUtils getAttributeValue`);
167    let value: attributeValues;
168    let keys = await accessibilityElement.attributeNames();
169    let isExit = false;
170    for (let keyString of keys) {
171      if (key == keyString) {
172        isExit = true;
173      }
174    }
175    if (isExit) {
176      try {
177        value = await accessibilityElement.attributeValue(key);
178        return value;
179      } catch (error) {
180        console.error(`AccessibilityUtils Failed to get attributeValue of ${key} . Cause:  ${JSON.stringify(error)}`);
181      }
182    }
183    return null;
184  }
185
186  async printAllElementInfo(): Promise<void> {
187    console.info(`AccessibilityManager printAllElementInfo`);
188    if (this.currentPageElementArray === null || this.currentPageElementArray.length === 0) {
189      console.error(`AccessibilityManager currentPageElementArray is null`);
190      return;
191    }
192    let info: string | null = null;
193    for (let index = 0; index < this.currentPageElementArray.length; index++) {
194      info = await this.getElementWantedInfo(this.currentPageElementArray[index], wantedAttribute);
195      console.info(`AccessibilityManager element information: ${info}`);
196    }
197  }
198}
199```
200
201## 如何处理一个无障碍事件
202
203相关无障碍事件可以在`onAccessibilityEvent()`方法中进行业务逻辑处理,具体事件可参考[AccessibilityEvent](../reference/apis/js-apis-application-accessibilityExtensionAbility.md#accessibilityevent)。此处以手势事件`rightThenDown`为例:
204
205```ts
206onAccessibilityEvent(accessibilityEvent: AccessibilityEvent) {
207    console.info('AccessibilityExtAbility onAccessibilityEvent: ' + JSON.stringify(accessibilityEvent));
208    if (accessibilityEvent.eventType === 'rightThenDown') {
209        console.info('AccessibilityExtAbility onAccessibilityEvent: rightThenDown');
210        // TODO: 自定义相关逻辑开发
211    }
212}
213```
214在相应的无障碍事件中,可以使用[辅助功能扩展上下文(AccessibilityExtensionContext)](../reference/apis/js-apis-inner-application-accessibilityExtensionContext.md)提供的接口进行扩展开发,包括允许配置辅助应用关注信息类型、查询节点信息、手势注入等。
215
216此外,还可在无障碍扩展服务中对物理按键事件进行处理,具体可参考[onKeyEvent](../reference/apis/js-apis-application-accessibilityExtensionAbility.md#accessibilityextensionabilityonkeyevent)。
217
218## 如何声明无障碍扩展服务具备的能力
219
220在完成自定义无障碍扩展服务的逻辑开发后,还需要在工程中Module对应的module.json5文件中加入新增扩展服务的配置信息,其中`srcEntry`标签为`extensionAbility`对应的路径。需要注意的一点是配置信息中的type标签要按照与无障碍子系统的约定进行配置,固定为`accessibility`,否则将无法正常连接。
221
222```json
223"extensionAbilities": [
224  {
225    "name": "AccessibilityExtAbility",
226    "srcEntry": "./ets/AccessibilityExtAbility/AccessibilityExtAbility.ets",
227    "label": "$string:MainAbility_label",
228    "description": "$string:MainAbility_desc",
229    "type": "accessibility",
230    "metadata": [
231      {
232        "name": "ohos.accessibleability",
233        "resource": "$profile:accessibility_config"
234      }
235    ]
236  }
237]
238```
239另外,配置信息中的`accessibility_config`为无障碍扩展服务的具体配置,需要在`resources/base/profile/`下新建`accessibility_config.json`文件,在该文件中声明此无障碍扩展服务具备的[能力类型](../reference/apis/js-apis-accessibility.md#capability),根据业务功能合理声明能力类型,本案例中,需要如下声明:
240```json
241{
242  "accessibilityCapabilities": [
243    "retrieve",
244    "gesture",
245    "touchGuide"
246  ]
247}
248```
249## 如何开启自定义的无障碍扩展服务
250
251当前提供设备-设置中的扩展服务管理页的开关按钮来开启/关闭选择的无障碍扩展服务:
252
253- 打开设备设置页面,进入“辅助功能”,“扩展服务”小标题下的“已安装的服务”显示当前安装的扩展服务个数,点击进入,展示安装的扩展服务列表;
254未安装扩展服务时,“已安装的扩展服务”不可点击,并显示“无服务”;<br>
255- 选择需要开启/关闭的扩展服务,通过开关按钮进行扩展服务的开启/关闭;
256- 开启时,弹出安全提醒,在倒计时结束后,勾选“我已知晓如上风险,并自愿承担可能导致的后果。”后,可选择“开启”/“不开启”按钮;  关闭时,将开启的开关关闭,即可关闭已开启的扩展服务。
257
258## 相关实例
259
260针对AccessibilityExtensionAbility开发,有以下相关实例可供参考:
261
262- [无障碍扩展(ArkTS)(API9及以上版本)](https://gitee.com/openharmony/applications_app_samples/tree/master/code/SystemFeature/ApplicationModels/AccessibilityExtAbility)
263