• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# InputMethodExtensionAbility
2
3## When to Use
4[InputMethodExtensionAbility](../reference/apis-ime-kit/js-apis-inputmethod-extension-ability.md), inherited from [ExtensionAbility](extensionability-overview.md), is used for developing input method applications.
5
6The entire lifecycle of the [InputMethodExtensionAbility](../reference/apis-ime-kit/js-apis-inputmethod-extension-ability.md) instance and the owning ExtensionAbility process is scheduled and managed by the input method framework. The input method framework provides the [InputMethodExtensionAbility](../reference/apis-ime-kit/js-apis-inputmethod-extension-ability.md) base class. Derive this base class to implement initialization and resource clearing.
7
8[InputMethodExtensionAbility](../reference/apis-ime-kit/js-apis-inputmethod-extension-ability.md) provides related capabilities through [InputMethodExtensionContext](../reference/apis-ime-kit/js-apis-inputmethod-extension-context.md).
9
10
11## Implementing an Input Method Application
12
13[InputMethodExtensionAbility](../reference/apis-ime-kit/js-apis-inputmethod-extension-ability.md) provides the **onCreate()** and **onDestroy()** 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
321. In the **ets** directory of the target module, right-click and choose **New > Directory** to create a directory named **InputMethodExtensionAbility**.
33
342. 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:
35
36```
37/src/main/
38├── ets/InputMethodExtensionAbility
39│   └──model/KeyboardController.ts			# Shows the keyboard.
40│   └──InputMethodService.ts				# Customizes a class that inherits from InputMethodExtensionAbility and add the required lifecycle callbacks.
41│   └──pages
42│      └── Index.ets						# Draws the keyboard and adds the input and deletion features.
43│      └── KeyboardKeyData.ts			    # Defines keyboard attributes.
44├── resources/base/profile/main_pages.json
45```
46
47## File Introduction
48
491. **InputMethodService.ts** file:
50
51   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.
52
53   ```ts
54   import Want from '@ohos.app.ability.Want';
55   import InputMethodExtensionAbility from '@ohos.InputMethodExtensionAbility';
56   import keyboardController from './model/KeyboardController'
57
58   export default class InputDemoService extends InputMethodExtensionAbility {
59
60     onCreate(want: Want): void {
61       keyboardController.onCreate(this.context); // Initialize the window and register an event listener for the input method framework.
62     }
63
64     onDestroy(): void {
65       console.log("onDestroy.");
66       keyboardController.onDestroy(); // Destroy the window and deregister the event listener.
67     }
68   }
69   ```
70
712. **KeyboardController.ts** file:
72
73   ```ts
74   import common from '@ohos.app.ability.common';
75   import display from '@ohos.display';
76   import inputMethodEngine from '@ohos.inputMethodEngine';
77   import InputMethodExtensionContext from '@ohos.InputMethodExtensionContext';
78
79   // Call the getInputMethodAbility API to obtain an instance, and then call the other APIs of the input method framework based on the instance.
80   const inputMethodAbility: inputMethodEngine.InputMethodAbility = inputMethodEngine.getInputMethodAbility();
81
82   export class KeyboardController {
83     private mContext: InputMethodExtensionContext | undefined = undefined; // Save the context attribute in InputMethodExtensionAbility.
84     private panel: inputMethodEngine.Panel | undefined = undefined;
85     private textInputClient: inputMethodEngine.InputClient | undefined = undefined;
86     private keyboardController: inputMethodEngine.KeyboardController | undefined = undefined;
87
88     constructor() {
89     }
90
91     public onCreate(context: InputMethodExtensionContext): void
92     {
93       this.mContext = context;
94       this.initWindow(); // Initialize the window.
95       this.registerListener(); // Register an event listener for the input method framework.
96     }
97
98     public onDestroy(): void // Destroy the instance.
99     {
100       this.unRegisterListener(); // Deregister the event listener.
101       if(this.panel) { // Destroy the window.
102         this.panel.hide();
103         inputMethodAbility.destroyPanel(this.panel);
104       }
105       if(this.mContext) {
106         this.mContext.destroy();
107       }
108     }
109
110     public insertText(text: string): void {
111       if(this.textInputClient) {
112         this.textInputClient.insertText(text);
113       }
114     }
115
116     public deleteForward(length: number): void {
117       if(this.textInputClient) {
118         this.textInputClient.deleteForward(length);
119       }
120     }
121
122     private initWindow(): void // Initialize the window.
123     {
124       if(this.mContext === undefined) {
125         return;
126       }
127       let dis = display.getDefaultDisplaySync();
128       let dWidth = dis.width;
129       let dHeight = dis.height;
130       let keyHeightRate = 0.47;
131       let keyHeight = dHeight * keyHeightRate;
132       let nonBarPosition = dHeight - keyHeight;
133       let panelInfo: inputMethodEngine.PanelInfo = {
134         type: inputMethodEngine.PanelType.SOFT_KEYBOARD,
135         flag: inputMethodEngine.PanelFlag.FLG_FIXED
136       };
137       inputMethodAbility.createPanel(this.mContext, panelInfo).then(async (inputPanel: inputMethodEngine.Panel) => {
138         this.panel = inputPanel;
139         if(this.panel) {
140           await this.panel.resize(dWidth, keyHeight);
141           await this.panel.moveTo(0, nonBarPosition);
142           await this.panel.setUiContent('inputmethodextability/pages/Index');
143         }
144       });
145     }
146
147     private registerListener(): void
148     {
149       this.registerInputListener(); // Register an event listener for the input method framework service.
150       ...
151       // Register a listener for keyboard hiding.
152     }
153
154     private registerInputListener(): void { // Register a listener for the enabling and disabling events of the input method framework service.
155       inputMethodAbility.on('inputStart', (kbController, textInputClient) => {
156         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.
157         this.keyboardController = kbController;
158       })
159       inputMethodAbility.on('inputStop', () => {
160         this.onDestroy (); // Destroy the KeyboardController instance.
161       });
162     }
163
164     private unRegisterListener(): void
165     {
166       inputMethodAbility.off('inputStart');
167       inputMethodAbility.off('inputStop', () => {});
168     }
169   }
170
171   const keyboardController = new KeyboardController();
172
173   export default keyboardController;
174   ```
175
1763. **KeyboardKeyData.ts** file:
177
178   In this file you can define the content displayed on the soft keyboard.
179
180   ```ts
181   export interface sourceListType {
182     content: string,
183   }
184
185   export let numberSourceListData: sourceListType[] = [
186     {
187       content: '1'
188     },
189     {
190       content: '2'
191     },
192     {
193       content: '3'
194     },
195     {
196       content: '4'
197     },
198     {
199       content: '5'
200     },
201     {
202       content: '6'
203     },
204     {
205       content: '7'
206     },
207     {
208       content: '8'
209     },
210     {
211       content: '9'
212     },
213     {
214       content: '0'
215     }
216   ]
217   ```
218
2194. **Index.ets** file:
220
221   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.
222
223   Add the path to this file to the **src** field in the **resources/base/profile/main_pages.json** file.
224
225   ```ets
226   import { numberSourceListData, sourceListType } from './keyboardKeyData';
227   import keyboardController from '../InputMethodExtensionAbility/model/KeyboardController';
228
229   @Component
230   struct keyItem {
231     private keyValue: sourceListType = numberSourceListData[0];
232     @State keyBgc: string = "#fff"
233     @State keyFontColor: string = "#000"
234
235     build() {
236       Column() {
237         Flex({ direction: FlexDirection.Column,
238           alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
239           Text(this.keyValue.content).fontSize(20).fontColor(this.keyFontColor)
240         }
241       }
242       .backgroundColor(this.keyBgc)
243       .borderRadius(6)
244       .width("8%")
245       .height("65%")
246       .onClick(() => {
247         keyboardController.insertText(this.keyValue.content);
248       })
249     }
250   }
251
252   // Component used for deletion.
253   @Component
254   export struct deleteItem {
255     @State keyBgc: string = "#fff"
256     @State keyFontColor: string = "#000"
257
258     build() {
259       Column() {
260         Flex({ direction: FlexDirection.Column,
261           alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {
262           Text("Delete").fontSize(20).fontColor(this.keyFontColor)
263         }
264       }
265       .backgroundColor(this.keyBgc)
266       .width("13%")
267       .borderRadius(6)
268       .onClick(() => {
269         keyboardController.deleteForward(1);
270       })
271     }
272   }
273
274   // Numeric keyboard
275   @Component
276   struct numberMenu {
277     private numberList: sourceListType[] = numberSourceListData;
278
279     build() {
280       Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.SpaceEvenly }) {
281         Flex({ justifyContent: FlexAlign.SpaceBetween }) {
282           ForEach(this.numberList, (item: sourceListType) => {// First row on the numeric keyboard
283             keyItem({ keyValue: item })
284           }, (item: sourceListType) => item.content);
285         }
286         .padding({ top: "2%" })
287         .width("96%")
288         .height("25%")
289
290         Flex({ justifyContent: FlexAlign.SpaceBetween }) {
291           deleteItem()
292         }
293         .width("96%")
294         .height("25%")
295       }
296     }
297   }
298
299   @Entry
300   @Component
301   struct Index {
302     private numberList: sourceListType[] = numberSourceListData
303
304     build() {
305       Stack() {
306         Flex({
307           direction: FlexDirection.Column,
308           alignItems: ItemAlign.Center,
309           justifyContent: FlexAlign.End
310         }) {
311               Flex({
312                 direction: FlexDirection.Column,
313                 alignItems: ItemAlign.Center,
314                 justifyContent: FlexAlign.SpaceBetween
315               }) {
316                 numberMenu({
317                   numberList: this.numberList
318                 })
319               }
320               .align(Alignment.End)
321               .width("100%")
322               .height("75%")
323             }
324         .height("100%").align(Alignment.End).backgroundColor("#cdd0d7")
325       }
326       .position({ x: 0, y: 0 }).zIndex(99999)
327     }
328   }
329   ```
330
3315. 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.
332
333   ```json
334   {
335     "module": {
336       ...
337       "extensionAbilities": [
338         {
339           "description": "inputMethod",
340           "icon": "$media:icon",
341           "name": "InputMethodExtAbility",
342           "srcEntry": "./ets/InputMethodExtensionAbility/InputMethodService.ts",
343           "type": "inputMethod",
344           "exported": true,
345         }
346       ]
347     }
348   }
349   ```
350
351## Verification
352
3531. Run the hdc command to display the dialog box for switching between input methods: **hdc shell aa start ability -a InputMethod -b com.ohos.inputmethodchoosedialog**
354
3552. In the dialog box for switching between input methods, switch the input method to the demo application.
356
3573. When you touch any edit box, the demo application should start.
358
359## Constraints
360
361To protect the InputMethodExtensionAbility against abuse, the invoking of APIs in the modules listed below is restricted in the InputMethodExtensionAbility.
362
363> **NOTE**
364>
365> - If a restricted module is imported, no error is reported during compilation, but an incorrect value (**undefined**) is returned during running, which renders the imported module ineffective.
366> - 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:
367>   - Users who deny the recording permission should still be allowed to use the non-voice-input features of the input method application.
368>   - 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.
369>   - Applications will be subject to increasingly stringent measures against violations with the preceding rules, and any violation may result in service exceptions.
370
371**Module list:**
372
373- [@ohos.ability.featureAbility (FeatureAbility)](../reference/apis-ability-kit/js-apis-ability-featureAbility.md)
374- [@ohos.ability.particleAbility (ParticleAbility)](../reference/apis-ability-kit/js-apis-ability-particleAbility.md)
375- [@ohos.account.distributedAccount (Distributed Account Management)](../reference/apis-basic-services-kit/js-apis-distributed-account.md)
376- [@ohos.backgroundTaskManager (Background Task Management)](../reference/apis-backgroundtasks-kit/js-apis-backgroundTaskManager.md)
377- [@ohos.bluetooth (Bluetooth)](../reference/apis-connectivity-kit/js-apis-bluetooth.md)
378- [@ohos.bluetoothManager (Bluetooth)](../reference/apis-connectivity-kit/js-apis-bluetoothManager.md)
379- [@ohos.connectedTag (Active Tags)](../reference/apis-connectivity-kit/js-apis-connectedTag.md)
380- [@ohos.geolocation (Geolocation)](../reference/apis-location-kit/js-apis-geolocation.md)
381- [@ohos.geoLocationManager (Geolocation Manager)](../reference/apis-location-kit/js-apis-geoLocationManager.md)
382- [@ohos.nfc.cardEmulation (Standard NFC Card Emulation)](../reference/apis-connectivity-kit/js-apis-cardEmulation.md)
383- [@ohos.nfc.controller (Standard NFC)](../reference/apis-connectivity-kit/js-apis-nfcController.md)
384- [@ohos.nfc.tag (Standard NFC Tags)](../reference/apis-connectivity-kit/js-apis-nfcTag.md)
385- [@ohos.reminderAgent (reminderAgent)](../reference/apis-backgroundtasks-kit/js-apis-reminderAgent.md)
386- [@ohos.reminderAgentManager (Agent-Powered Reminders)](../reference/apis-backgroundtasks-kit/js-apis-reminderAgentManager.md)
387- [@ohos.sensor (Sensor)](../reference/apis-sensor-service-kit/js-apis-sensor.md)
388- [@ohos.telephony.call (Call)](../reference/apis-telephony-kit/js-apis-call.md)
389- [@ohos.telephony.data (Cellular Data)](../reference/apis-telephony-kit/js-apis-telephony-data.md)
390- [@ohos.telephony.observer (observer)](../reference/apis-telephony-kit/js-apis-observer.md)
391- [@ohos.telephony.radio (Network Search)](../reference/apis-telephony-kit/js-apis-radio.md)
392- [@ohos.telephony.sim (SIM Management)](../reference/apis-telephony-kit/js-apis-sim.md)
393- [@ohos.telephony.sms (SMS)](../reference/apis-telephony-kit/js-apis-sms.md)
394- [@ohos.telephony.call (Call) (System API)](../reference/apis-telephony-kit/js-apis-call-sys.md)
395- [@ohos.telephony.data (Cellular Data) (System API)](../reference/apis-telephony-kit/js-apis-telephony-data-sys.md)
396- [@ohos.telephony.observer (Observer) (System API)](../reference/apis-telephony-kit/js-apis-observer-sys.md)
397- [@ohos.telephony.radio (Radio) (System API)](../reference/apis-telephony-kit/js-apis-radio-sys.md)
398- [@ohos.telephony.sim (SIM Management) (System API)](../reference/apis-telephony-kit/js-apis-sim-sys.md)
399- [@ohos.telephony.sms (SMS) (System API)](../reference/apis-telephony-kit/js-apis-sms-sys.md)
400- [@ohos.wallpaper (Wallpaper)](../reference/apis-basic-services-kit/js-apis-wallpaper.md)
401- [@ohos.wifiext (WLAN Extension)](../reference/apis-connectivity-kit/js-apis-wifiext.md)
402- [@ohos.wifiManager (WLAN)](../reference/apis-connectivity-kit/js-apis-wifiManager.md)
403- [@ohos.wifiManagerExt (WLAN Extension)](../reference/apis-connectivity-kit/js-apis-wifiManagerExt.md)
404- [@system.geolocation (Geolocation)](../reference/apis-location-kit/js-apis-system-location.md)
405- [nfctech (Standard NFC Technologies)](../reference/apis-connectivity-kit/js-apis-nfctech.md)
406- [tagSession (Standard NFC Tag Session)](../reference/apis-connectivity-kit/js-apis-tagSession.md)
407
408<!--no_check-->
409
410
411