• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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## 概述
11
12[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)组件,提供自动填充能力。
13
14自动填充能力根据自动填充控件内容的不同,分为账号密码自动填充和情景化自动填充。
15
16- 账号密码自动填充:帮助用户自动填充已保存的账号密码,提高用户输入信息的效率。
17- 情景化自动填充:根据组件的使用场景实现手机号、地址等信息的自动填充。
18
19为了便于表述,本例中将提供[AutoFillExtensionAbility](../reference/apis-ability-kit/js-apis-app-ability-autoFillExtensionAbility-sys.md)能力的一方称为提供方,将启动AutoFillExtensionAbility的一方称为使用方。
20
21## 接口说明
22
23自动填充功能主要接口如下。其他接口介绍详情参见[AutoFillRequest](../reference/apis-ability-kit/js-apis-inner-application-autoFillRequest-sys.md)、[AutoFillExtensionAbility](../reference/apis-ability-kit/js-apis-app-ability-autoFillExtensionAbility-sys.md)。
24
25| 接口名称                                                     | 说明                                                         |
26| ------------------------------------------------------------ | ------------------------------------------------------------ |
27| onFillRequest(session: UIExtensionContentSession, request: FillRequest, callback: FillRequestCallback): void | 当发起自动填充请求或者生成密码时触发此回调函数。             |
28| onSaveRequest(session: UIExtensionContentSession, request: SaveRequest, callback: SaveRequestCallback): void | 当发起自动保存或者手动保存时触发此回调函数。                 |
29| FillRequestCallback.onSuccess(response: FillResponse): void  | 自动填充或者生成密码时的回调对象,可以通过此回调通知客户端成功。 |
30
31## 开发AutoFillExtensionAbility提供方
32
33### 生命周期
34
35[AutoFillExtensionAbility](../reference/apis-ability-kit/js-apis-app-ability-autoFillExtensionAbility-sys.md)提供了[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)和[onFillRequest](../reference/apis-ability-kit/js-apis-app-ability-autoFillExtensionAbility-sys.md#onfillrequest)生命周期回调,根据需要重写对应的回调方法。
36
37- **onCreate**:当AutoFillExtensionAbility创建时回调,执行初始化业务逻辑操作。
38- **onSessionDestroy**:当AutoFillExtensionAbility界面内容对象销毁后调用。
39- **onForeground**:当AutoFillExtensionAbility从后台转到前台时触发。
40- **onBackground**:当AutoFillExtensionAbility从前台转到后台时触发。
41- **onDestroy**:当AutoFillExtensionAbility销毁时回调,可以执行资源清理等操作。
42- **onSaveRequest**:表单中有数据存在并且切换页面时,会触发自动保存的生命周期回调。
43- **onFillRequest**:当发起自动填充请求或者生成密码时,会触发该生命周期回调。
44
45### 实现账号密码自动填充功能
46
47开发者在实现自动填充服务时,需要在DevEco Studio工程中手动新建一个AutoFillExtensionAbility。
48
491. 设定AutoFillExtensionAbility应用的包名。
50
51   在AppScope/[app.json5配置文件](../quick-start/app-configuration-file.md)中把bundleName设定为:"com.ohos.passwordbox",例如:
52
53   ```json
54   "app": {
55     "bundleName": "com.ohos.passwordbox",
56      // ...
57   }
58   ```
59
602. 配置extensionAbilities信息。
61
62entry/src/main/[module.json5配置文件](../quick-start/module-configuration-file.md)中配置AutoFillAbility,例如:
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. 实现自动填充与自动保存。
76
77   1. 在ets目录右键选择“New &gt; Directory”,新建一个目录并命名为autofillability。
78
79   2. 在autofillability目录,右键选择“New &gt; File”,新建一个.ets文件并命名为AutoFillAbility.ets。例如:
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        // 自动填充服务发起自动填充请求时会触发onFillRequest的生命周期
88        onFillRequest(session: UIExtensionContentSession, request: autoFillManager.FillRequest, callback: autoFillManager.FillRequestCallback) {
89          hilog.info(0x0000, 'testTag', '%{public}s', 'autofill onFillRequest');
90          try {
91            // 保存onFillRequest请求过来的页面数据和callback数据
92            let obj: Record<string, UIExtensionContentSession | autoFillManager.FillRequestCallback | autoFillManager.ViewData> = {
93              'session': session,
94              'fillCallback': callback, // 自动填充处理结果通过此callback回调到客户端
95              'viewData': request.viewData // 将需要回填的数据组装到viewData中,并通过callback带回到客户端
96            };
97            let storageFill: LocalStorage = new LocalStorage(obj);
98            // 加载自动填充处理界面
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        // 自动保存服务发起自动保存请求时会触发onSaveRequest的生命周期
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, // 自动保存处理结果通过此callback回调到客户端
112              'viewData': request.viewData // 将需要回填的数据组装到viewData中,并通过callback带回到客户端
113            }
114            // 保存onSaveRequest请求过来的页面数据和callback数据
115            let storageSave: LocalStorage = new LocalStorage(obj);
116            // 加载自动保存处理界面
117            session.loadContent('autofillpages/SavePage', storageSave);
118          } catch (err) {
119            hilog.error(0x0000, 'testTag', '%{public}s', 'autofill failed');
120          }
121        }
122      }
123      ```
124
1254. 构建自动填充处理界面。
126
127   1. 在ets目录右键选择“New &gt; Directory”,新建一个目录并命名为autofillpages。
128
129   2. 在autofillpages目录中,右键选择“New &gt; File”,新建一个.ets文件并命名为AutoFillPassWord.ets130
131   3. 当点击界面中账号或密码输入框时,自动填充框架会向自动填充服务发起自动填充请求,触发[onFillRequest](../reference/apis-ability-kit/js-apis-app-ability-autoFillExtensionAbility-sys.md#onfillrequest)的生命周期。在onFillRequest生命周期中拉起账号密码备选信息页面(AutoFillPassWord.ets)。
132
133      ```ts
134      import { autoFillManager } from '@kit.AbilityKit';
135
136      // 将需要回填的数据组装到viewData中,并通过callback的onSuccess带回到客户端用于自动填充
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('选择已保存的账号密码')
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                      // 将选择的账号信息回填到客户端
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                  // 放弃本次自动填充的场景触发cancelFunc()通知客户端取消自动填充
228                  cancelFunc(undefined, this.fillCallback);
229                })
230                .margin({ top: 30, bottom: 10, left: 10, right: 10 })
231
232              Button("Failure")
233                .onClick(() => {
234                  // 未获取到账号密码数据的情况下触发failFunc()通知客户端自动填充失败
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. 构建自动保存处理界面。
246
247   1. 在autofillpages目录,右键选择“New &gt; File”,新建一个.ets文件并命名为SavePage.ets248
249   2. 当TextInput中存在有信息时,页面切换(点击登录按钮)将触发[onSaveRequest](../reference/apis-ability-kit/js-apis-app-ability-autoFillExtensionAbility-sys.md#onsaverequest)的生命周期。在onSaveRequest中拉起保存信息处理界面(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                // 用户保存表单数据成功,点击页面save按钮触发onSuccess()回调通知客户端保存表单数据成功
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                // 用户保存表单数据失败或放弃保存表单数据,点击页面back按钮触发onFailure()回调通知客户端保存表单数据失败
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### 实现情景化自动填充功能
306
307情景化自动填充的具体类型可参考[自动填充类型的定义](../reference/apis-ability-kit/js-apis-inner-application-autoFillType-sys.md)。
308
309开发者在实现情景化自动填充服务时,需要在DevEco Studio工程中手动新建一个SmartAutoFillExtensionAbility,具体步骤如下。
310
3111. 设定AutoFillExtensionAbility应用的包名。
312
313   在AppScope/[app.json5配置文件](../quick-start/app-configuration-file.md)中把bundleName设定为:"com.ohos.textautofill",例如:
314
315   ```json
316   "app": {
317     "bundleName": "com.ohos.textautofill",
318      // ...
319   }
320   ```
321
3222. 配置extensionAbilities信息。
323
324entry/src/main/[module.json5配置文件](../quick-start/module-configuration-file.md)中配置AutoFillAbility,例如:
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. 情景化自动填充与自动填充服务的实现基本一致。请参考[实现账号密码自动填充功能](#实现账号密码自动填充功能)。
338
339## 开发AutoFillExtensionAbility使用方
340
341开发者可以在主页面中通过点击自动填充组件启动[AutoFillExtensionAbility](../reference/apis-ability-kit/js-apis-app-ability-autoFillExtensionAbility-sys.md)。如在主页面中添加如下内容:
342
343### 添加支持账号密码自动填充能力的组件
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      // 添加账号类型的输入框
362      List() {
363        ListItemGroup({ style: ListItemGroupStyle.CARD }) {
364          ListItem({ style: ListItemStyle.CARD }) {
365            TextInput({ placeholder: '请输入账号' })
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          // 添加密码类型的输入框
385          ListItem({ style: ListItemStyle.CARD }) {
386            TextInput({ placeholder: '请输入密码' })
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### 添加支持情景化自动填充能力的组件
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('情景化填充')
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('设置类型')
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)// 情景化自动填充类型
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('设置类型为姓名')
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)// 情景化自动填充类型
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
508