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