1# Implementing an Input Method Application 2<!--Kit: IME Kit--> 3<!--Subsystem: MiscServices--> 4<!--Owner: @illybyy--> 5<!--Designer: @andeszhang--> 6<!--Tester: @murphy1984--> 7<!--Adviser: @zhang_yixin13--> 8 9[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: 10 11- **onCreate()** 12 13 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. 14 15 > **NOTE** 16 > 17 > If an InputMethodExtensionAbility has been created, starting it again does not trigger the **onCreate()** callback. 18 19- **onDestroy()** 20 21 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. 22 23 24## How to Develop 25 26<!--RP1--> 27To implement an input method application, manually create an InputMethodExtensionAbility component in DevEco Studio. The procedure is as follows: 28 291. In the **ets** directory of the target module, right-click and choose **New** > **Directory** to create a directory named **InputMethodExtensionAbility**. 30 312. 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: 32 33``` 34/src/main/ 35├── ets/InputMethodExtensionAbility 36│ └──model/KeyboardController.ts # Shows the keyboard. 37│ └──InputMethodService.ts # Customizes a class that inherits from InputMethodExtensionAbility and add the required lifecycle callbacks. 38│ └──pages 39│ └── Index.ets # Draws the keyboard and adds the input and deletion features. 40│ └── KeyboardKeyData.ts # Defines keyboard attributes. 41├── resources/base/profile/main_pages.json 42``` 43<!--RP1End--> 44 45## Related Files 46 471. **InputMethodService.ts** file: 48 49 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. 50 51 ```ts 52 import { Want } from '@kit.AbilityKit'; 53 import keyboardController from './model/KeyboardController'; 54 import { InputMethodExtensionAbility } from '@kit.IMEKit'; 55 56 export default class InputDemoService extends InputMethodExtensionAbility { 57 58 onCreate(want: Want): void { 59 keyboardController.onCreate(this.context); // Initialize the window and register an event listener for the input method framework. 60 } 61 62 onDestroy(): void { 63 console.log("onDestroy."); 64 keyboardController.onDestroy(); // Destroy the window and deregister the event listener. 65 } 66 } 67 ``` 68 69<!--RP2--> 702. **KeyboardController.ts** file: 71 72 ```ts 73 import { display } from '@kit.ArkUI'; 74 import { inputMethodEngine, InputMethodExtensionContext } from '@kit.IMEKit'; 75 76 // Call the getInputMethodAbility API to obtain an instance, and then call the other APIs of the input method framework based on the instance. 77 const inputMethodAbility: inputMethodEngine.InputMethodAbility = inputMethodEngine.getInputMethodAbility(); 78 79 export class KeyboardController { 80 private mContext: InputMethodExtensionContext | undefined = undefined; // Save the context attribute in InputMethodExtensionAbility. 81 private panel: inputMethodEngine.Panel | undefined = undefined; 82 private textInputClient: inputMethodEngine.InputClient | undefined = undefined; 83 private keyboardController: inputMethodEngine.KeyboardController | undefined = undefined; 84 85 constructor() { 86 } 87 88 public onCreate(context: InputMethodExtensionContext): void 89 { 90 this.mContext = context; 91 this.initWindow(); // Initialize the window. 92 this.registerListener(); // Register an event listener for the input method framework. 93 } 94 95 public onDestroy(): void // Destroy the instance. 96 { 97 this.unRegisterListener(); // Deregister the event listener. 98 if(this.panel) { // Destroy the window. 99 inputMethodAbility.destroyPanel(this.panel); 100 } 101 if(this.mContext) { 102 this.mContext.destroy(); 103 } 104 } 105 106 public insertText(text: string): void { 107 if(this.textInputClient) { 108 this.textInputClient.insertText(text); 109 } 110 } 111 112 public deleteForward(length: number): void { 113 if(this.textInputClient) { 114 this.textInputClient.deleteForward(length); 115 } 116 } 117 118 private initWindow(): void // Initialize the window. 119 { 120 if(this.mContext === undefined) { 121 return; 122 } 123 let dis = display.getDefaultDisplaySync(); 124 let dWidth = dis.width; 125 let dHeight = dis.height; 126 let keyHeightRate = 0.47; 127 let keyHeight = dHeight * keyHeightRate; 128 let nonBarPosition = dHeight - keyHeight; 129 let panelInfo: inputMethodEngine.PanelInfo = { 130 type: inputMethodEngine.PanelType.SOFT_KEYBOARD, 131 flag: inputMethodEngine.PanelFlag.FLG_FIXED 132 }; 133 inputMethodAbility.createPanel(this.mContext, panelInfo).then(async (inputPanel: inputMethodEngine.Panel) => { 134 this.panel = inputPanel; 135 if(this.panel) { 136 await this.panel.resize(dWidth, keyHeight); 137 await this.panel.moveTo(0, nonBarPosition); 138 await this.panel.setUiContent('InputMethodExtensionAbility/pages/Index'); 139 } 140 }); 141 } 142 143 private registerListener(): void 144 { 145 this.registerInputListener(); // Register an event listener for the input method framework service. 146 // Register a listener for keyboard hiding. 147 } 148 149 private registerInputListener(): void { // Register a listener for the enabling and disabling events of the input method framework service. 150 inputMethodAbility.on('inputStart', (kbController, textInputClient) => { 151 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. 152 this.keyboardController = kbController; 153 }) 154 inputMethodAbility.on('inputStop', () => { 155 this.onDestroy (); // Destroy the KeyboardController instance. 156 }); 157 } 158 159 private unRegisterListener(): void 160 { 161 inputMethodAbility.off('inputStart'); 162 inputMethodAbility.off('inputStop', () => {}); 163 } 164 } 165 166 const keyboardController = new KeyboardController(); 167 168 export default keyboardController; 169 ``` 170<!--RP2End--> 1713. **KeyboardKeyData.ts** file: 172 173 In this file you can define the content displayed on the soft keyboard. 174 175 ```ts 176 export interface sourceListType { 177 content: string, 178 } 179 180 export let numberSourceListData: sourceListType[] = [ 181 { 182 content: '1' 183 }, 184 { 185 content: '2' 186 }, 187 { 188 content: '3' 189 }, 190 { 191 content: '4' 192 }, 193 { 194 content: '5' 195 }, 196 { 197 content: '6' 198 }, 199 { 200 content: '7' 201 }, 202 { 203 content: '8' 204 }, 205 { 206 content: '9' 207 }, 208 { 209 content: '0' 210 } 211 ] 212 ``` 213 2144. **Index.ets** file: 215 216 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. 217 218 Add the path to this file to the **src** field in the **resources/base/profile/main_pages.json** file. 219 220 ```ets 221 import { numberSourceListData, sourceListType } from './KeyboardKeyData'; 222 import keyboardController from '../model/KeyboardController'; 223 224 @Component 225 struct keyItem { 226 private keyValue: sourceListType = numberSourceListData[0]; 227 @State keyBgc: string = "#fff" 228 @State keyFontColor: string = "#000" 229 230 build() { 231 Column() { 232 Flex({ direction: FlexDirection.Column, 233 alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) { 234 Text(this.keyValue.content).fontSize(20).fontColor(this.keyFontColor) 235 } 236 } 237 .backgroundColor(this.keyBgc) 238 .borderRadius(6) 239 .width("8%") 240 .height("65%") 241 .onClick(() => { 242 keyboardController.insertText(this.keyValue.content); 243 }) 244 } 245 } 246 247 // Component used for deletion 248 @Component 249 export struct deleteItem { 250 @State keyBgc: string = "#fff" 251 @State keyFontColor: string = "#000" 252 253 build() { 254 Column() { 255 Flex({ direction: FlexDirection.Column, 256 alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) { 257 Text("Delete").fontSize(20).fontColor(this.keyFontColor) 258 } 259 } 260 .backgroundColor(this.keyBgc) 261 .width("13%") 262 .borderRadius(6) 263 .onClick(() => { 264 keyboardController.deleteForward(1); 265 }) 266 } 267 } 268 269 // Numeric keyboard 270 @Component 271 struct numberMenu { 272 private numberList: sourceListType[] = numberSourceListData; 273 274 build() { 275 Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.SpaceEvenly }) { 276 Flex({ justifyContent: FlexAlign.SpaceBetween }) { 277 ForEach(this.numberList, (item: sourceListType) => { // First row on the numeric keyboard 278 keyItem({ keyValue: item }) 279 }, (item: sourceListType) => item.content); 280 } 281 .padding({ top: "2%" }) 282 .width("96%") 283 .height("25%") 284 285 Flex({ justifyContent: FlexAlign.SpaceBetween }) { 286 deleteItem() 287 } 288 .width("96%") 289 .height("25%") 290 } 291 } 292 } 293 294 @Entry 295 @Component 296 struct Index { 297 private numberList: sourceListType[] = numberSourceListData 298 299 build() { 300 Stack() { 301 Flex({ 302 direction: FlexDirection.Column, 303 alignItems: ItemAlign.Center, 304 justifyContent: FlexAlign.End 305 }) { 306 Flex({ 307 direction: FlexDirection.Column, 308 alignItems: ItemAlign.Center, 309 justifyContent: FlexAlign.SpaceBetween 310 }) { 311 numberMenu({ 312 numberList: this.numberList 313 }) 314 } 315 .align(Alignment.End) 316 .width("100%") 317 .height("75%") 318 } 319 .height("100%").align(Alignment.End).backgroundColor("#cdd0d7") 320 } 321 .position({ x: 0, y: 0 }).zIndex(99999) 322 } 323 } 324 ``` 325 326<!--Del--> 3275. **module.json5** file:<br>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. 328 329 ```json 330 { 331 "module": { 332 ... 333 "extensionAbilities": [ 334 { 335 "description": "inputMethod", 336 "name": "InputMethodExtensionAbility", 337 "icon": "$media:app_icon", 338 "srcEntry": "./ets/InputMethodExtensionAbility/InputMethodService.ts", 339 "type": "inputMethod", 340 "exported": true 341 } 342 ] 343 } 344 } 345 ``` 346<!--DelEnd--> 347 348 349<!--RP3--> 350 351<!--RP3End--> 352 353## Constraints 354 355To protect the InputMethodExtensionAbility against abuse, functional constraints of the basic access mode are provided. 356 357> **NOTE** 358> 359> 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. 360