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## Overview 11 12The [AutoFillExtensionAbility](../reference/apis-ability-kit/js-apis-app-ability-autoFillExtensionAbility-sys.md) is an [ExtensionAbility](../reference/apis-ability-kit/js-apis-app-ability-extensionAbility.md) component of the AUTO_FILL_PASSWORD or AUTO_FILL_SMART type that provides the auto-fill service. 13 14The auto-fill service can be classified as follows: 15 16- Auto-fill for accounts and passwords: Saved accounts and passwords are automatically populated, improving the efficiency of information input. 17- Scenario-specific auto-fill: Information such as the mobile number and address is automatically populated based on the usage scenario. 18 19In this example, the party that provides the [AutoFillExtensionAbility](../reference/apis-ability-kit/js-apis-app-ability-autoFillExtensionAbility-sys.md) capability is called the provider, and the party that starts the AutoFillExtensionAbility is called the client. 20 21## Available APIs 22 23The table below describes the main APIs related to the auto-fill service. For details about other APIs, see [AutoFillRequest](../reference/apis-ability-kit/js-apis-inner-application-autoFillRequest-sys.md) and [AutoFillExtensionAbility](../reference/apis-ability-kit/js-apis-app-ability-autoFillExtensionAbility-sys.md). 24 25| API | Description | 26| ------------------------------------------------------------ | ------------------------------------------------------------ | 27| onFillRequest(session: UIExtensionContentSession, request: FillRequest, callback: FillRequestCallback): void | Called when an auto-fill request is initiated or a password is generated. | 28| onSaveRequest(session: UIExtensionContentSession, request: SaveRequest, callback: SaveRequestCallback): void | Called when an automatic or manual save request is initiated. | 29| FillRequestCallback.onSuccess(response: FillResponse): void | Implements the callback for an auto-fill request, which is used to automatically fill in or generate a password. The callback can be used to notify the client of the success of the request.| 30 31## Developing the AutoFillExtensionAbility Provider 32 33### Lifecycle 34 35The [AutoFillExtensionAbility](../reference/apis-ability-kit/js-apis-app-ability-autoFillExtensionAbility-sys.md) provides the lifecycle callbacks [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), and [onFillRequest](../reference/apis-ability-kit/js-apis-app-ability-autoFillExtensionAbility-sys.md#onfillrequest). Override them as required. 36 37- **onCreate**: called to initialize the service logic when an AutoFillExtensionAbility is created. 38- **onSessionDestroy**: called when a UIExtensionContentSession instance is destroyed for the AutoFillExtensionAbility. 39- **onForeground**: called when the AutoFillExtensionAbility is switched from the background to the foreground. 40- **onBackground**: called when the AutoFillExtensionAbility is switched from the foreground to the background. 41- **onDestroy**: called to clear resources when the AutoFillExtensionAbility is destroyed. 42- **onSaveRequest**: called to trigger auto-save when form data exists and the page is to be switched. 43- **onFillRequest**: called to automatically fill in the account and password when a fill request is sent. 44 45### Implementing Auto-Fill for Accounts and Passwords 46 47Before implementing auto-fill for accounts and passwords, manually create an AutoFillExtensionAbility in the DevEco Studio project. 48 491. Set the bundle name of the AutoFillExtensionAbility provider. 50 51 In the [app.json5 file](../quick-start/app-configuration-file.md) in the **AppScope** directory, set **bundleName** to **com.ohos.passwordbox**. An example configuration is as follows: 52 53 ```json 54 "app": { 55 "bundleName": "com.ohos.passwordbox", 56 // ... 57 } 58 ``` 59 602. Configuration information about this ExtensionAbility. 61 62 Configure an AutoFillExtensionAbility in the [module.json5 file](../quick-start/module-configuration-file.md) in the **entry/src/main/** directory. An example configuration is as follows: 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. Implement auto-fill and auto-save. 76 77 1. Right-click the **ets** directory, and choose **New > Directory** to create a directory named **autofillability**. 78 79 2. Right-click the **autofillability** directory, and choose **New > File** to create a file named **AutoFillAbility.ets**. An example code snippet is as follows: 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 // The onFillRequest lifecycle callback is triggered when the auto-fill service initiates an auto-fill request. 88 onFillRequest(session: UIExtensionContentSession, request: autoFillManager.FillRequest, callback: autoFillManager.FillRequestCallback) { 89 hilog.info(0x0000, 'testTag', '%{public}s', 'autofill onFillRequest'); 90 try { 91 // Save the page data and callback data carried in onFillRequest. 92 let obj: Record<string, UIExtensionContentSession | autoFillManager.FillRequestCallback | autoFillManager.ViewData> = { 93 'session': session, 94 'fillCallback': callback, // The auto-fill processing result is returned to the client through this callback. 95 'viewData': request.viewData, // Assemble the data to be populated to viewData and return the data to the client through the callback. 96 }; 97 let storageFill: LocalStorage = new LocalStorage(obj); 98 // Load the auto-fill processing page. 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 // The onSaveRequest lifecycle callback is triggered when the auto-save service initiates an auto-save request. 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, // The auto-save processing result is returned to the client through this callback. 112 'viewData': request.viewData, // Assemble the data to be populated to viewData and return the data to the client through the callback. 113 } 114 // Save the page data and callback data carried in onSaveRequest. 115 let storageSave: LocalStorage = new LocalStorage(obj); 116 // Load the auto-save processing page. 117 session.loadContent('autofillpages/SavePage', storageSave); 118 } catch (err) { 119 hilog.error(0x0000, 'testTag', '%{public}s', 'autofill failed'); 120 } 121 } 122 } 123 ``` 124 1254. Build the auto-fill processing page. 126 127 1. Right-click the **ets** directory, and choose **New > Directory** to create a directory named **autofillpages**. 128 129 2. Right-click the **autofillpages** directory, and choose **New > File** to create a file named **AutoFillPassWord.ets**. 130 131 3. When users touch the account or password text box on the page, the auto-fill framework sends an auto-fill request to the auto-fill service to trigger the [onFillRequest](../reference/apis-ability-kit/js-apis-app-ability-autoFillExtensionAbility-sys.md#onfillrequest) lifecycle callback. In the **onFillRequest** lifecycle callback, display the page that shows the available accounts and passwords (implemented by **AutoFillPassWord.ets**). 132 133 ```ts 134 import { autoFillManager } from '@kit.AbilityKit'; 135 136 // Assemble the data to be populated to viewData and use the onSuccess callback to return the data to the client for auto-fill. 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('Select a saved account and password') 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 // Populate the selected account and password in the client. 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 // Call cancelFunc() to notify the client when auto-fill is canceled. 228 cancelFunc(undefined, this.fillCallback); 229 }) 230 .margin({ top: 30, bottom: 10, left: 10, right: 10 }) 231 232 Button("Failure") 233 .onClick(() => { 234 // Call failFunc() to notify the client that auto-fill fails when the account and password are not obtained. 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. Build the auto-save processing page. 246 247 1. Right-click the **autofillpages** directory, and choose **New > File** to create a file named **SavePage.ets**. 248 249 2. When information exists in the **TextInput** component, trigger the [onSaveRequest](../reference/apis-ability-kit/js-apis-app-ability-autoFillExtensionAbility-sys.md#onsaverequest) lifecycle callback during page redirection (a user touches the login button). In the **onSaveRequest** callback, display the information save processing page (implemented by **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 // Call onSuccess() (upon the touch of the save button) to notify the client that the form data is saved successfully. 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 // Call onFailure() (upon the touch of the back button) to notify the client that the user cancels saving the form data or saving the form data fails. 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### Implementing Scenario-specific Auto-Fill 306 307For details about the types of scenario-specific auto-fill, see [AutoFillType](../reference/apis-ability-kit/js-apis-inner-application-autoFillType-sys.md). 308 309Before implementing scenario-specific auto-fill, you need to create a SmartAutoFillExtensionAbility object in the DevEco Studio project. 310 3111. Set the bundle name of the AutoFillExtensionAbility provider. 312 313 In the [app.json5 file](../quick-start/app-configuration-file.md) in the **AppScope** directory, set **bundleName** to **com.ohos.textautofill**. An example configuration is as follows: 314 315 ```json 316 "app": { 317 "bundleName": "com.ohos.textautofill", 318 // ... 319 } 320 ``` 321 3222. Configuration information about this ExtensionAbility. 323 324 Configure an AutoFillExtensionAbility in the [module.json5 file](../quick-start/module-configuration-file.md) in the **entry/src/main/** directory. An example configuration is as follows: 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. The implementation of the scenario-specific auto-fill service is basically the same as that of auto-fill for accounts and passwords. For details, see [Implementing Auto-Fill for Accounts and Passwords](#implementing-auto-fill-for-accounts-and-passwords). 338 339## Developing the AutoFillExtensionAbility Client 340 341You can click the auto-fill component on the home page to start the [AutoFillExtensionAbility](../reference/apis-ability-kit/js-apis-app-ability-autoFillExtensionAbility-sys.md). For example, you can add the following component to the main page: 342 343### Component That Supports Auto-Fill of Accounts and Passwords 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 // Add a text box of the account type. 362 List() { 363 ListItemGroup({ style: ListItemGroupStyle.CARD }) { 364 ListItem({ style: ListItemStyle.CARD }) { 365 TextInput({placeholder: 'Enter an account.'}) 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 // Add a text box of the password type. 385 ListItem({ style: ListItemStyle.CARD }) { 386 TextInput({placeholder: 'Enter the password.'}) 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### Component That Supports Scenario-Specific Auto-Fill 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 ('Scenario-specific population') 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('Set the type.') 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) // Scenario-specific automatic population 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('Set the type to name') 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) // Scenario-specific automatic population 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