1# AccessibilityExtensionAbility 2 3AccessibilityExtensionAbility基于ExtensionAbility框架,提供无障碍扩展服务,开发者可以基于AccessibilityExtensionAbility模板开发自己的辅助应用,协助用户完成一些快捷的交互过程。 4 5## AccessibilityExtensionAbility概述 6 7“信息无障碍”译自“Accessibility”,是指任何人在任何情况下都能平等、方便地获取信息并利用信息。其目的是缩小全社会不同阶层、不同地区、不同年龄、不同健康状况的人群在信息理解、信息交互、信息利用方面的数字鸿沟,使其更加方便地参与社会生活,享受数字发展带来的便利。 8 9AccessibilityExtensionAbility为无障碍扩展服务框架,允许三方开发自己的扩展服务,提供在应用程序和扩展服务之间交换信息的标准机制,以便为各种障碍人群和障碍场景提供辅助能力,增强用户的无障碍使用体验。例如在使用微信时,利用辅助应用旁白,用户可以“听见”当前屏幕内容。 10 11 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