1/* 2 * Copyright (c) 2023 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16import commonEventManager from '@ohos.commonEventManager'; 17import display from '@ohos.display'; 18import inputMethodEngine from '@ohos.inputMethodEngine'; 19import InputMethodExtensionContext from '@ohos.InputMethodExtensionContext'; 20 21// Call the getInputMethodAbility method of the input method framework to get the instance 22const inputMethodAbility: inputMethodEngine.InputMethodAbility = inputMethodEngine.getInputMethodAbility(); 23const DEFAULT_DIRECTION: number = 1; 24const DEFAULT_LENGTH: number = 1; 25const DEFAULT_SELECT_RANGE: number = 10; 26 27enum TEST_RESULT_CODE { 28 SUCCESS, 29 FAILED 30} 31 32enum TEST_FUNCTION { 33 INSERT_TEXT_SYNC, 34 MOVE_CURSOR_SYNC, 35 GET_ATTRIBUTE_SYNC, 36 SELECT_BY_RANGE_SYNC, 37 SELECT_BY_MOVEMENT_SYNC, 38 GET_INDEX_AT_CURSOR_SYNC, 39 DELETE_FORWARD_SYNC, 40 DELETE_BACKWARD_SYNC, 41 GET_FORWARD_SYNC, 42 GET_BACKWARD_SYNC, 43 CHANGE_FLAG_TO_FIXED, 44 CHANGE_FLAG_TO_FLOATING, 45 SETPRIVACYMODE_WITHOUT_PERMISSION, 46 SETPRIVACYMODE_ERROR_PARAM, 47 ADJUST_WITH_INVALID_FLAG, 48 ADJUST_WITH_NON_FULL_SCREEN_NO_PANEL_RECT, 49 ADJUST_WITH_FULL_SCREEN_NO_AVOID_Y, 50 ADJUST_WITH_INVALID_AVOID_Y, 51 ADJUST_WITH_INVALID_TYPE, 52 ADJUST_SUCCESS, 53 SET_PREVIEW_TEXT, 54 FINISH_TEXT_PREVIEW, 55} 56 57export class KeyboardController { 58 private TAG: string = 'inputDemo: KeyboardController '; 59 private mContext: InputMethodExtensionContext | undefined = undefined; // save the context property of InputMethodExtensionAbility 60 private panel: inputMethodEngine.Panel | undefined = undefined; 61 private subscriber = undefined; 62 63 constructor() { 64 } 65 66 public onCreate(context: InputMethodExtensionContext): void { 67 this.mContext = context; 68 this.initWindow(); // init window 69 this.registerListener(); //register input listener 70 } 71 72 public onDestroy(): void { 73 this.unRegisterListener(); 74 if (this.panel) { 75 this.panel.hide(); 76 inputMethodAbility.destroyPanel(this.panel); 77 } 78 this.mContext.destroy(); 79 } 80 81 private initWindow(): void { 82 if (this.mContext === undefined) { 83 return; 84 } 85 let dis = display.getDefaultDisplaySync(); 86 let dWidth = dis.width; 87 let dHeight = dis.height; 88 let keyHeightRate = 0.47; 89 let keyHeight = dHeight * keyHeightRate; 90 let nonBarPosition = dHeight - keyHeight; 91 let panelInfo: inputMethodEngine.PanelInfo = { 92 type: inputMethodEngine.PanelType.SOFT_KEYBOARD, 93 flag: inputMethodEngine.PanelFlag.FLG_FIXED 94 }; 95 inputMethodAbility.createPanel(this.mContext, panelInfo).then(async (inputPanel: inputMethodEngine.Panel) => { 96 this.panel = inputPanel; 97 if (this.panel) { 98 await this.panel.resize(dWidth, keyHeight); 99 await this.panel.moveTo(0, nonBarPosition); 100 await this.panel.setUiContent('pages/Index'); 101 } 102 }); 103 } 104 105 publishCommonEvent(event: string, codeNumber: number): void { 106 this.addLog(`[inputDemo] publish event, event= ${event}, codeNumber= ${codeNumber}`); 107 commonEventManager.publish(event, { code: codeNumber }, (err) => { 108 if (err) { 109 this.addLog(`inputDemo publish ${event} failed, err = ${JSON.stringify(err)}`); 110 } else { 111 this.addLog(`inputDemo publish ${event} success`); 112 } 113 }) 114 } 115 116 private registerListener(): void { 117 this.registerInputListener(); 118 let subscribeInfo = { 119 events: ['syncTestFunction'] 120 } 121 commonEventManager.createSubscriber(subscribeInfo).then((data) => { 122 this.subscriber = data; 123 commonEventManager.subscribe(this.subscriber, (err, eventData) => { 124 this.addLog(`[inputDemo] subscribe, eventData.code = ${eventData.code}`); 125 if (globalThis.textInputClient === undefined) { 126 return; 127 } 128 switch (eventData.code) { 129 case TEST_FUNCTION.INSERT_TEXT_SYNC: 130 globalThis.textInputClient.insertTextSync("text"); 131 break; 132 case TEST_FUNCTION.MOVE_CURSOR_SYNC: 133 globalThis.textInputClient.moveCursorSync(DEFAULT_DIRECTION); 134 break; 135 case TEST_FUNCTION.GET_ATTRIBUTE_SYNC: 136 this.getAttributeSync(); 137 break; 138 case TEST_FUNCTION.SELECT_BY_RANGE_SYNC: 139 globalThis.textInputClient.selectByRangeSync({ start: 0, end: DEFAULT_SELECT_RANGE }); 140 break; 141 case TEST_FUNCTION.SELECT_BY_MOVEMENT_SYNC: 142 globalThis.textInputClient.selectByMovementSync({ direction: inputMethodEngine.CURSOR_LEFT }); 143 break; 144 case TEST_FUNCTION.GET_INDEX_AT_CURSOR_SYNC: 145 this.getIndexAtCursorSync() 146 break; 147 case TEST_FUNCTION.DELETE_FORWARD_SYNC: 148 globalThis.textInputClient.deleteForwardSync(DEFAULT_LENGTH); 149 break; 150 case TEST_FUNCTION.DELETE_BACKWARD_SYNC: 151 globalThis.textInputClient.deleteBackwardSync(DEFAULT_LENGTH); 152 break; 153 case TEST_FUNCTION.GET_FORWARD_SYNC: 154 this.getForwardSync(); 155 break; 156 case TEST_FUNCTION.GET_BACKWARD_SYNC: 157 this.getBackwardSync(); 158 break; 159 case TEST_FUNCTION.CHANGE_FLAG_TO_FIXED: 160 this.changePanelFlag(inputMethodEngine.PanelFlag.FLG_FIXED); 161 break; 162 case TEST_FUNCTION.CHANGE_FLAG_TO_FLOATING: 163 this.changePanelFlag(inputMethodEngine.PanelFlag.FLG_FLOATING); 164 break; 165 case TEST_FUNCTION.SETPRIVACYMODE_WITHOUT_PERMISSION: 166 this.setPrivacyModeWithoutPermission(); 167 break; 168 case TEST_FUNCTION.SETPRIVACYMODE_ERROR_PARAM: 169 this.setPrivacyModeErrorParam(); 170 break; 171 case TEST_FUNCTION.ADJUST_WITH_INVALID_FLAG: 172 this.adjustWithInvalidFlag(); 173 break; 174 case TEST_FUNCTION.ADJUST_WITH_NON_FULL_SCREEN_NO_PANEL_RECT: 175 this.adjustWithNonFullScreenNoPanelRect(); 176 break; 177 case TEST_FUNCTION.ADJUST_WITH_FULL_SCREEN_NO_AVOID_Y: 178 this.adjustWithFullScreenNoAvoidY(); 179 break; 180 case TEST_FUNCTION.ADJUST_WITH_INVALID_AVOID_Y: 181 this.adjustWithInvalidAvoidY(); 182 break; 183 case TEST_FUNCTION.ADJUST_WITH_INVALID_TYPE: 184 this.adjustWithInvalidPanelType(); 185 break; 186 case TEST_FUNCTION.ADJUST_SUCCESS: 187 this.adjustEnhancedPanelRect(); 188 break; 189 case TEST_FUNCTION.SET_PREVIEW_TEXT: 190 globalThis.textInputClient.setPreviewTextSync("text", { start: 0, end: DEFAULT_SELECT_RANGE }); 191 break; 192 case TEST_FUNCTION.FINISH_TEXT_PREVIEW: 193 globalThis.textInputClient.finishTextPreviewSync(); 194 break; 195 default: 196 break; 197 } 198 }) 199 }) 200 } 201 202 private registerInputListener(): void { // 注册对输入法框架服务的开启及停止事件监听 203 inputMethodAbility.on('inputStart', (kbController, textInputClient) => { 204 globalThis.textInputClient = textInputClient; // 此为输入法客户端实例,由此调用输入法框架提供给输入法应用的功能接口 205 globalThis.keyboardController = kbController; 206 }) 207 inputMethodAbility.on('inputStop', () => { 208 this.onDestroy(); 209 }); 210 } 211 212 private unRegisterListener(): void { 213 inputMethodAbility.off('inputStart'); 214 inputMethodAbility.off('inputStop', () => { 215 }); 216 } 217 218 getBackwardSync() { 219 let backward: string = globalThis.textInputClient.getBackwardSync(1); 220 if (backward.length >= 0) { 221 this.publishCommonEvent('getBackwardSyncResult', TEST_RESULT_CODE.SUCCESS); 222 } else { 223 this.publishCommonEvent('getBackwardSyncResult', TEST_RESULT_CODE.FAILED); 224 } 225 } 226 227 getForwardSync() { 228 let forward: string = globalThis.textInputClient.getForwardSync(1); 229 if (forward.length >= 0) { 230 this.publishCommonEvent('getForwardSyncResult', TEST_RESULT_CODE.SUCCESS); 231 } else { 232 this.publishCommonEvent('getForwardSyncResult', TEST_RESULT_CODE.FAILED); 233 } 234 } 235 236 async getAttributeSync() { 237 try { 238 let editAttribute = globalThis.textInputClient.getEditorAttributeSync(); 239 let editAttribute1 = await globalThis.textInputClient.getEditorAttribute(); 240 this.addLog(`[inputDemo] publish getEditorAttributeSync editAttribute= ${JSON.stringify(editAttribute)}`); 241 if(editAttribute.inputPattern == editAttribute1.inputPattern && editAttribute.enterKeyType == editAttribute1.enterKeyType) { 242 this.publishCommonEvent('getEditorAttributeSyncResult', TEST_RESULT_CODE.SUCCESS); 243 } else{ 244 this.publishCommonEvent('getEditorAttributeSyncResult', TEST_RESULT_CODE.FAILED); 245 } 246 } catch (err) { 247 this.publishCommonEvent('getEditorAttributeSyncResult', TEST_RESULT_CODE.FAILED); 248 } 249 } 250 251 getIndexAtCursorSync() { 252 try { 253 let index = globalThis.textInputClient.getTextIndexAtCursorSync(); 254 this.addLog(`[inputDemo] publish getTextIndexAtCursorSync index= ${index}`); 255 this.publishCommonEvent('getTextIndexAtCursorSyncResult', TEST_RESULT_CODE.SUCCESS); 256 } catch (err) { 257 this.publishCommonEvent('getTextIndexAtCursorSyncResult', TEST_RESULT_CODE.FAILED); 258 } 259 } 260 261 changePanelFlag(panelFlag: inputMethodEngine.PanelFlag) { 262 try { 263 this.panel.hide((err) => { 264 if (err) { 265 this.addLog(`Failed to hide panel: ${JSON.stringify(err)}`); 266 this.publishCommonEvent('changeFlag', TEST_RESULT_CODE.FAILED); 267 return; 268 } 269 this.panel.changeFlag(panelFlag); 270 this.panel.show((err) => { 271 this.addLog(`Failed to show panel: ${JSON.stringify(err)}`); 272 this.publishCommonEvent('changeFlag', TEST_RESULT_CODE.SUCCESS); 273 }); 274 }); 275 } catch (err) { 276 this.addLog(`failed: ${JSON.stringify(err)}`); 277 this.publishCommonEvent('changeFlag', TEST_RESULT_CODE.FAILED); 278 } 279 } 280 281 setPrivacyModeWithoutPermission() { 282 try { 283 let isSetPrivacyMode: boolean = true; 284 this.panel.setPrivacyMode(isSetPrivacyMode); 285 this.publishCommonEvent('setPrivacyModeWithoutPermissionResult', TEST_RESULT_CODE.FAILED); 286 } catch (err) { 287 this.addLog(`failed: ${JSON.stringify(err)}`); 288 if (err.code === 201) { 289 this.publishCommonEvent('setPrivacyModeWithoutPermissionResult', TEST_RESULT_CODE.SUCCESS); 290 return; 291 } 292 } 293 } 294 295 setPrivacyModeErrorParam() { 296 try { 297 this.panel.setPrivacyMode(undefined); 298 this.publishCommonEvent('setPrivacyModeErrorParamResult', TEST_RESULT_CODE.FAILED); 299 } catch (err) { 300 this.addLog(`failed: ${JSON.stringify(err)}`); 301 if (err.code === 401) { 302 this.publishCommonEvent('setPrivacyModeErrorParamResult', TEST_RESULT_CODE.SUCCESS); 303 return; 304 } 305 } 306 } 307 308 adjustWithInvalidFlag() { 309 try { 310 let defaultDisplay = display.getDefaultDisplaySync(); 311 let displayWidth = defaultDisplay.width; 312 let displayHeight = defaultDisplay.height; 313 let heightRate = 0.7; 314 let portraitAvoidY = 0; 315 let landscapeAvoidY = 0; 316 if (displayWidth < displayHeight) { 317 portraitAvoidY = displayHeight - (displayHeight * heightRate - 1); 318 landscapeAvoidY = displayWidth - (displayWidth * heightRate - 1); 319 } else { 320 portraitAvoidY = displayWidth - (displayWidth * heightRate - 1); 321 landscapeAvoidY = displayHeight - (displayHeight * heightRate - 1); 322 } 323 let panelRect: inputMethodEngine.EnhancedPanelRect = { 324 landscapeAvoidY: landscapeAvoidY, 325 portraitAvoidY: portraitAvoidY, 326 fullScreenMode: true 327 } 328 this.panel.adjustPanelRect(2, panelRect); 329 this.publishCommonEvent('adjustWithInvalidFlagResult', TEST_RESULT_CODE.FAILED); 330 } catch (err) { 331 this.addLog(`failed: ${JSON.stringify(err)}`); 332 if (err.code === 12800017) { 333 this.publishCommonEvent('adjustWithInvalidFlagResult', TEST_RESULT_CODE.SUCCESS); 334 return; 335 } 336 } 337 } 338 339 adjustWithNonFullScreenNoPanelRect() { 340 try { 341 let panelRect: inputMethodEngine.EnhancedPanelRect = { 342 fullScreenMode: false 343 } 344 this.panel.adjustPanelRect(inputMethodEngine.PanelFlag.FLG_FIXED, panelRect); 345 this.publishCommonEvent('adjustWithNonFullScreenNoPanelRectResult', TEST_RESULT_CODE.FAILED); 346 } catch (err) { 347 this.addLog(`failed: ${JSON.stringify(err)}`); 348 if (err.code === 401) { 349 this.publishCommonEvent('adjustWithNonFullScreenNoPanelRectResult', TEST_RESULT_CODE.SUCCESS); 350 return; 351 } 352 } 353 } 354 355 adjustWithFullScreenNoAvoidY() { 356 try { 357 let panelRect: inputMethodEngine.EnhancedPanelRect = { 358 fullScreenMode: true 359 } 360 this.panel.adjustPanelRect(inputMethodEngine.PanelFlag.FLG_FIXED, panelRect); 361 this.publishCommonEvent('adjustWithFullScreenNoAvoidYResult', TEST_RESULT_CODE.FAILED); 362 } catch (err) { 363 this.addLog(`failed: ${JSON.stringify(err)}`); 364 if (err.code === 401) { 365 this.publishCommonEvent('adjustWithFullScreenNoAvoidYResult', TEST_RESULT_CODE.SUCCESS); 366 return; 367 } 368 } 369 } 370 371 adjustWithInvalidAvoidY() { 372 try { 373 let defaultDisplay = display.getDefaultDisplaySync(); 374 let displayWidth = defaultDisplay.width; 375 let displayHeight = defaultDisplay.height; 376 let heightRate = 0.7; 377 let portraitAvoidY = 0; 378 let landscapeAvoidY = 0; 379 if (displayWidth < displayHeight) { 380 portraitAvoidY = displayHeight - (displayHeight * heightRate + 1); 381 landscapeAvoidY = displayWidth - (displayWidth * heightRate + 1); 382 } else { 383 portraitAvoidY = displayWidth - (displayWidth * heightRate + 1); 384 landscapeAvoidY = displayHeight - (displayHeight * heightRate + 1); 385 } 386 let panelRect: inputMethodEngine.EnhancedPanelRect = { 387 landscapeAvoidY: landscapeAvoidY, 388 portraitAvoidY: portraitAvoidY, 389 fullScreenMode: true 390 } 391 this.panel.adjustPanelRect(inputMethodEngine.PanelFlag.FLG_FIXED, panelRect); 392 this.publishCommonEvent('adjustWithInvalidAvoidYResult', TEST_RESULT_CODE.FAILED); 393 } catch (err) { 394 this.addLog(`failed: ${JSON.stringify(err)}`); 395 if (err.code === 401) { 396 this.publishCommonEvent('adjustWithInvalidAvoidYResult', TEST_RESULT_CODE.SUCCESS); 397 return; 398 } 399 } 400 } 401 402 adjustWithInvalidPanelType() { 403 let panelInfo: inputMethodEngine.PanelInfo = { 404 type: inputMethodEngine.PanelType.STATUS_BAR 405 }; 406 inputMethodAbility.createPanel(this.mContext, panelInfo).then(async (inputPanel: inputMethodEngine.Panel) => { 407 try { 408 let defaultDisplay = display.getDefaultDisplaySync(); 409 let displayWidth = defaultDisplay.width; 410 let displayHeight = defaultDisplay.height; 411 let heightRate = 0.7; 412 let portraitAvoidY = 0; 413 let landscapeAvoidY = 0; 414 if (displayWidth < displayHeight) { 415 portraitAvoidY = displayHeight - (displayHeight * heightRate - 1); 416 landscapeAvoidY = displayWidth - (displayWidth * heightRate - 1); 417 } else { 418 portraitAvoidY = displayWidth - (displayWidth * heightRate - 1); 419 landscapeAvoidY = displayHeight - (displayHeight * heightRate - 1); 420 } 421 let panelRect: inputMethodEngine.EnhancedPanelRect = { 422 landscapeAvoidY: landscapeAvoidY, 423 portraitAvoidY: portraitAvoidY, 424 fullScreenMode: true 425 } 426 inputPanel.adjustPanelRect(inputMethodEngine.PanelFlag.FLG_FIXED, panelRect); 427 this.addLog('adjustPanelRect success'); 428 this.publishCommonEvent('adjustWithInvalidTypeResult', TEST_RESULT_CODE.FAILED); 429 inputMethodAbility.destroyPanel(inputPanel); 430 } catch (err) { 431 this.addLog(`failed: ${JSON.stringify(err)}`); 432 inputMethodAbility.destroyPanel(inputPanel); 433 if (err.code === 12800017) { 434 this.publishCommonEvent('adjustWithInvalidTypeResult', TEST_RESULT_CODE.SUCCESS); 435 return; 436 } 437 } 438 }); 439 } 440 441 adjustEnhancedPanelRect() { 442 try { 443 let defaultDisplay = display.getDefaultDisplaySync(); 444 let displayWidth = defaultDisplay.width; 445 let displayHeight = defaultDisplay.height; 446 let heightRate = 0.7; 447 let portraitAvoidY = 0; 448 let landscapeAvoidY = 0; 449 if (displayWidth < displayHeight) { 450 portraitAvoidY = displayHeight - (displayHeight * heightRate - 1); 451 landscapeAvoidY = displayWidth - (displayWidth * heightRate - 1); 452 } else { 453 portraitAvoidY = displayWidth - (displayWidth * heightRate - 1); 454 landscapeAvoidY = displayHeight - (displayHeight * heightRate - 1); 455 } 456 let panelRect: inputMethodEngine.EnhancedPanelRect = { 457 landscapeAvoidY: landscapeAvoidY, 458 portraitAvoidY: portraitAvoidY, 459 fullScreenMode: true 460 } 461 this.panel.adjustPanelRect(inputMethodEngine.PanelFlag.FLG_FIXED, panelRect); 462 this.addLog('adjustPanelRect success'); 463 this.publishCommonEvent('adjustSuccessResult', TEST_RESULT_CODE.SUCCESS); 464 } catch (err) { 465 this.addLog(`failed: ${JSON.stringify(err)}`); 466 this.publishCommonEvent('adjustSuccessResult', TEST_RESULT_CODE.FAILED); 467 return; 468 } 469 } 470 471 private addLog(message): void { 472 console.log(this.TAG + message) 473 } 474} 475 476const keyboardController = new KeyboardController(); 477 478export default keyboardController;