• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# InputMethodExtensionAbility Development
2
3[InputMethodExtensionAbility](../reference/apis/js-apis-inputmethod-extension-ability.md) is an ExtensionAbility component of the inputMethod type that provides extension capabilities for the input method framework.
4
5InputMethodExtensionAbility can be started or connected by other application components to process transactions in the background based on the request of the caller.
6
7
8InputMethodExtensionAbility provides related capabilities through the [InputMethodExtensionContext](../reference/apis/js-apis-inputmethod-extension-context.md).
9
10
11## Implementing an Input Method Application
12
13InputMethodExtensionAbility provides the **onCreate()** and **onDestory()** callbacks, as described below. Override them as required.
14
15- **onCreate**
16
17  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.
18
19  > **NOTE**
20  >
21  > If a service has been created, starting it again does not trigger the **onCreate()** callback.
22
23- **onDestroy**
24
25  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.
26
27
28## How to Develop
29
30To implement an input method application, manually create an InputMethodExtensionAbility component in DevEco Studio. The procedure is as follows:
31
32In the **ets** directory of the target module, right-click and choose **New** > **Extention Ability** > **InputMethod** to a minimum template of InputMethodExtensionAbility.
33
34> **NOTE**
35>
36> When compiling the input method application, use the signature at the system_basic level. Otherwise, the application will not be able to start the keyboard.
37
38The minimum template implements an input method application with the most basic features, such as starting the keyboard, entering text, and deleting input. You can diversify the feature set of the application by, for example, adding the feature to hide the keyboard.
39
40The minimum template contains four files: **KeyboardController.ts**, **InputMethodService.ts**, **Index.ets**, and **KeyboardKeyData.ts**. The file directory is as follows:
41
42```
43/src/main/
44├── ets/inputmethodextability
45│   └──model/KeyboardController.ts			# Shows the keyboard.
46│   └──InputMethodService.ts				# Customizes a class that inherits from InputMethodExtensionAbility and add the required lifecycle callbacks.
47│   └──pages
48│      └── Index.ets						# Draws the keyboard and adds the input and deletion features.
49│      └── KeyboardKeyData.ts			    # Defines keyboard attributes.
50├── resources/base/profile/main_pages.json
51```
52
53## File Introduction
54
551. **InputMethodService.ts** file:
56
57   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.
58
59   ```ts
60   import InputMethodExtensionAbility from '@ohos.InputMethodExtensionAbility';
61   import { KeyboardController } from './model/KeyboardController'
62
63   export default class InputDemoService extends InputMethodExtensionAbility {
64     private keyboardController: KeyboardController;
65
66     onCreate(want) {
67       this.keyboardController = new KeyboardController(this.context);
68       this.keyboardController.onCreate();  // Initialize the window and register an event listener for the input method framework.
69     }
70
71     onDestroy() {
72       console.log("onDestroy.");
73       this.context.destroy();
74     }
75   }
76   ```
77
782. **KeyboardController.ts** file:
79
80   ```ts
81   import inputMethodEngine from '@ohos.inputMethodEngine';
82   import display from '@ohos.display';
83   import windowManager from '@ohos.window';
84
85   // Call the getInputMethodAbility API to obtain an instance, and then call the other APIs of the input method framework based on the instance.
86   globalThis.inputAbility = inputMethodEngine.getInputMethodAbility();
87
88   export class KeyboardController {
89     mContext;	// Save the context attribute in InputMethodExtensionAbility.
90     WINDOW_TYPE_INPUT_METHOD_FLOAT = 2105;		// Define the window type. The value 2105 indicates the input method window type, which is used to create an input method application window.
91     windowName = 'inputApp';
92     private windowHeight: number = 0;
93     private windowWidth: number = 0;
94     private nonBarPosition: number = 0;
95     private isWindowShowing: boolean = false;
96
97     constructor(context) {
98       this.mContext = context;
99     }
100
101     public onCreate(): void
102     {
103       this.initWindow();				// Initialize the window.
104       this.registerListener();		// Register an event listener for the input method framework.
105     }
106
107     public onDestroy(): void			// Destroy the instance.
108     {
109       this.unRegisterListener();		// Deregister the event listener.
110       let win = windowManager.findWindow(this.windowName);
111       win.destroyWindow();				// Destroy the window.
112       this.mContext.terminateSelf();	// Terminate the InputMethodExtensionAbility service.
113     }
114
115     private initWindow(): void		// Initialize the window.
116     {
117       let dis = display.getDefaultDisplaySync();
118       let dWidth = dis.width;
119       let dHeight = dis.height;
120       let keyHeightRate = 0.47;
121       let keyHeight = dHeight * keyHeightRate;
122       this.windowWidth = dWidth;
123       this.windowHeight = keyHeight;
124       this.nonBarPosition = dHeight - keyHeight;
125
126       let config = {
127         name: this.windowName,
128         windowType: this.WINDOW_TYPE_INPUT_METHOD_FLOAT,
129         ctx: this.mContext
130       }
131       windowManager.createWindow(config).then((win) => {	// Create a window of the specified type.
132         win.resize(dWidth, keyHeight).then(() => {
133           win.moveWindowTo(0, this.nonBarPosition).then(() => {
134             win.setUIContent('pages/InputMethodExtAbility/Index').then(() => {
135             });
136           });
137         });
138       });
139     }
140
141     private registerListener(): void
142     {
143       this.registerInputListener();	// Register an event listener for the input method framework service.
144       globalThis.inputAbility.on('keyboardShow', () => {// Register an event listener for the keyboard .
145         if (this.isWindowShowing) {
146           return;
147         }
148         this.isWindowShowing = true;
149         this.showHighWindow(); // Show the window.
150       });
151       ...
152       // Register a listener for keyboard hiding.
153     }
154
155     private registerInputListener() {		// Register a listener for the enabling and disabling events of the input method framework service.
156       globalThis.inputAbility.on('inputStart', (kbController, textInputClient) => {
157         globalThis.textInputClient = textInputClient;		// This is an input method client instance, based on which you can call the functional APIs that the input method framework provides for the input method application.
158         globalThis.keyboardController = kbController;
159       })
160       globalThis.inputAbility.on('inputStop', (imeId) => {
161         if (imeId == "Bundle name/Ability name") {
162           this.onDestroy();
163         }
164       });
165     }
166
167     private unRegisterListener(): void
168     {
169       globalThis.inputAbility.off('inputStart');
170       globalThis.inputAbility.off('inputStop', () => {});
171       globalThis.inputAbility.off('keyboardShow');
172     }
173
174     private showHighWindow() {
175       let win = windowManager.findWindow(this.windowName)
176       win.resize(this.windowWidth, this.windowHeight).then(() => {
177         win.moveWindowTo(0, this.nonBarPosition).then(() => {
178           win.showWindow().then(() => {
179             this.isWindowShowing = false;
180           })
181         })
182       })
183     }
184   }
185   ```
186
1873. **KeyboardKeyData.ts** file:
188
189   In this file you can define the content displayed on the soft keyboard.
190
191   ```ts
192   export interface sourceListType {
193     content: string,
194   }
195
196   export let numberSourceListData: sourceListType[] = [
197     {
198       content: '1'
199     },
200     {
201       content: '2'
202     },
203     {
204       content: '3'
205     },
206     {
207       content: '4'
208     },
209     {
210       content: '5'
211     },
212     {
213       content: '6'
214     },
215     {
216       content: '7'
217     },
218     {
219       content: '8'
220     },
221     {
222       content: '9'
223     },
224     {
225       content: '0'
226     }
227   ]
228   ```
229
2304. **Index.ets** file:
231
232   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.
233
234   Add the path to this file to the **src** field in the **resources/base/profile/main_pages.json** file.
235
236   ```ts
237   import { numberSourceListData, sourceListType } from './keyboardKeyData'
238
239   @Component
240   struct keyItem {
241     private keyValue: sourceListType
242     @State keyBgc: string = "#fff"
243     @State keyFontColor: string = "#000"
244
245     build() {
246       Column() {
247         Flex({ direction: FlexDirection.Column,
248           alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
249           Text(this.keyValue.content).fontSize(20).fontColor(this.keyFontColor)
250         }
251       }
252       .backgroundColor(this.keyBgc)
253       .borderRadius(6)
254       .width("8%")
255       .height("65%")
256       .onTouch((event: TouchEvent) => {
257         if (event.type === TouchType.Down) {
258           globalThis.textInputClient.insertText(this.keyValue.content);
259         }
260       })
261     }
262   }
263
264   // Component used for deletion.
265   @Component
266   export struct deleteItem {
267     @State keyBgc: string = "#fff"
268     @State keyFontColor: string = "#000"
269
270     build() {
271       Column() {
272         Flex({ direction: FlexDirection.Column,
273           alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
274           Text("Delete").fontSize(20).fontColor(this.keyFontColor)
275         }
276       }
277       .backgroundColor(this.keyBgc)
278       .width("13%")
279       .borderRadius(6)
280       .onTouch((event: TouchEvent) => {
281         if (event.type === TouchType.Down) {
282           globalThis.textInputClient.deleteForward(1);
283         }
284       })
285     }
286   }
287
288   // Numeric keyboard
289   @Component
290   struct numberMenu {
291     private numberList: sourceListType[]
292
293     build() {
294       Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.SpaceEvenly }) {
295         Flex({ justifyContent: FlexAlign.SpaceBetween }) {
296           ForEach(this.numberList, (item: sourceListType) => {  // First row on the numeric keyboard
297             keyItem({ keyValue: item })
298           }, (item: sourceListType) => item.content);
299         }
300         .padding({ top: "2%" })
301         .width("96%")
302         .height("25%")
303
304         Flex({ justifyContent: FlexAlign.SpaceBetween }) {
305           deleteItem()
306         }
307         .width("96%")
308         .height("25%")
309       }
310     }
311   }
312
313   @Entry
314   @Component
315   struct Index {
316     private numberList: sourceListType[] = numberSourceListData
317
318     build() {
319       Stack() {
320         Flex({
321           direction: FlexDirection.Column,
322           alignItems: ItemAlign.Center,
323           justifyContent: FlexAlign.End
324         }) {
325               Flex({
326                 direction: FlexDirection.Column,
327                 alignItems: ItemAlign.Center,
328                 justifyContent: FlexAlign.SpaceBetween
329               }) {
330                 numberMenu({
331                   numberList: this.numberList
332                 })
333               }
334               .align(Alignment.End)
335               .width("100%")
336               .height("75%")
337             }
338         .height("100%").align(Alignment.End).backgroundColor("#cdd0d7")
339       }
340       .position({ x: 0, y: 0 }).zIndex(99999)
341     }
342   }
343   ```
344
345   Register the InputMethodExtensionAbility in the [module.json5 file](../quick-start/module-configuration-file.md) corresponding to the target module. Set **type** to **"inputMethod"** and **srcEntry** to the code path of the InputMethodExtensionAbility component.
346
347   ```ts
348   {
349     "module": {
350       // ...
351       "extensionAbilities": [
352         {
353           "description": "inputMethod",
354           "icon": "$media:icon",
355           "name": "InputMethodExtAbility",
356           "srcEntry": "./ets/inputmethodextability/InputMethodService.ts",
357           "type": "inputMethod",
358           "exported": true,
359         }
360       ]
361     }
362   }
363   ```
364
365
366
367