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