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. InputMethodExtensionAbility lifecycle: 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 an InputMethodExtensionAbility 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 { display } from '@kit.ArkUI'; 65 import { inputMethodEngine, InputMethodExtensionContext } from '@kit.IMEKit'; 66 67 // Call the getInputMethodAbility API to obtain an instance, and then call the other APIs of the input method framework based on the instance. 68 const inputMethodAbility: inputMethodEngine.InputMethodAbility = inputMethodEngine.getInputMethodAbility(); 69 70 export class KeyboardController { 71 private mContext: InputMethodExtensionContext | undefined = undefined; // Save the context attribute in InputMethodExtensionAbility. 72 private panel: inputMethodEngine.Panel | undefined = undefined; 73 private textInputClient: inputMethodEngine.InputClient | undefined = undefined; 74 private keyboardController: inputMethodEngine.KeyboardController | undefined = undefined; 75 76 constructor() { 77 } 78 79 public onCreate(context: InputMethodExtensionContext): void 80 { 81 this.mContext = context; 82 this.initWindow(); // Initialize the window. 83 this.registerListener(); // Register an event listener for the input method framework. 84 } 85 86 public onDestroy(): void // Destroy the instance. 87 { 88 this.unRegisterListener(); // Deregister the event listener. 89 if(this.panel) { // Destroy the window. 90 inputMethodAbility.destroyPanel(this.panel); 91 } 92 if(this.mContext) { 93 this.mContext.destroy(); 94 } 95 } 96 97 public insertText(text: string): void { 98 if(this.textInputClient) { 99 this.textInputClient.insertText(text); 100 } 101 } 102 103 public deleteForward(length: number): void { 104 if(this.textInputClient) { 105 this.textInputClient.deleteForward(length); 106 } 107 } 108 109 private initWindow(): void // Initialize the window. 110 { 111 if(this.mContext === undefined) { 112 return; 113 } 114 let dis = display.getDefaultDisplaySync(); 115 let dWidth = dis.width; 116 let dHeight = dis.height; 117 let keyHeightRate = 0.47; 118 let keyHeight = dHeight * keyHeightRate; 119 let nonBarPosition = dHeight - keyHeight; 120 let panelInfo: inputMethodEngine.PanelInfo = { 121 type: inputMethodEngine.PanelType.SOFT_KEYBOARD, 122 flag: inputMethodEngine.PanelFlag.FLG_FIXED 123 }; 124 inputMethodAbility.createPanel(this.mContext, panelInfo).then(async (inputPanel: inputMethodEngine.Panel) => { 125 this.panel = inputPanel; 126 if(this.panel) { 127 await this.panel.resize(dWidth, keyHeight); 128 await this.panel.moveTo(0, nonBarPosition); 129 await this.panel.setUiContent('InputMethodExtensionAbility/pages/Index'); 130 } 131 }); 132 } 133 134 private registerListener(): void 135 { 136 this.registerInputListener(); // Register an event listener for the input method framework service. 137 // Register a listener for keyboard hiding. 138 } 139 140 private registerInputListener(): void { // Register a listener for the enabling and disabling events of the input method framework service. 141 inputMethodAbility.on('inputStart', (kbController, textInputClient) => { 142 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. 143 this.keyboardController = kbController; 144 }) 145 inputMethodAbility.on('inputStop', () => { 146 this.onDestroy (); // Destroy the KeyboardController instance. 147 }); 148 } 149 150 private unRegisterListener(): void 151 { 152 inputMethodAbility.off('inputStart'); 153 inputMethodAbility.off('inputStop', () => {}); 154 } 155 } 156 157 const keyboardController = new KeyboardController(); 158 159 export default keyboardController; 160 ``` 161 1623. **KeyboardKeyData.ts** file: 163 164 In this file you can define the content displayed on the soft keyboard. 165 166 ```ts 167 export interface sourceListType { 168 content: string, 169 } 170 171 export let numberSourceListData: sourceListType[] = [ 172 { 173 content: '1' 174 }, 175 { 176 content: '2' 177 }, 178 { 179 content: '3' 180 }, 181 { 182 content: '4' 183 }, 184 { 185 content: '5' 186 }, 187 { 188 content: '6' 189 }, 190 { 191 content: '7' 192 }, 193 { 194 content: '8' 195 }, 196 { 197 content: '9' 198 }, 199 { 200 content: '0' 201 } 202 ] 203 ``` 204 2054. **Index.ets** file: 206 207 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. 208 209 Add the path to this file to the **src** field in the **resources/base/profile/main_pages.json** file. 210 211 ```ets 212 import { numberSourceListData, sourceListType } from './KeyboardKeyData'; 213 import keyboardController from '../model/KeyboardController'; 214 215 @Component 216 struct keyItem { 217 private keyValue: sourceListType = numberSourceListData[0]; 218 @State keyBgc: string = "#fff" 219 @State keyFontColor: string = "#000" 220 221 build() { 222 Column() { 223 Flex({ direction: FlexDirection.Column, 224 alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) { 225 Text(this.keyValue.content).fontSize(20).fontColor(this.keyFontColor) 226 } 227 } 228 .backgroundColor(this.keyBgc) 229 .borderRadius(6) 230 .width("8%") 231 .height("65%") 232 .onClick(() => { 233 keyboardController.insertText(this.keyValue.content); 234 }) 235 } 236 } 237 238 // Component used for deletion 239 @Component 240 export struct deleteItem { 241 @State keyBgc: string = "#fff" 242 @State keyFontColor: string = "#000" 243 244 build() { 245 Column() { 246 Flex({ direction: FlexDirection.Column, 247 alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) { 248 Text("Delete").fontSize(20).fontColor(this.keyFontColor) 249 } 250 } 251 .backgroundColor(this.keyBgc) 252 .width("13%") 253 .borderRadius(6) 254 .onClick(() => { 255 keyboardController.deleteForward(1); 256 }) 257 } 258 } 259 260 // Numeric keyboard 261 @Component 262 struct numberMenu { 263 private numberList: sourceListType[] = numberSourceListData; 264 265 build() { 266 Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.SpaceEvenly }) { 267 Flex({ justifyContent: FlexAlign.SpaceBetween }) { 268 ForEach(this.numberList, (item: sourceListType) => { // First row on the numeric keyboard 269 keyItem({ keyValue: item }) 270 }, (item: sourceListType) => item.content); 271 } 272 .padding({ top: "2%" }) 273 .width("96%") 274 .height("25%") 275 276 Flex({ justifyContent: FlexAlign.SpaceBetween }) { 277 deleteItem() 278 } 279 .width("96%") 280 .height("25%") 281 } 282 } 283 } 284 285 @Entry 286 @Component 287 struct Index { 288 private numberList: sourceListType[] = numberSourceListData 289 290 build() { 291 Stack() { 292 Flex({ 293 direction: FlexDirection.Column, 294 alignItems: ItemAlign.Center, 295 justifyContent: FlexAlign.End 296 }) { 297 Flex({ 298 direction: FlexDirection.Column, 299 alignItems: ItemAlign.Center, 300 justifyContent: FlexAlign.SpaceBetween 301 }) { 302 numberMenu({ 303 numberList: this.numberList 304 }) 305 } 306 .align(Alignment.End) 307 .width("100%") 308 .height("75%") 309 } 310 .height("100%").align(Alignment.End).backgroundColor("#cdd0d7") 311 } 312 .position({ x: 0, y: 0 }).zIndex(99999) 313 } 314 } 315 ``` 316 3175. **module.json5** file: 318 319 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. 320 321 ```json 322 { 323 "module": { 324 ... 325 "extensionAbilities": [ 326 { 327 "description": "inputMethod", 328 "name": "InputMethodExtensionAbility", 329 "icon": "$media:app_icon", 330 "srcEntry": "./ets/InputMethodExtensionAbility/InputMethodService.ts", 331 "type": "inputMethod", 332 "exported": true, 333 } 334 ] 335 } 336 } 337 ``` 338 339## Verification Method 340 3411. Start the dialog box that lists the input methods for switching by an API for an application. 342 343 ```ts 344 import { inputMethod } from '@kit.IMEKit'; 345 import { BusinessError } from '@kit.BasicServicesKit'; 346 347 let inputMethodSetting = inputMethod.getSetting(); 348 try { 349 inputMethodSetting.showOptionalInputMethods((err: BusinessError, data: boolean) => { 350 if (err) { 351 console.error(`Failed to showOptionalInputMethods: ${JSON.stringify(err)}`); 352 return; 353 } 354 console.log('Succeeded in showing optionalInputMethods.'); 355 }); 356 } catch (err) { 357 console.error(`Failed to showOptionalInputMethods: ${JSON.stringify(err)}`); 358 } 359 ``` 360 3612. In the dialog box for switching between input methods, switch the input method to the demo application. 362 3633. When you click any edit box, the demo application should start. 364 365## Constraints 366 367To protect the InputMethodExtensionAbility against abuse, functional constraints of the basic access mode are provided. 368 369> **NOTE** 370> 371> 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. 372