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