1# 使用AutoFillExtensionAbility实现自动填充功能(仅对系统应用开放) 2 3<!--Kit: Ability Kit--> 4<!--Subsystem: Ability--> 5<!--Owner: @hanchen45; @Luobniz21--> 6<!--Designer: @ccllee1--> 7<!--Tester: @lixueqing513--> 8<!--Adviser: @huipeizi--> 9 10## 概述 11 12[AutoFillExtensionAbility](../reference/apis-ability-kit/js-apis-app-ability-autoFillExtensionAbility-sys.md)是AUTO_FILL_PASSWORD/AUTO_FILL_SMART类型的[ExtensionAbility](../reference/apis-ability-kit/js-apis-app-ability-extensionAbility.md)组件,提供自动填充能力。 13 14自动填充能力根据自动填充控件内容的不同,分为账号密码自动填充和情景化自动填充。 15 16- 账号密码自动填充:帮助用户自动填充已保存的账号密码,提高用户输入信息的效率。 17- 情景化自动填充:根据组件的使用场景实现手机号、地址等信息的自动填充。 18 19为了便于表述,本例中将提供[AutoFillExtensionAbility](../reference/apis-ability-kit/js-apis-app-ability-autoFillExtensionAbility-sys.md)能力的一方称为提供方,将启动AutoFillExtensionAbility的一方称为使用方。 20 21## 接口说明 22 23自动填充功能主要接口如下。其他接口介绍详情参见[AutoFillRequest](../reference/apis-ability-kit/js-apis-inner-application-autoFillRequest-sys.md)、[AutoFillExtensionAbility](../reference/apis-ability-kit/js-apis-app-ability-autoFillExtensionAbility-sys.md)。 24 25| 接口名称 | 说明 | 26| ------------------------------------------------------------ | ------------------------------------------------------------ | 27| onFillRequest(session: UIExtensionContentSession, request: FillRequest, callback: FillRequestCallback): void | 当发起自动填充请求或者生成密码时触发此回调函数。 | 28| onSaveRequest(session: UIExtensionContentSession, request: SaveRequest, callback: SaveRequestCallback): void | 当发起自动保存或者手动保存时触发此回调函数。 | 29| FillRequestCallback.onSuccess(response: FillResponse): void | 自动填充或者生成密码时的回调对象,可以通过此回调通知客户端成功。 | 30 31## 开发AutoFillExtensionAbility提供方 32 33### 生命周期 34 35[AutoFillExtensionAbility](../reference/apis-ability-kit/js-apis-app-ability-autoFillExtensionAbility-sys.md)提供了[onCreate](../reference/apis-ability-kit/js-apis-app-ability-autoFillExtensionAbility-sys.md#oncreate)、[onSessionDestroy](../reference/apis-ability-kit/js-apis-app-ability-autoFillExtensionAbility-sys.md#onsessiondestroy)、[onForeground](../reference/apis-ability-kit/js-apis-app-ability-autoFillExtensionAbility-sys.md#onforeground)、[onBackground](../reference/apis-ability-kit/js-apis-app-ability-autoFillExtensionAbility-sys.md#onbackground)、[onDestroy](../reference/apis-ability-kit/js-apis-app-ability-autoFillExtensionAbility-sys.md#ondestroy)、[onSaveRequest](../reference/apis-ability-kit/js-apis-app-ability-autoFillExtensionAbility-sys.md#onsaverequest)和[onFillRequest](../reference/apis-ability-kit/js-apis-app-ability-autoFillExtensionAbility-sys.md#onfillrequest)生命周期回调,根据需要重写对应的回调方法。 36 37- **onCreate**:当AutoFillExtensionAbility创建时回调,执行初始化业务逻辑操作。 38- **onSessionDestroy**:当AutoFillExtensionAbility界面内容对象销毁后调用。 39- **onForeground**:当AutoFillExtensionAbility从后台转到前台时触发。 40- **onBackground**:当AutoFillExtensionAbility从前台转到后台时触发。 41- **onDestroy**:当AutoFillExtensionAbility销毁时回调,可以执行资源清理等操作。 42- **onSaveRequest**:表单中有数据存在并且切换页面时,会触发自动保存的生命周期回调。 43- **onFillRequest**:当发起自动填充请求或者生成密码时,会触发该生命周期回调。 44 45### 实现账号密码自动填充功能 46 47开发者在实现自动填充服务时,需要在DevEco Studio工程中手动新建一个AutoFillExtensionAbility。 48 491. 设定AutoFillExtensionAbility应用的包名。 50 51 在AppScope/[app.json5配置文件](../quick-start/app-configuration-file.md)中把bundleName设定为:"com.ohos.passwordbox",例如: 52 53 ```json 54 "app": { 55 "bundleName": "com.ohos.passwordbox", 56 // ... 57 } 58 ``` 59 602. 配置extensionAbilities信息。 61 62 在entry/src/main/[module.json5配置文件](../quick-start/module-configuration-file.md)中配置AutoFillAbility,例如: 63 64 ```json 65 "extensionAbilities": [ 66 { 67 "name": "AutoFillAbility", 68 "srcEntry": "./ets/autofillability/AutoFillAbility.ets", 69 // ... 70 "type": "autoFill/password" 71 } 72 ] 73 ``` 74 753. 实现自动填充与自动保存。 76 77 1. 在ets目录右键选择“New > Directory”,新建一个目录并命名为autofillability。 78 79 2. 在autofillability目录,右键选择“New > File”,新建一个.ets文件并命名为AutoFillAbility.ets。例如: 80 81 ```ts 82 import { hilog } from '@kit.PerformanceAnalysisKit'; 83 import { AutoFillExtensionAbility, autoFillManager, UIExtensionContentSession } from '@kit.AbilityKit'; 84 85 class AutoFillAbility extends AutoFillExtensionAbility { 86 // ... 87 // 自动填充服务发起自动填充请求时会触发onFillRequest的生命周期 88 onFillRequest(session: UIExtensionContentSession, request: autoFillManager.FillRequest, callback: autoFillManager.FillRequestCallback) { 89 hilog.info(0x0000, 'testTag', '%{public}s', 'autofill onFillRequest'); 90 try { 91 // 保存onFillRequest请求过来的页面数据和callback数据 92 let obj: Record<string, UIExtensionContentSession | autoFillManager.FillRequestCallback | autoFillManager.ViewData> = { 93 'session': session, 94 'fillCallback': callback, // 自动填充处理结果通过此callback回调到客户端 95 'viewData': request.viewData // 将需要回填的数据组装到viewData中,并通过callback带回到客户端 96 }; 97 let storageFill: LocalStorage = new LocalStorage(obj); 98 // 加载自动填充处理界面 99 session.loadContent('autofillpages/AutoFillPassWord', storageFill); 100 } catch (err) { 101 hilog.error(0x0000, 'testTag', '%{public}s', 'autofill failed to load content'); 102 } 103 } 104 105 // 自动保存服务发起自动保存请求时会触发onSaveRequest的生命周期 106 onSaveRequest(session: UIExtensionContentSession, request: autoFillManager.SaveRequest, callback: autoFillManager.SaveRequestCallback): void { 107 hilog.info(0x0000, 'testTag', '%{public}s', 'autofill onSaveRequest'); 108 try { 109 let obj: Record<string, UIExtensionContentSession | autoFillManager.SaveRequestCallback | autoFillManager.ViewData> = { 110 'session': session, 111 'saveCallback': callback, // 自动保存处理结果通过此callback回调到客户端 112 'viewData': request.viewData // 将需要回填的数据组装到viewData中,并通过callback带回到客户端 113 } 114 // 保存onSaveRequest请求过来的页面数据和callback数据 115 let storageSave: LocalStorage = new LocalStorage(obj); 116 // 加载自动保存处理界面 117 session.loadContent('autofillpages/SavePage', storageSave); 118 } catch (err) { 119 hilog.error(0x0000, 'testTag', '%{public}s', 'autofill failed'); 120 } 121 } 122 } 123 ``` 124 1254. 构建自动填充处理界面。 126 127 1. 在ets目录右键选择“New > Directory”,新建一个目录并命名为autofillpages。 128 129 2. 在autofillpages目录中,右键选择“New > File”,新建一个.ets文件并命名为AutoFillPassWord.ets。 130 131 3. 当点击界面中账号或密码输入框时,自动填充框架会向自动填充服务发起自动填充请求,触发[onFillRequest](../reference/apis-ability-kit/js-apis-app-ability-autoFillExtensionAbility-sys.md#onfillrequest)的生命周期。在onFillRequest生命周期中拉起账号密码备选信息页面(AutoFillPassWord.ets)。 132 133 ```ts 134 import { autoFillManager } from '@kit.AbilityKit'; 135 136 // 将需要回填的数据组装到viewData中,并通过callback的onSuccess带回到客户端用于自动填充 137 function successFunc(data: autoFillManager.ViewData, target: string, fillCallback?: autoFillManager.FillRequestCallback) { 138 console.info(`data.pageNodeInfos.length`, data.pageNodeInfos.length); 139 for (let i = 0; i < data.pageNodeInfos.length; i++) { 140 console.info(`data.pageNodeInfos[i].isFocus`, data.pageNodeInfos[i].isFocus); 141 if (data.pageNodeInfos[i].isFocus == true) { 142 data.pageNodeInfos[i].value = target; 143 break; 144 } 145 } 146 if (fillCallback) { 147 let response: autoFillManager.FillResponse = { viewData: data }; 148 fillCallback.onSuccess(response); 149 } 150 } 151 152 function failFunc(fillCallback?: autoFillManager.FillRequestCallback) { 153 if (fillCallback) { 154 fillCallback.onFailure(); 155 } 156 } 157 158 function cancelFunc(fillContent?: string, fillCallback?: autoFillManager.FillRequestCallback) { 159 if (fillCallback) { 160 try { 161 fillCallback.onCancel(fillContent); 162 } catch (error) { 163 console.error('fillContent undefined: ', JSON.stringify(error)); 164 } 165 } 166 } 167 168 @Entry 169 @Component 170 struct AutoFillControl { 171 storage: LocalStorage | undefined = this.getUIContext().getSharedLocalStorage(); 172 fillCallback: autoFillManager.FillRequestCallback | undefined = this.storage?.get<autoFillManager.FillRequestCallback>('fillCallback'); 173 viewData: autoFillManager.ViewData | undefined = this.storage?.get<autoFillManager.ViewData>('viewData'); 174 175 build() { 176 Column() { 177 Flex({ justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center }) { 178 Text('选择已保存的账号密码') 179 .fontWeight(500) 180 .fontFamily('HarmonyHeiTi-Medium') 181 .fontSize(20) 182 .fontColor('#000000') 183 .margin({ left: '4.4%' }) 184 }.margin({ top: '8.8%', left: '4.9%' }).height('7.2%') 185 186 Row() { 187 Column() { 188 List({ space: 10, initialIndex: 0 }) { 189 ListItem() { 190 Text('15501212262') 191 .width('100%') 192 .height(40) 193 .fontSize(16) 194 .textAlign(TextAlign.Center) 195 .borderRadius(5) 196 } 197 .onClick(() => { 198 if (this.viewData != undefined) { 199 // 将选择的账号信息回填到客户端 200 successFunc(this.viewData, '15501212262', this.fillCallback); 201 } 202 }) 203 } 204 // ... 205 .listDirection(Axis.Vertical) 206 .scrollBar(BarState.Off) 207 .friction(0.6) 208 .divider({ strokeWidth: 1, color: '#fff5eeee', startMargin: 20, endMargin: 20 }) 209 .edgeEffect(EdgeEffect.Spring) 210 .onScrollIndex((firstIndex: number, lastIndex: number, centerIndex: number) => { 211 console.info('first' + firstIndex) 212 console.info('last' + lastIndex) 213 console.info('center' + centerIndex) 214 }) 215 .onDidScroll((scrollOffset: number, scrollState: ScrollState) => { 216 console.info(`onDidScroll scrollState = ScrollState` + scrollState + `scrollOffset = ` + scrollOffset) 217 }) 218 } 219 .width('100%') 220 .shadow(ShadowStyle.OUTER_FLOATING_SM) 221 .margin({ top: 50 }) 222 } 223 224 Row() { 225 Button("Cancel") 226 .onClick(() => { 227 // 放弃本次自动填充的场景触发cancelFunc()通知客户端取消自动填充 228 cancelFunc(undefined, this.fillCallback); 229 }) 230 .margin({ top: 30, bottom: 10, left: 10, right: 10 }) 231 232 Button("Failure") 233 .onClick(() => { 234 // 未获取到账号密码数据的情况下触发failFunc()通知客户端自动填充失败 235 failFunc(this.fillCallback); 236 }) 237 .margin({ top: 30, bottom: 10, left: 10, right: 10 }) 238 } 239 .backgroundColor('#f1f3f5').height('100%') 240 } 241 } 242 } 243 ``` 244 2455. 构建自动保存处理界面。 246 247 1. 在autofillpages目录,右键选择“New > File”,新建一个.ets文件并命名为SavePage.ets。 248 249 2. 当TextInput中存在有信息时,页面切换(点击登录按钮)将触发[onSaveRequest](../reference/apis-ability-kit/js-apis-app-ability-autoFillExtensionAbility-sys.md#onsaverequest)的生命周期。在onSaveRequest中拉起保存信息处理界面(SavePage.ets)。 250 251 ```ts 252 import { autoFillManager } from '@kit.AbilityKit'; 253 import { hilog } from '@kit.PerformanceAnalysisKit'; 254 255 function SuccessFunc(success : boolean, saveRequestCallback?: autoFillManager.SaveRequestCallback) { 256 if (saveRequestCallback) { 257 if (success) { 258 saveRequestCallback.onSuccess(); 259 return; 260 } 261 saveRequestCallback.onFailure(); 262 } 263 hilog.error(0x0000, "testTag", "saveRequestCallback is nullptr!"); 264 } 265 266 @Entry 267 @Component 268 struct SavePage { 269 @State message: string = 'Save Account?' 270 storage: LocalStorage | undefined = this.getUIContext().getSharedLocalStorage(); 271 saveRequestCallback: autoFillManager.SaveRequestCallback | undefined = this.storage?.get<autoFillManager.SaveRequestCallback>('saveCallback'); 272 273 build() { 274 Row() { 275 Column() { 276 Text(this.message) 277 .fontSize(35) 278 .fontWeight(FontWeight.Bold) 279 Row() { 280 // 用户保存表单数据成功,点击页面save按钮触发onSuccess()回调通知客户端保存表单数据成功 281 Button("save") 282 .type(ButtonType.Capsule) 283 .fontSize(20) 284 .margin({ top: 30, right: 30 }) 285 .onClick(() => { 286 SuccessFunc(true, this.saveRequestCallback); 287 }) 288 // 用户保存表单数据失败或放弃保存表单数据,点击页面back按钮触发onFailure()回调通知客户端保存表单数据失败 289 Button("back") 290 .type(ButtonType.Capsule) 291 .fontSize(20) 292 .margin({ top: 30, left: 30 }) 293 .onClick(() => { 294 SuccessFunc(false, this.saveRequestCallback); 295 }) 296 } 297 } 298 .width('100%') 299 } 300 .height('100%') 301 } 302 } 303 ``` 304 305### 实现情景化自动填充功能 306 307情景化自动填充的具体类型可参考[自动填充类型的定义](../reference/apis-ability-kit/js-apis-inner-application-autoFillType-sys.md)。 308 309开发者在实现情景化自动填充服务时,需要在DevEco Studio工程中手动新建一个SmartAutoFillExtensionAbility,具体步骤如下。 310 3111. 设定AutoFillExtensionAbility应用的包名。 312 313 在AppScope/[app.json5配置文件](../quick-start/app-configuration-file.md)中把bundleName设定为:"com.ohos.textautofill",例如: 314 315 ```json 316 "app": { 317 "bundleName": "com.ohos.textautofill", 318 // ... 319 } 320 ``` 321 3222. 配置extensionAbilities信息。 323 324 在entry/src/main/[module.json5配置文件](../quick-start/module-configuration-file.md)中配置AutoFillAbility,例如: 325 326 ```json 327 "extensionAbilities": [ 328 { 329 "name": "AutoFillAbility", 330 "srcEntry": "./ets/autofillability/AutoFillAbility.ets", 331 // ... 332 "type": "autoFill/smart" 333 } 334 ] 335 ``` 336 3373. 情景化自动填充与自动填充服务的实现基本一致。请参考[实现账号密码自动填充功能](#实现账号密码自动填充功能)。 338 339## 开发AutoFillExtensionAbility使用方 340 341开发者可以在主页面中通过点击自动填充组件启动[AutoFillExtensionAbility](../reference/apis-ability-kit/js-apis-app-ability-autoFillExtensionAbility-sys.md)。如在主页面中添加如下内容: 342 343### 添加支持账号密码自动填充能力的组件 344 345```ts 346@Entry 347@Component 348struct Index { 349 loginBtnColor: string = '#bfdbf9'; 350 351 build() { 352 Column() { 353 Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) { 354 Text('Welcome!') 355 .fontSize(24) 356 .fontWeight(500) 357 .fontFamily('HarmonyHeiTi-Medium') 358 .fontColor('#182431') 359 }.margin({ top: '32.3%' }).width('35%').height('4.1%') 360 361 // 添加账号类型的输入框 362 List() { 363 ListItemGroup({ style: ListItemGroupStyle.CARD }) { 364 ListItem({ style: ListItemStyle.CARD }) { 365 TextInput({ placeholder: '请输入账号' }) 366 .type(InputType.USER_NAME) 367 .fontFamily('HarmonyHeiTi') 368 .fontColor('#182431') 369 .fontWeight(400) 370 .fontSize(16) 371 .height('100%') 372 .id('userName') 373 .backgroundColor('#FFFFFF') 374 .onChange((value: string) => { 375 if (value) { 376 this.loginBtnColor = '#007DFF'; 377 } else { 378 this.loginBtnColor = '#bfdbf9'; 379 } 380 }) 381 .enableAutoFill(true) 382 }.padding(0) 383 384 // 添加密码类型的输入框 385 ListItem({ style: ListItemStyle.CARD }) { 386 TextInput({ placeholder: '请输入密码' }) 387 .type(InputType.Password) 388 .fontFamily('HarmonyHeiTi') 389 .fontColor('#182431') 390 .fontWeight(400) 391 .fontSize(16) 392 .height('100%') 393 .backgroundColor('#FFFFFF') 394 .id('passWord') 395 .onChange((value: string) => { 396 if (value) { 397 this.loginBtnColor = '#007DFF'; 398 } else { 399 this.loginBtnColor = '#bfdbf9'; 400 } 401 }) 402 .enableAutoFill(true) 403 }.padding(0) 404 } 405 .backgroundColor('#FFFFFF') 406 .divider({ strokeWidth: 0.5, color: '#f1f3f5', startMargin: 15, endMargin: 15 }) 407 } 408 .borderRadius(24) 409 .width('93.3%') 410 .height('16%') 411 .margin({ top: '8.6%' }) 412 } 413 } 414} 415``` 416 417### 添加支持情景化自动填充能力的组件 418 419```ts 420@Entry 421@Component 422struct Index { 423 @State inputTxt: string = ''; 424 425 build() { 426 Column() { 427 Column() { 428 Flex({ justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center }) { 429 Text('情景化填充') 430 .fontWeight(500) 431 .fontFamily('HarmonyHeiTi-Medium') 432 // ... 433 } 434 .margin({ top: '14.2%' }).height('7.2%') 435 436 Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) { 437 Column() { 438 Row() { 439 Text('设置类型') 440 .fontColor('#99000000') 441 .fontSize(14) 442 .fontWeight(400) 443 .textAlign(TextAlign.Start) 444 .width('91%') 445 .margin({ top: 5, left: -7.5 }) 446 } 447 448 Row() { 449 TextInput({ placeholder: 'Input content', text: this.inputTxt }) 450 .contentType(ContentType.FULL_PHONE_NUMBER)// 情景化自动填充类型 451 .height('9.4%') 452 .width('91%') 453 .fontWeight(FontWeight.Bolder) 454 .placeholderColor('#99000000') 455 .backgroundColor('#ffffffff') 456 .id('password1') 457 .fontSize(16) 458 .fontWeight(400) 459 .borderStyle(BorderStyle.Solid) 460 .enableAutoFill(true) 461 .borderRadius(25) 462 .margin({ top: '8vp' }) 463 } 464 }.margin({ top: '7.1%' }) 465 } 466 467 468 Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) { 469 Column() { 470 Row() { 471 Text('设置类型为姓名') 472 .fontColor('#99000000') 473 .fontSize(14) 474 .fontWeight(400) 475 .textAlign(TextAlign.Start) 476 .width('91%') 477 .margin({ top: 5, left: -7.5 }) 478 } 479 480 Row() { 481 TextInput({ placeholder: 'Name', text: this.inputTxt }) 482 .contentType(ContentType.PERSON_FULL_NAME)// 情景化自动填充类型 483 .height('9.4%') 484 .width('91%') 485 .fontWeight(FontWeight.Bold) 486 .placeholderColor('#99000000') 487 .backgroundColor('#ffffffff') 488 .fontSize(16) 489 .fontWeight(400) 490 .id('password3') 491 .borderStyle(BorderStyle.Solid) 492 .enableAutoFill(true) 493 .borderRadius(25) 494 .onChange(() => { 495 }) 496 .margin({ top: '8vp' }) 497 } 498 } 499 } 500 .margin({ top: '20vp' }) 501 }.height('70%') 502 } 503 .backgroundColor('#ffffff').height('100%') 504 } 505} 506``` 507 508