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