1# AutoFillExtensionAbility 2 3## Overview 4 5The [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. 6 7The auto-fill service can be classified as follows: 8 9- Auto-fill for accounts and passwords: Saved accounts and passwords are automatically populated, improving the efficiency of information input. 10- Scenario-specific auto-fill: Information such as the mobile number and address is automatically populated based on the usage scenario. 11 12## Available APIs 13 14The 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). 15 16| API | Description | 17| ------------------------------------------------------------ | ------------------------------------------------------------ | 18| onFillRequest(session: UIExtensionContentSession, request: FillRequest, callback: FillRequestCallback): void | Called when an auto-fill request is initiated or a password is generated. | 19| onSaveRequest(session: UIExtensionContentSession, request: SaveRequest, callback: SaveRequestCallback): void | Called when an automatic or manual save request is initiated. | 20| 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.| 21 22## How to Develop 23 24In 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. 25 26### Developing the AutoFillExtensionAbility Provider 27 28#### Lifecycle 29 30The [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#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), and [onFillRequest](../reference/apis-ability-kit/js-apis-app-ability-autoFillExtensionAbility-sys.md#autofillextensionabilityonfillrequest). Override them as required. 31 32- **onCreate**: called to initialize the service logic when an AutoFillExtensionAbility is created. 33- **onSessionDestroy**: called when a **UIExtensionContentSession** instance is destroyed for the AutoFillExtensionAbility. 34- **onForeground**: called when the AutoFillExtensionAbility is switched from the background to the foreground. 35- **onBackground**: called when the AutoFillExtensionAbility is switched from the foreground to the background. 36- **onDestroy**: called to clear resources when the AutoFillExtensionAbility is destroyed. 37- **onSaveRequest**: called to trigger auto-save when form data exists and the page is to be switched. 38- **onFillRequest**: called to automatically fill in the account and password when a fill request is sent. 39 40#### Implementing Auto-Fill for Accounts and Passwords 41 42Before implementing auto-fill for accounts and passwords, manually create an AutoFillExtensionAbility in the DevEco Studio project. 43 441. Set the bundle name of the AutoFillExtensionAbility provider. 45 46 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: 47 48 ```json 49 "app": { 50 "bundleName": "com.ohos.passwordbox", 51 // ... 52 } 53 ``` 54 552. Configuration information about this ExtensionAbility. 56 57 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: 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. Implement auto-fill and auto-save. 71 72 1. Right-click the **ets** directory, and choose **New > Directory** to create a directory named **autofillability**. 73 74 2. Right-click the **autofillability** directory, and choose **New > File** to create a file named **AutoFillAbility.ets**. An example code snippet is as follows: 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 // The onFillRequest lifecycle callback is triggered when the auto-fill service initiates an auto-fill request. 83 onFillRequest(session: UIExtensionContentSession, request: autoFillManager.FillRequest, callback: autoFillManager.FillRequestCallback) { 84 hilog.info(0x0000, 'testTag', '%{public}s', 'autofill onFillRequest'); 85 try { 86 // Save the page data and callback data carried in onFillRequest. 87 let obj: Record<string, UIExtensionContentSession | autoFillManager.FillRequestCallback | autoFillManager.ViewData> = { 88 'session': session, 89 'fillCallback': callback, // The auto-fill processing result is returned to the client through this callback. 90 'viewData': request.viewData, // Assemble the data to be populated to viewData and return the data to the client through the callback. 91 }; 92 let storageFill: LocalStorage = new LocalStorage(obj); 93 // Load the auto-fill processing page. 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 // The onSaveRequest lifecycle callback is triggered when the auto-save service initiates an auto-save request. 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, // The auto-save processing result is returned to the client through this callback. 107 'viewData': request.viewData, // Assemble the data to be populated to viewData and return the data to the client through the callback. 108 } 109 // Save the page data and callback data carried in onSaveRequest. 110 let storageSave: LocalStorage = new LocalStorage(obj); 111 // Load the auto-save processing page. 112 session.loadContent('autofillpages/SavePage', storageSave); 113 } catch (err) { 114 hilog.error(0x0000, 'testTag', '%{public}s', 'autofill failed'); 115 } 116 } 117 } 118 ``` 119 1204. Build the auto-fill processing page. 121 122 1. Right-click the **ets** directory, and choose **New > Directory** to create a directory named **autofillpages**. 123 124 2. Right-click the **autofillpages** directory, and choose **New > File** to create a file named **AutoFillPassWord.ets**. 125 126 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#autofillextensionabilityonfillrequest) lifecycle callback. In the **onFillRequest** lifecycle callback, display the page that shows the available accounts and passwords (implemented by **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 // Assemble the data to be populated to viewData and use the onSuccess callback to return the data to the client for auto-fill. 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('Select a saved account and password') 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 // Populate the selected account and password in the client. 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 // Call cancelFunc() to notify the client when auto-fill is canceled. 223 cancelFunc(); 224 }) 225 .margin({ top: 30, bottom: 10, left: 10, right: 10 }) 226 227 Button("Failure") 228 .onClick(() => { 229 // Call failFunc() to notify the client that auto-fill fails when the account and password are not obtained. 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. Build the auto-save processing page. 241 242 1. Right-click the **autofillpages** directory, and choose **New > File** to create a file named **SavePage.ets**. 243 244 2. When information exists in the **TextInput** component, trigger the [onSaveRequest](../reference/apis-ability-kit/js-apis-app-ability-autoFillExtensionAbility-sys.md#autofillextensionabilityonsaverequest) 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**). 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 // Call onSuccess() (upon the touch of the save button) to notify the client that the form data is saved successfully. 276 Button("save") 277 .type(ButtonType.Capsule) 278 .fontSize(20) 279 .margin({ top: 30, right: 30 }) 280 .onClick(() => { 281 SuccessFunc(true); 282 }) 283 // 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. 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#### Implementing Scenario-specific Auto-Fill 301 302For details about the types of scenario-specific auto-fill, see [AutoFillType](../reference/apis-ability-kit/js-apis-inner-application-autoFillType-sys.md). 303 304Before implementing scenario-specific auto-fill, you need to create a **SmartAutoFillExtensionAbility** object in the DevEco Studio project. 305 3061. Set the bundle name of the AutoFillExtensionAbility provider. 307 308 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: 309 310 ```json 311 "app": { 312 "bundleName": "com.ohos.textautofill", 313 // ... 314 } 315 ``` 316 3172. Configuration information about this ExtensionAbility. 318 319 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: 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. 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). 333 334### Developing the AutoFillExtensionAbility Client 335 336You 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: 337 338#### Component That Supports Auto-Fill of Accounts and Passwords 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 // Add a text box of the account type. 357 List() { 358 ListItemGroup({ style: ListItemGroupStyle.CARD }) { 359 ListItem({ style: ListItemStyle.CARD }) { 360 TextInput({placeholder: 'Enter an account.'}) 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 // Add a text box of the password type. 380 ListItem({ style: ListItemStyle.CARD }) { 381 TextInput({placeholder: 'Enter the password.'}) 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#### Component That Supports Scenario-Specific Auto-Fill 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 ('Scenario-specific population') 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('Set the type.') 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) // Scenario-specific automatic population 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('Set the type to name') 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) // Scenario-specific automatic population 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