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. InputMethodExtensionAbility lifecycle:
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 an InputMethodExtensionAbility 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 '@kit.AbilityKit';
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 { display } from '@kit.ArkUI';
65   import { inputMethodEngine, InputMethodExtensionContext } from '@kit.IMEKit';
66
67   // Call the getInputMethodAbility API to obtain an instance, and then call the other APIs of the input method framework based on the instance.
68   const inputMethodAbility: inputMethodEngine.InputMethodAbility = inputMethodEngine.getInputMethodAbility();
69
70   export class KeyboardController {
71     private mContext: InputMethodExtensionContext | undefined = undefined; // Save the context attribute in InputMethodExtensionAbility.
72     private panel: inputMethodEngine.Panel | undefined = undefined;
73     private textInputClient: inputMethodEngine.InputClient | undefined = undefined;
74     private keyboardController: inputMethodEngine.KeyboardController | undefined = undefined;
75
76     constructor() {
77     }
78
79     public onCreate(context: InputMethodExtensionContext): void
80     {
81       this.mContext = context;
82       this.initWindow(); // Initialize the window.
83       this.registerListener(); // Register an event listener for the input method framework.
84     }
85
86     public onDestroy(): void // Destroy the instance.
87     {
88       this.unRegisterListener(); // Deregister the event listener.
89       if(this.panel) { // Destroy the window.
90         inputMethodAbility.destroyPanel(this.panel);
91       }
92       if(this.mContext) {
93         this.mContext.destroy();
94       }
95     }
96
97     public insertText(text: string): void {
98       if(this.textInputClient) {
99         this.textInputClient.insertText(text);
100       }
101     }
102
103     public deleteForward(length: number): void {
104       if(this.textInputClient) {
105         this.textInputClient.deleteForward(length);
106       }
107     }
108
109     private initWindow(): void // Initialize the window.
110     {
111       if(this.mContext === undefined) {
112         return;
113       }
114       let dis = display.getDefaultDisplaySync();
115       let dWidth = dis.width;
116       let dHeight = dis.height;
117       let keyHeightRate = 0.47;
118       let keyHeight = dHeight * keyHeightRate;
119       let nonBarPosition = dHeight - keyHeight;
120       let panelInfo: inputMethodEngine.PanelInfo = {
121         type: inputMethodEngine.PanelType.SOFT_KEYBOARD,
122         flag: inputMethodEngine.PanelFlag.FLG_FIXED
123       };
124       inputMethodAbility.createPanel(this.mContext, panelInfo).then(async (inputPanel: inputMethodEngine.Panel) => {
125         this.panel = inputPanel;
126         if(this.panel) {
127           await this.panel.resize(dWidth, keyHeight);
128           await this.panel.moveTo(0, nonBarPosition);
129           await this.panel.setUiContent('InputMethodExtensionAbility/pages/Index');
130         }
131       });
132     }
133
134     private registerListener(): void
135     {
136       this.registerInputListener(); // Register an event listener for the input method framework service.
137       // Register a listener for keyboard hiding.
138     }
139
140     private registerInputListener(): void { // Register a listener for the enabling and disabling events of the input method framework service.
141       inputMethodAbility.on('inputStart', (kbController, textInputClient) => {
142         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.
143         this.keyboardController = kbController;
144       })
145       inputMethodAbility.on('inputStop', () => {
146         this.onDestroy (); // Destroy the KeyboardController instance.
147       });
148     }
149
150     private unRegisterListener(): void
151     {
152       inputMethodAbility.off('inputStart');
153       inputMethodAbility.off('inputStop', () => {});
154     }
155   }
156
157   const keyboardController = new KeyboardController();
158
159   export default keyboardController;
160   ```
161
1623. **KeyboardKeyData.ts** file:
163
164   In this file you can define the content displayed on the soft keyboard.
165
166   ```ts
167   export interface sourceListType {
168     content: string,
169   }
170
171   export let numberSourceListData: sourceListType[] = [
172     {
173       content: '1'
174     },
175     {
176       content: '2'
177     },
178     {
179       content: '3'
180     },
181     {
182       content: '4'
183     },
184     {
185       content: '5'
186     },
187     {
188       content: '6'
189     },
190     {
191       content: '7'
192     },
193     {
194       content: '8'
195     },
196     {
197       content: '9'
198     },
199     {
200       content: '0'
201     }
202   ]
203   ```
204
2054. **Index.ets** file:
206
207   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.
208
209   Add the path to this file to the **src** field in the **resources/base/profile/main_pages.json** file.
210
211   ```ets
212   import { numberSourceListData, sourceListType } from './KeyboardKeyData';
213   import keyboardController from '../model/KeyboardController';
214
215   @Component
216   struct keyItem {
217     private keyValue: sourceListType = numberSourceListData[0];
218     @State keyBgc: string = "#fff"
219     @State keyFontColor: string = "#000"
220
221     build() {
222       Column() {
223         Flex({ direction: FlexDirection.Column,
224           alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
225           Text(this.keyValue.content).fontSize(20).fontColor(this.keyFontColor)
226         }
227       }
228       .backgroundColor(this.keyBgc)
229       .borderRadius(6)
230       .width("8%")
231       .height("65%")
232       .onClick(() => {
233         keyboardController.insertText(this.keyValue.content);
234       })
235     }
236   }
237
238   // Component used for deletion
239   @Component
240   export struct deleteItem {
241     @State keyBgc: string = "#fff"
242     @State keyFontColor: string = "#000"
243
244     build() {
245       Column() {
246         Flex({ direction: FlexDirection.Column,
247           alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
248           Text("Delete").fontSize(20).fontColor(this.keyFontColor)
249         }
250       }
251       .backgroundColor(this.keyBgc)
252       .width("13%")
253       .borderRadius(6)
254       .onClick(() => {
255         keyboardController.deleteForward(1);
256       })
257     }
258   }
259
260   // Numeric keyboard
261   @Component
262   struct numberMenu {
263     private numberList: sourceListType[] = numberSourceListData;
264
265     build() {
266       Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.SpaceEvenly }) {
267         Flex({ justifyContent: FlexAlign.SpaceBetween }) {
268           ForEach(this.numberList, (item: sourceListType) => { // First row on the numeric keyboard
269             keyItem({ keyValue: item })
270           }, (item: sourceListType) => item.content);
271         }
272         .padding({ top: "2%" })
273         .width("96%")
274         .height("25%")
275
276         Flex({ justifyContent: FlexAlign.SpaceBetween }) {
277           deleteItem()
278         }
279         .width("96%")
280         .height("25%")
281       }
282     }
283   }
284
285   @Entry
286   @Component
287   struct Index {
288     private numberList: sourceListType[] = numberSourceListData
289
290     build() {
291       Stack() {
292         Flex({
293           direction: FlexDirection.Column,
294           alignItems: ItemAlign.Center,
295           justifyContent: FlexAlign.End
296         }) {
297               Flex({
298                 direction: FlexDirection.Column,
299                 alignItems: ItemAlign.Center,
300                 justifyContent: FlexAlign.SpaceBetween
301               }) {
302                 numberMenu({
303                   numberList: this.numberList
304                 })
305               }
306               .align(Alignment.End)
307               .width("100%")
308               .height("75%")
309             }
310         .height("100%").align(Alignment.End).backgroundColor("#cdd0d7")
311       }
312       .position({ x: 0, y: 0 }).zIndex(99999)
313     }
314   }
315   ```
316
3175. **module.json5** file:
318
319   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.
320
321   ```json
322   {
323     "module": {
324       ...
325       "extensionAbilities": [
326         {
327           "description": "inputMethod",
328           "name": "InputMethodExtensionAbility",
329           "icon": "$media:app_icon",
330           "srcEntry": "./ets/InputMethodExtensionAbility/InputMethodService.ts",
331           "type": "inputMethod",
332           "exported": true,
333         }
334       ]
335     }
336   }
337   ```
338
339## Verification Method
340
3411. Start the dialog box that lists the input methods for switching by an API for an application.
342
343     ```ts
344     import { inputMethod } from '@kit.IMEKit';
345     import { BusinessError } from '@kit.BasicServicesKit';
346
347     let inputMethodSetting = inputMethod.getSetting();
348     try {
349       inputMethodSetting.showOptionalInputMethods((err: BusinessError, data: boolean) => {
350         if (err) {
351           console.error(`Failed to showOptionalInputMethods: ${JSON.stringify(err)}`);
352           return;
353         }
354         console.log('Succeeded in showing optionalInputMethods.');
355       });
356     } catch (err) {
357       console.error(`Failed to showOptionalInputMethods: ${JSON.stringify(err)}`);
358     }
359     ```
360
3612. In the dialog box for switching between input methods, switch the input method to the demo application.
362
3633. When you click any edit box, the demo application should start.
364
365## Constraints
366
367To protect the InputMethodExtensionAbility against abuse, functional constraints of the basic access mode are provided.
368
369> **NOTE**
370>
371> 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.
372