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
57entry/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 &gt; Directory”,新建一个目录并命名为autofillpages。
123
124   2. 在autofillpages目录中,右键选择“New &gt; File”,新建一个.ets文件并命名为AutoFillPassWord.ets125
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 &gt; File”,新建一个.ets文件并命名为SavePage.ets243
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
319entry/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