• 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 '@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 { common } from '@kit.AbilityKit';
65   import { display } from '@kit.ArkUI';
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       // Register a listener for keyboard hiding.
140     }
141
142     private registerInputListener(): void { // Register a listener for the enabling and disabling events of the input method framework service.
143       inputMethodAbility.on('inputStart', (kbController, textInputClient) => {
144         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.
145         this.keyboardController = kbController;
146       })
147       inputMethodAbility.on('inputStop', () => {
148         this.onDestroy (); // Destroy the KeyboardController instance.
149       });
150     }
151
152     private unRegisterListener(): void
153     {
154       inputMethodAbility.off('inputStart');
155       inputMethodAbility.off('inputStop', () => {});
156     }
157   }
158
159   const keyboardController = new KeyboardController();
160
161   export default keyboardController;
162   ```
163
1643. **KeyboardKeyData.ts** file:
165
166   In this file you can define the content displayed on the soft keyboard.
167
168   ```ts
169   export interface sourceListType {
170     content: string,
171   }
172
173   export let numberSourceListData: sourceListType[] = [
174     {
175       content: '1'
176     },
177     {
178       content: '2'
179     },
180     {
181       content: '3'
182     },
183     {
184       content: '4'
185     },
186     {
187       content: '5'
188     },
189     {
190       content: '6'
191     },
192     {
193       content: '7'
194     },
195     {
196       content: '8'
197     },
198     {
199       content: '9'
200     },
201     {
202       content: '0'
203     }
204   ]
205   ```
206
2074. **Index.ets** file:
208
209   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.
210
211   Add the path to this file to the **src** field in the **resources/base/profile/main_pages.json** file.
212
213   ```ets
214   import { numberSourceListData, sourceListType } from './keyboardKeyData';
215   import keyboardController from '../InputMethodExtensionAbility/model/KeyboardController';
216
217   @Component
218   struct keyItem {
219     private keyValue: sourceListType = numberSourceListData[0];
220     @State keyBgc: string = "#fff"
221     @State keyFontColor: string = "#000"
222
223     build() {
224       Column() {
225         Flex({ direction: FlexDirection.Column,
226           alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
227           Text(this.keyValue.content).fontSize(20).fontColor(this.keyFontColor)
228         }
229       }
230       .backgroundColor(this.keyBgc)
231       .borderRadius(6)
232       .width("8%")
233       .height("65%")
234       .onClick(() => {
235         keyboardController.insertText(this.keyValue.content);
236       })
237     }
238   }
239
240   // Component used for deletion.
241   @Component
242   export struct deleteItem {
243     @State keyBgc: string = "#fff"
244     @State keyFontColor: string = "#000"
245
246     build() {
247       Column() {
248         Flex({ direction: FlexDirection.Column,
249           alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
250           Text("Delete").fontSize(20).fontColor(this.keyFontColor)
251         }
252       }
253       .backgroundColor(this.keyBgc)
254       .width("13%")
255       .borderRadius(6)
256       .onClick(() => {
257         keyboardController.deleteForward(1);
258       })
259     }
260   }
261
262   // Numeric keyboard
263   @Component
264   struct numberMenu {
265     private numberList: sourceListType[] = numberSourceListData;
266
267     build() {
268       Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.SpaceEvenly }) {
269         Flex({ justifyContent: FlexAlign.SpaceBetween }) {
270           ForEach(this.numberList, (item: sourceListType) => {// First row on the numeric keyboard
271             keyItem({ keyValue: item })
272           }, (item: sourceListType) => item.content);
273         }
274         .padding({ top: "2%" })
275         .width("96%")
276         .height("25%")
277
278         Flex({ justifyContent: FlexAlign.SpaceBetween }) {
279           deleteItem()
280         }
281         .width("96%")
282         .height("25%")
283       }
284     }
285   }
286
287   @Entry
288   @Component
289   struct Index {
290     private numberList: sourceListType[] = numberSourceListData
291
292     build() {
293       Stack() {
294         Flex({
295           direction: FlexDirection.Column,
296           alignItems: ItemAlign.Center,
297           justifyContent: FlexAlign.End
298         }) {
299               Flex({
300                 direction: FlexDirection.Column,
301                 alignItems: ItemAlign.Center,
302                 justifyContent: FlexAlign.SpaceBetween
303               }) {
304                 numberMenu({
305                   numberList: this.numberList
306                 })
307               }
308               .align(Alignment.End)
309               .width("100%")
310               .height("75%")
311             }
312         .height("100%").align(Alignment.End).backgroundColor("#cdd0d7")
313       }
314       .position({ x: 0, y: 0 }).zIndex(99999)
315     }
316   }
317   ```
318
3195. **module.json5** file:
320
321   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.
322
323   ```json
324   {
325     "module": {
326       ...
327       "extensionAbilities": [
328         {
329           "description": "inputMethod",
330           "icon": "$media:icon",
331           "name": "InputMethodExtAbility",
332           "srcEntry": "./ets/InputMethodExtensionAbility/InputMethodService.ts",
333           "type": "inputMethod",
334           "exported": true,
335         }
336       ]
337     }
338   }
339   ```
340
341## Verification
342
3431. Start the dialog box that lists the input methods for switching by an API for an application.
344
345  ```ts
346  import { inputMethod } from '@kit.IMEKit';
347  import { BusinessError } from '@kit.BasicServicesKit';
348
349  let inputMethodSetting = inputMethod.getSetting();
350  try {
351    inputMethodSetting.showOptionalInputMethods((err: BusinessError, data: boolean) => {
352      if (err) {
353        console.error(`Failed to showOptionalInputMethods: ${JSON.stringify(err)}`);
354        return;
355      }
356      console.log('Succeeded in showing optionalInputMethods.');
357    });
358  } catch (err) {
359    console.error(`Failed to showOptionalInputMethods: ${JSON.stringify(err)}`);
360  }
361  ```
362
3632. In the dialog box for switching between input methods, switch the input method to the demo application.
364
3653. When you touch any edit box, the demo application should start.
366
367## Constraints
368
369To protect the InputMethodExtensionAbility against abuse, the invoking of APIs in the modules listed below is restricted in the InputMethodExtensionAbility.
370
371> **NOTE**
372>
373> - If a restricted module is imported, no error is reported during compilation, but **undefined** is returned during running, which renders the imported module ineffective.
374> - 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:
375>   - Users who deny the recording permission should still be allowed to use the non-voice-input features of the input method application.
376>   - 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.
377>   - Applications will be subject to increasingly stringent measures against violations with the preceding rules, and any violation may result in service exceptions.
378> - 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.
379
380
381
382