• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Implementing an Input Method Application
2<!--Kit: IME Kit-->
3<!--Subsystem: MiscServices-->
4<!--Owner: @illybyy-->
5<!--Designer: @andeszhang-->
6<!--Tester: @murphy1984-->
7<!--Adviser: @zhang_yixin13-->
8
9[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:
10
11- **onCreate()**
12
13  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.
14
15  > **NOTE**
16  >
17  > If an InputMethodExtensionAbility has been created, starting it again does not trigger the **onCreate()** callback.
18
19- **onDestroy()**
20
21  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.
22
23
24## How to Develop
25
26<!--RP1-->
27To implement an input method application, manually create an InputMethodExtensionAbility component in DevEco Studio. The procedure is as follows:
28
291. In the **ets** directory of the target module, right-click and choose **New** > **Directory** to create a directory named **InputMethodExtensionAbility**.
30
312. 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:
32
33```
34/src/main/
35├── ets/InputMethodExtensionAbility
36│       └──model/KeyboardController.ts			# Shows the keyboard.
37│       └──InputMethodService.ts				# Customizes a class that inherits from InputMethodExtensionAbility and add the required lifecycle callbacks.
38│       └──pages
39│         └── Index.ets						# Draws the keyboard and adds the input and deletion features.
40│         └── KeyboardKeyData.ts			    # Defines keyboard attributes.
41├── resources/base/profile/main_pages.json
42```
43<!--RP1End-->
44
45## Related Files
46
471. **InputMethodService.ts** file:
48
49   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.
50
51   ```ts
52   import { Want } from '@kit.AbilityKit';
53   import keyboardController from './model/KeyboardController';
54   import { InputMethodExtensionAbility } from '@kit.IMEKit';
55
56   export default class InputDemoService extends InputMethodExtensionAbility {
57
58     onCreate(want: Want): void {
59       keyboardController.onCreate(this.context); // Initialize the window and register an event listener for the input method framework.
60     }
61
62     onDestroy(): void {
63       console.log("onDestroy.");
64       keyboardController.onDestroy(); // Destroy the window and deregister the event listener.
65     }
66   }
67   ```
68
69<!--RP2-->
702. **KeyboardController.ts** file:
71
72   ```ts
73   import { display } from '@kit.ArkUI';
74   import { inputMethodEngine, InputMethodExtensionContext } from '@kit.IMEKit';
75
76   // Call the getInputMethodAbility API to obtain an instance, and then call the other APIs of the input method framework based on the instance.
77   const inputMethodAbility: inputMethodEngine.InputMethodAbility = inputMethodEngine.getInputMethodAbility();
78
79   export class KeyboardController {
80     private mContext: InputMethodExtensionContext | undefined = undefined; // Save the context attribute in InputMethodExtensionAbility.
81     private panel: inputMethodEngine.Panel | undefined = undefined;
82     private textInputClient: inputMethodEngine.InputClient | undefined = undefined;
83     private keyboardController: inputMethodEngine.KeyboardController | undefined = undefined;
84
85     constructor() {
86     }
87
88     public onCreate(context: InputMethodExtensionContext): void
89     {
90       this.mContext = context;
91       this.initWindow(); // Initialize the window.
92       this.registerListener(); // Register an event listener for the input method framework.
93     }
94
95     public onDestroy(): void // Destroy the instance.
96     {
97       this.unRegisterListener(); // Deregister the event listener.
98       if(this.panel) { // Destroy the window.
99         inputMethodAbility.destroyPanel(this.panel);
100       }
101       if(this.mContext) {
102         this.mContext.destroy();
103       }
104     }
105
106     public insertText(text: string): void {
107       if(this.textInputClient) {
108         this.textInputClient.insertText(text);
109       }
110     }
111
112     public deleteForward(length: number): void {
113       if(this.textInputClient) {
114         this.textInputClient.deleteForward(length);
115       }
116     }
117
118     private initWindow(): void // Initialize the window.
119     {
120       if(this.mContext === undefined) {
121         return;
122       }
123       let dis = display.getDefaultDisplaySync();
124       let dWidth = dis.width;
125       let dHeight = dis.height;
126       let keyHeightRate = 0.47;
127       let keyHeight = dHeight * keyHeightRate;
128       let nonBarPosition = dHeight - keyHeight;
129       let panelInfo: inputMethodEngine.PanelInfo = {
130         type: inputMethodEngine.PanelType.SOFT_KEYBOARD,
131         flag: inputMethodEngine.PanelFlag.FLG_FIXED
132       };
133       inputMethodAbility.createPanel(this.mContext, panelInfo).then(async (inputPanel: inputMethodEngine.Panel) => {
134         this.panel = inputPanel;
135         if(this.panel) {
136           await this.panel.resize(dWidth, keyHeight);
137           await this.panel.moveTo(0, nonBarPosition);
138           await this.panel.setUiContent('InputMethodExtensionAbility/pages/Index');
139         }
140       });
141     }
142
143     private registerListener(): void
144     {
145       this.registerInputListener(); // Register an event listener for the input method framework service.
146       // Register a listener for keyboard hiding.
147     }
148
149     private registerInputListener(): void { // Register a listener for the enabling and disabling events of the input method framework service.
150       inputMethodAbility.on('inputStart', (kbController, textInputClient) => {
151         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.
152         this.keyboardController = kbController;
153       })
154       inputMethodAbility.on('inputStop', () => {
155         this.onDestroy (); // Destroy the KeyboardController instance.
156       });
157     }
158
159     private unRegisterListener(): void
160     {
161       inputMethodAbility.off('inputStart');
162       inputMethodAbility.off('inputStop', () => {});
163     }
164   }
165
166   const keyboardController = new KeyboardController();
167
168   export default keyboardController;
169   ```
170<!--RP2End-->
1713. **KeyboardKeyData.ts** file:
172
173   In this file you can define the content displayed on the soft keyboard.
174
175   ```ts
176   export interface sourceListType {
177     content: string,
178   }
179
180   export let numberSourceListData: sourceListType[] = [
181     {
182       content: '1'
183     },
184     {
185       content: '2'
186     },
187     {
188       content: '3'
189     },
190     {
191       content: '4'
192     },
193     {
194       content: '5'
195     },
196     {
197       content: '6'
198     },
199     {
200       content: '7'
201     },
202     {
203       content: '8'
204     },
205     {
206       content: '9'
207     },
208     {
209       content: '0'
210     }
211   ]
212   ```
213
2144. **Index.ets** file:
215
216   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.
217
218   Add the path to this file to the **src** field in the **resources/base/profile/main_pages.json** file.
219
220   ```ets
221   import { numberSourceListData, sourceListType } from './KeyboardKeyData';
222   import keyboardController from '../model/KeyboardController';
223
224   @Component
225   struct keyItem {
226     private keyValue: sourceListType = numberSourceListData[0];
227     @State keyBgc: string = "#fff"
228     @State keyFontColor: string = "#000"
229
230     build() {
231       Column() {
232         Flex({ direction: FlexDirection.Column,
233           alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
234           Text(this.keyValue.content).fontSize(20).fontColor(this.keyFontColor)
235         }
236       }
237       .backgroundColor(this.keyBgc)
238       .borderRadius(6)
239       .width("8%")
240       .height("65%")
241       .onClick(() => {
242         keyboardController.insertText(this.keyValue.content);
243       })
244     }
245   }
246
247   // Component used for deletion
248   @Component
249   export struct deleteItem {
250     @State keyBgc: string = "#fff"
251     @State keyFontColor: string = "#000"
252
253     build() {
254       Column() {
255         Flex({ direction: FlexDirection.Column,
256           alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
257           Text("Delete").fontSize(20).fontColor(this.keyFontColor)
258         }
259       }
260       .backgroundColor(this.keyBgc)
261       .width("13%")
262       .borderRadius(6)
263       .onClick(() => {
264         keyboardController.deleteForward(1);
265       })
266     }
267   }
268
269   // Numeric keyboard
270   @Component
271   struct numberMenu {
272     private numberList: sourceListType[] = numberSourceListData;
273
274     build() {
275       Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.SpaceEvenly }) {
276         Flex({ justifyContent: FlexAlign.SpaceBetween }) {
277           ForEach(this.numberList, (item: sourceListType) => { // First row on the numeric keyboard
278             keyItem({ keyValue: item })
279           }, (item: sourceListType) => item.content);
280         }
281         .padding({ top: "2%" })
282         .width("96%")
283         .height("25%")
284
285         Flex({ justifyContent: FlexAlign.SpaceBetween }) {
286           deleteItem()
287         }
288         .width("96%")
289         .height("25%")
290       }
291     }
292   }
293
294   @Entry
295   @Component
296   struct Index {
297     private numberList: sourceListType[] = numberSourceListData
298
299     build() {
300       Stack() {
301         Flex({
302           direction: FlexDirection.Column,
303           alignItems: ItemAlign.Center,
304           justifyContent: FlexAlign.End
305         }) {
306               Flex({
307                 direction: FlexDirection.Column,
308                 alignItems: ItemAlign.Center,
309                 justifyContent: FlexAlign.SpaceBetween
310               }) {
311                 numberMenu({
312                   numberList: this.numberList
313                 })
314               }
315               .align(Alignment.End)
316               .width("100%")
317               .height("75%")
318             }
319         .height("100%").align(Alignment.End).backgroundColor("#cdd0d7")
320       }
321       .position({ x: 0, y: 0 }).zIndex(99999)
322     }
323   }
324   ```
325
326<!--Del-->
3275. **module.json5** file:<br>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.
328
329   ```json
330   {
331     "module": {
332       ...
333       "extensionAbilities": [
334         {
335           "description": "inputMethod",
336           "name": "InputMethodExtensionAbility",
337           "icon": "$media:app_icon",
338           "srcEntry": "./ets/InputMethodExtensionAbility/InputMethodService.ts",
339           "type": "inputMethod",
340           "exported": true
341         }
342       ]
343     }
344   }
345   ```
346<!--DelEnd-->
347
348
349<!--RP3-->
350
351<!--RP3End-->
352
353## Constraints
354
355To protect the InputMethodExtensionAbility against abuse, functional constraints of the basic access mode are provided.
356
357> **NOTE**
358>
359> 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.
360