• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Implementing an Input Method Application
2
3[InputMethodExtensionAbility](../reference/apis-ime-kit/js-apis-inputmethod-extension-ability.md) provides the **onCreate()** and **onDestroy()** callbacks, as described below. Override them as required.
4
5- **onCreate()**
6
7  This callback is triggered when a service is created for the first time. You can perform initialization operations, for example, registering a common event listener.
8
9  > **NOTE**
10  >
11  > If a service has been created, starting it again does not trigger the **onCreate()** callback.
12
13- **onDestroy()**
14
15  This callback is triggered when the service is no longer used and the instance is ready for destruction. You can clear resources in this callback, for example, deregister the listener.
16
17
18## How to Develop
19
20To implement an input method application, manually create an InputMethodExtensionAbility component in DevEco Studio. The procedure is as follows:
21
221. In the **ets** directory of the target module, right-click and choose **New > Directory** to create a directory named **InputMethodExtensionAbility**.
23
242. Right-click the **InputMethodExtensionAbility** directory, choose **New** > **File**, and create four files: **KeyboardController.ts**, **InputMethodService.ts**, **Index.ets**, and **KeyboardKeyData.ts**. The file directory is as follows:
25
26   ```
27   /src/main/
28   ├── ets/InputMethodExtensionAbility
29   │   └──model/KeyboardController.ts		   # Shows the keyboard.
30   │   └──InputMethodService.ts			   # Customizes a class that inherits from InputMethodExtensionAbility and add the required lifecycle callbacks.
31   │   └──pages
32   │      └── Index.ets					  # Draws the keyboard and adds the input and deletion features.
33   │      └── KeyboardKeyData.ts			   # Defines keyboard attributes.
34   ├── resources/base/profile/main_pages.json
35   ```
36
37## Related Files
38
391. **InputMethodService.ts** file:
40
41   In the **InputMethodService.ts** file, add the dependency package for importing InputMethodExtensionAbility. Customize a class that inherits from InputMethodExtensionAbility and add the required lifecycle callbacks.
42
43   ```ts
44   import Want from '@ohos.app.ability.Want';
45   import keyboardController from './model/KeyboardController';
46   import { InputMethodExtensionAbility } from '@kit.IMEKit';
47
48   export default class InputDemoService extends InputMethodExtensionAbility {
49
50     onCreate(want: Want): void {
51       keyboardController.onCreate(this.context); // Initialize the window and register an event listener for the input method framework.
52     }
53
54     onDestroy(): void {
55       console.log("onDestroy.");
56       keyboardController.onDestroy(); // Destroy the window and deregister the event listener.
57     }
58   }
59   ```
60
612. **KeyboardController.ts** file:
62
63   ```ts
64   import common from '@ohos.app.ability.common';
65   import display from '@ohos.display';
66   import { inputMethodEngine, InputMethodExtensionContext } from '@kit.IMEKit';
67
68   // Call the getInputMethodAbility API to obtain an instance, and then call the other APIs of the input method framework based on the instance.
69   const inputMethodAbility: inputMethodEngine.InputMethodAbility = inputMethodEngine.getInputMethodAbility();
70
71   export class KeyboardController {
72     private mContext: InputMethodExtensionContext | undefined = undefined; // Save the context attribute in InputMethodExtensionAbility.
73     private panel: inputMethodEngine.Panel | undefined = undefined;
74     private textInputClient: inputMethodEngine.InputClient | undefined = undefined;
75     private keyboardController: inputMethodEngine.KeyboardController | undefined = undefined;
76
77     constructor() {
78     }
79
80     public onCreate(context: InputMethodExtensionContext): void
81     {
82       this.mContext = context;
83       this.initWindow(); // Initialize the window.
84       this.registerListener(); // Register an event listener for the input method framework.
85     }
86
87     public onDestroy(): void // Destroy the instance.
88     {
89       this.unRegisterListener(); // Deregister the event listener.
90       if(this.panel) { // Destroy the window.
91         this.panel.hide();
92         inputMethodAbility.destroyPanel(this.panel);
93       }
94       if(this.mContext) {
95         this.mContext.destroy();
96       }
97     }
98
99     public insertText(text: string): void {
100       if(this.textInputClient) {
101         this.textInputClient.insertText(text);
102       }
103     }
104
105     public deleteForward(length: number): void {
106       if(this.textInputClient) {
107         this.textInputClient.deleteForward(length);
108       }
109     }
110
111     private initWindow(): void // Initialize the window.
112     {
113       if(this.mContext === undefined) {
114         return;
115       }
116       let dis = display.getDefaultDisplaySync();
117       let dWidth = dis.width;
118       let dHeight = dis.height;
119       let keyHeightRate = 0.47;
120       let keyHeight = dHeight * keyHeightRate;
121       let nonBarPosition = dHeight - keyHeight;
122       let panelInfo: inputMethodEngine.PanelInfo = {
123         type: inputMethodEngine.PanelType.SOFT_KEYBOARD,
124         flag: inputMethodEngine.PanelFlag.FLG_FIXED
125       };
126       inputMethodAbility.createPanel(this.mContext, panelInfo).then(async (inputPanel: inputMethodEngine.Panel) => {
127         this.panel = inputPanel;
128         if(this.panel) {
129           await this.panel.resize(dWidth, keyHeight);
130           await this.panel.moveTo(0, nonBarPosition);
131           await this.panel.setUiContent('inputmethodextability/pages/Index');
132         }
133       });
134     }
135
136     private registerListener(): void
137     {
138       this.registerInputListener(); // Register an event listener for the input method framework service.
139       ...
140       // Register a listener for keyboard hiding.
141     }
142
143     private registerInputListener(): void { // Register a listener for the enabling and disabling events of the input method framework service.
144       inputMethodAbility.on('inputStart', (kbController, textInputClient) => {
145         this.textInputClient = textInputClient; // This is an input method client instance, based on which you can call the APIs that the input method framework provides for the input method.
146         this.keyboardController = kbController;
147       })
148       inputMethodAbility.on('inputStop', () => {
149         this.onDestroy (); // Destroy the KeyboardController instance.
150       });
151     }
152
153     private unRegisterListener(): void
154     {
155       inputMethodAbility.off('inputStart');
156       inputMethodAbility.off('inputStop', () => {});
157     }
158   }
159
160   const keyboardController = new KeyboardController();
161
162   export default keyboardController;
163   ```
164
1653. **KeyboardKeyData.ts** file:
166
167   In this file you can define the content displayed on the soft keyboard.
168
169   ```ts
170   export interface sourceListType {
171     content: string,
172   }
173
174   export let numberSourceListData: sourceListType[] = [
175     {
176       content: '1'
177     },
178     {
179       content: '2'
180     },
181     {
182       content: '3'
183     },
184     {
185       content: '4'
186     },
187     {
188       content: '5'
189     },
190     {
191       content: '6'
192     },
193     {
194       content: '7'
195     },
196     {
197       content: '8'
198     },
199     {
200       content: '9'
201     },
202     {
203       content: '0'
204     }
205   ]
206   ```
207
2084. **Index.ets** file:
209
210   This file describes the functions of keys. For example, the number keys print numbers in the text box, and the delete key deletes what's entered.
211
212   Add the path to this file to the **src** field in the **resources/base/profile/main_pages.json** file.
213
214   ```ets
215   import { numberSourceListData, sourceListType } from './keyboardKeyData';
216   import keyboardController from '../InputMethodExtensionAbility/model/KeyboardController';
217
218   @Component
219   struct keyItem {
220     private keyValue: sourceListType = numberSourceListData[0];
221     @State keyBgc: string = "#fff"
222     @State keyFontColor: string = "#000"
223
224     build() {
225       Column() {
226         Flex({ direction: FlexDirection.Column,
227           alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
228           Text(this.keyValue.content).fontSize(20).fontColor(this.keyFontColor)
229         }
230       }
231       .backgroundColor(this.keyBgc)
232       .borderRadius(6)
233       .width("8%")
234       .height("65%")
235       .onClick(() => {
236         keyboardController.insertText(this.keyValue.content);
237       })
238     }
239   }
240
241   // Component used for deletion.
242   @Component
243   export struct deleteItem {
244     @State keyBgc: string = "#fff"
245     @State keyFontColor: string = "#000"
246
247     build() {
248       Column() {
249         Flex({ direction: FlexDirection.Column,
250           alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
251           Text("Delete").fontSize(20).fontColor(this.keyFontColor)
252         }
253       }
254       .backgroundColor(this.keyBgc)
255       .width("13%")
256       .borderRadius(6)
257       .onClick(() => {
258         keyboardController.deleteForward(1);
259       })
260     }
261   }
262
263   // Numeric keyboard
264   @Component
265   struct numberMenu {
266     private numberList: sourceListType[] = numberSourceListData;
267
268     build() {
269       Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.SpaceEvenly }) {
270         Flex({ justifyContent: FlexAlign.SpaceBetween }) {
271           ForEach(this.numberList, (item: sourceListType) => {// First row on the numeric keyboard
272             keyItem({ keyValue: item })
273           }, (item: sourceListType) => item.content);
274         }
275         .padding({ top: "2%" })
276         .width("96%")
277         .height("25%")
278
279         Flex({ justifyContent: FlexAlign.SpaceBetween }) {
280           deleteItem()
281         }
282         .width("96%")
283         .height("25%")
284       }
285     }
286   }
287
288   @Entry
289   @Component
290   struct Index {
291     private numberList: sourceListType[] = numberSourceListData
292
293     build() {
294       Stack() {
295         Flex({
296           direction: FlexDirection.Column,
297           alignItems: ItemAlign.Center,
298           justifyContent: FlexAlign.End
299         }) {
300               Flex({
301                 direction: FlexDirection.Column,
302                 alignItems: ItemAlign.Center,
303                 justifyContent: FlexAlign.SpaceBetween
304               }) {
305                 numberMenu({
306                   numberList: this.numberList
307                 })
308               }
309               .align(Alignment.End)
310               .width("100%")
311               .height("75%")
312             }
313         .height("100%").align(Alignment.End).backgroundColor("#cdd0d7")
314       }
315       .position({ x: 0, y: 0 }).zIndex(99999)
316     }
317   }
318   ```
319
3205. **module.json5** file:
321
322   Register the InputMethodExtensionAbility in the [module.json5 file](../quick-start/module-configuration-file.md) corresponding to the **Module** project. Set **type** to **"inputMethod"** and **srcEntry** to the code path of the InputMethodExtensionAbility component.
323
324   ```json
325   {
326     "module": {
327       ...
328       "extensionAbilities": [
329         {
330           "description": "inputMethod",
331           "icon": "$media:icon",
332           "name": "InputMethodExtAbility",
333           "srcEntry": "./ets/InputMethodExtensionAbility/InputMethodService.ts",
334           "type": "inputMethod",
335           "exported": true,
336         }
337       ]
338     }
339   }
340   ```
341
342## Verification
343
3441. Start the dialog box that lists the input methods for switching by an API for an application.
345
346     ```ts
347     import { inputMethod } from '@kit.IMEKit';
348     import { BusinessError } from '@ohos.base';
349
350     let inputMethodSetting = inputMethod.getSetting();
351     try {
352       inputMethodSetting.showOptionalInputMethods((err: BusinessError, data: boolean) => {
353         if (err) {
354           console.error(`Failed to showOptionalInputMethods: ${JSON.stringify(err)}`);
355           return;
356         }
357         console.log('Succeeded in showing optionalInputMethods.');
358       });
359     } catch (err) {
360       console.error(`Failed to showOptionalInputMethods: ${JSON.stringify(err)}`);
361     }
362     ```
363
3642. In the dialog box for switching between input methods, switch the input method to the demo application.
365
3663. When you touch any edit box, the demo application should start.
367
368## Constraints
369
370To protect the InputMethodExtensionAbility against abuse, the invoking of APIs in the modules listed below is restricted in the InputMethodExtensionAbility.
371
372> **NOTE**
373>
374> - If a restricted module is imported, no error is reported during compilation, but **undefined** is returned during running, which renders the imported module ineffective.
375> - Currently, access to the [@ohos.multimedia.audio (Audio Management)](../reference/apis-audio-kit/js-apis-audio.md) module is allowed, but subject to the following rules:
376>   - Users who deny the recording permission should still be allowed to use the non-voice-input features of the input method application.
377>   - Recording-related services are allowed only when the InputMethodExtensionAbility is in the foreground. For example, perform recording only when the soft keyboard is in the foreground and the user is proactively using the voice input method; stop recording when the application is switched to the background.
378>   - Applications will be subject to increasingly stringent measures against violations with the preceding rules, and any violation may result in service exceptions.
379> - Strictly comply with the functional constraints of the basic access mode. In this mode, you should provide only basic typing features, not interaction with online services in any form. The system will gradually introduce measures for compliance with the basic access mode, including but not limited to running the Extension process as an independent process and in sandbox mode, preventing the Extension process from creating subprocesses, and restricting inter-process communication and network access. Violations may result in service exceptions.
380
381