• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// @ts-nocheck
2/*
3 * Copyright (c) 2022 Huawei Device Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16import { GetHardKeyValue } from './HardKeyUtils.ets'
17import inputMethodEngine from '@ohos.inputMethodEngine'
18import settings from '@ohos.settings';
19import display from '@ohos.display'
20import windowManager from '@ohos.window'
21import featureAbility from '@ohos.ability.featureAbility'
22import { KeyCode } from '@ohos.multimodalInput.keyCode';
23import Log from './Log'
24
25let InputMethodEngine = inputMethodEngine.getInputMethodAbility();
26let isDebug = false;
27let TAG: string = 'KeyboardController->';
28export class KeyboardController {
29  private readonly uri = 'dataability:///com.ohos.settingsdata.DataAbility'
30  private helper: any = null
31  private getValueRes: string = "1"
32  private nonBarPosition: number = 0
33  private barPosition: number = 0
34  private keyCodes: Array<number> = []
35  mContext
36  WINDOW_TYPE_INPUT_METHOD_FLOAT= 2105
37  windowName = 'imeWindow';
38  isSpecialKeyPress = false;
39  keySum = 0;
40  isKeyboardShow = false;
41  inputHandle= InputHandler.getInstance();
42  mKeyboardDelegate;
43  constructor(context) {
44    this.inputHandle.addLog('constructor')
45    this.mContext = context;
46    this.helper = featureAbility.acquireDataAbilityHelper(this.mContext, this.uri)
47  }
48
49  public getValue(settingDataKey: string) {
50    this.getValueRes = settings.getValueSync(this.helper, settingDataKey, "1")
51  }
52
53  public onCreate(): void {
54    this.inputHandle.addLog('onCreate')
55    this.initWindow();
56    this.registerListener()
57  }
58
59  public onDestroy(): void {
60    this.inputHandle.addLog('onDestroy')
61    this.unRegisterListener();
62    this.destroyWindow();
63  }
64
65  private initWindow(): void {
66    this.inputHandle.addLog('initWindow')
67    display.getDefaultDisplay().then(dis => {
68      this.inputHandle.addLog("initWindow-oncall display");
69      var dWidth = dis.width;
70      var dHeight = dis.height;
71      var navigationbar_height = 75;
72      var keyHeightRate = 0.43;
73      AppStorage.SetOrCreate<number>('windowWidth', dis.width)
74      AppStorage.SetOrCreate<number>('windowHeight', dis.height)
75      if (dis.width > dis.height) {
76        AppStorage.SetOrCreate('isLandscape', true)
77      } else {
78        AppStorage.SetOrCreate('isLandscape', false)
79      }
80      if (dWidth == 1080 && dHeight == 2376) {
81        navigationbar_height = 105
82        keyHeightRate = 35 / 99
83      } else if(dWidth == 720 && dHeight == 1280) {
84        navigationbar_height = 72
85        AppStorage.SetOrCreate('isRkDevice', true)
86      } else if (dWidth == 2376 && dHeight == 1080) {
87        navigationbar_height = 90
88        keyHeightRate = 0.63
89      } else if (dWidth == 2560 && dHeight == 1600) {
90        navigationbar_height = 88
91        keyHeightRate = 0.5
92      } else if (dWidth == 1600 && dHeight == 2560) {
93        navigationbar_height = 88
94        keyHeightRate = 0.34
95      }
96      var keyHeight = dHeight * keyHeightRate;
97      this.barPosition = dHeight - keyHeight - navigationbar_height
98      this.nonBarPosition = dHeight - keyHeight
99      this.inputHandle.addLog("initWindow-dWidth = " + dWidth + ";dHeight = " + dHeight + ";keyboard height = " + keyHeight + ";navibar height = " + navigationbar_height);
100
101      this.inputHandle.addLog(typeof (this.mContext))
102      this.inputHandle.addLog('initWindow-window = ' + typeof (windowManager))
103      windowManager.create(this.mContext, this.windowName, this.WINDOW_TYPE_INPUT_METHOD_FLOAT).then((win) => {
104        win.resetSize(dWidth, keyHeight).then(() => {
105          win.moveTo(0, this.barPosition).then(() => {
106            win.loadContent('pages/index').then(() => {
107              this.inputHandle.addLog('loadContent finished')
108            })
109          })
110        })
111      })
112    });
113  }
114
115  private destroyWindow(): void {
116    this.inputHandle.addLog('destroyWindow');
117    try {
118      let win = windowManager.findWindow(this.windowName);
119      win.destroyWindow((err) => {
120        if (err) {
121          this.inputHandle.addLog('Failed to destroy the window. Cause:' + JSON.stringify(err));
122          return;
123        }
124      });
125    } catch (exception) {
126      this.inputHandle.addLog('Failed to find the Window. Cause: ' + JSON.stringify(exception));
127    }
128  }
129
130  private resizeWindow() {
131    this.inputHandle.addLog('resizeWindow');
132
133    display.getDefaultDisplay().then(dis => {
134      this.inputHandle.addLog("resizeWindow-oncall display");
135
136      var dWidth = dis.width;
137      var dHeight = dis.height;
138      var navigationbar_height = dHeight * 0.07; //todo:有些产品导航栏高度为0,默认为0.07
139      var keyHeightRate = 0.47;
140      AppStorage.SetOrCreate<number>('windowWidth', dis.width)
141      AppStorage.SetOrCreate<number>('windowHeight', dis.height)
142      if (dis.width > dis.height) {
143        AppStorage.Set('isLandscape', true)
144      } else {
145        AppStorage.Set('isLandscape', false)
146      }
147      if (dWidth == 1080 && dHeight == 2376) {
148        navigationbar_height = 105
149        keyHeightRate = 35 / 99
150      } else if(dWidth == 720 && dHeight == 1280) {
151        navigationbar_height = 72
152      } else if (dWidth == 2376 && dHeight == 1080) {
153        navigationbar_height = 90
154        keyHeightRate = 0.63
155      } else if (dWidth == 2560 && dHeight == 1600) {
156        navigationbar_height = 88
157        keyHeightRate = 0.5
158      } else if (dWidth == 1600 && dHeight == 2560) {
159        navigationbar_height = 88
160        keyHeightRate = 0.34
161      }
162      var keyHeight = dHeight * keyHeightRate;
163      this.inputHandle.addLog("resizeWindow-dWidth = " + dWidth + ";dHeight = " + dHeight + ";keyboard height = " + keyHeight + ";navibar height = " + navigationbar_height);
164
165      windowManager.find(this.windowName).then((win) => {
166        win.resetSize(dWidth, keyHeight).then(() => {
167          win.moveTo(0, dHeight - keyHeight - navigationbar_height).then(() => {
168            this.inputHandle.addLog('resizeWindow-moveTo success');
169          })
170
171        })
172      })
173    });
174  }
175
176  private registerListener(): void {
177    this.inputHandle.addLog('registerListener')
178
179    display.on('change', (screenEvent) => {
180      this.inputHandle.addLog('screenChangeEvent');
181      this.resizeWindow()
182    });
183    InputMethodEngine.on('inputStart', (kbController, textInputClient) => {
184      this.inputHandle.addLog('keyboard inputStart');
185      this.inputHandle.onInputStart(kbController, textInputClient);
186    })
187    InputMethodEngine.on('inputStop', () => {
188      this.inputHandle.addLog('keyboard inputStop');
189        this.mContext.destroy();
190    });
191    InputMethodEngine.on('keyboardShow', () => {
192      this.inputHandle.addLog('keyboard show');
193      this.showWindow()
194    });
195
196    InputMethodEngine.on('keyboardHide', () => {
197      this.inputHandle.addLog('keyboard hide');
198      this.hideWindow();
199    });
200
201    this.mKeyboardDelegate = inputMethodEngine.getKeyboardDelegate();
202
203    this.mKeyboardDelegate.on('keyDown', (keyEvent) => {
204      if (this.isKeyboardShow) {
205        this.inputHandle.hideKeyboardSelf();
206      }
207      this.inputHandle.addLog('keyDown: code = ' + keyEvent.keyCode);
208      let result = this.onKeyDown(keyEvent);
209      this.inputHandle.addLog('keyDown: result = ' + result);
210      return result
211    });
212
213    this.mKeyboardDelegate.on('keyUp', (keyEvent) => {
214      this.inputHandle.addLog('keyUp: code = ' + keyEvent.keyCode);
215      let result = this.onKeyUp(keyEvent);
216      this.inputHandle.addLog('keyUp: result = ' + result);
217      return result
218    });
219
220    if (isDebug) {
221      this.mKeyboardDelegate.on('cursorContextChange', (x, y, height) => {
222        this.inputHandle.setCursorInfo('cursorInfo:(' + x + ',' + y + '), h = ' + height);
223      });
224
225      this.mKeyboardDelegate.on('selectionChange', (oldBegin, oldEnd, newBegin, newEnd) => {
226        this.inputHandle.setSelectInfo('selectInfo: from(' + oldBegin + ',' + oldEnd + ') to (' + newBegin + ',' + newEnd + ')');
227      });
228
229      this.mKeyboardDelegate.on('textChange', (text) => {
230        this.inputHandle.setTextInfo('textInfo: ' + text);
231      });
232    }
233  }
234
235  public isShiftKeyHold(): boolean {
236    if (this.keyCodes.length === 0) {
237      return false
238    }
239    let preDownKey = this.keyCodes[0]
240    return preDownKey === KeyCode.KEYCODE_SHIFT_LEFT || preDownKey === KeyCode.KEYCODE_SHIFT_RIGHT
241  }
242
243  public onKeyDown(keyEvent): boolean {
244    this.inputHandle.addLog('onKeyDown: code = ' + keyEvent.keyCode);
245    var keyCode = keyEvent.keyCode
246    let idx = this.keyCodes.indexOf(keyCode)
247    if (idx == -1) {
248      this.keyCodes.push(keyCode)
249    } else {
250      this.inputHandle.addLog("keyCode down is intercepted: " + keyCode);
251    }
252    this.inputHandle.addLog('onKeyDown: this.keyCodes = ' + JSON.stringify(this.keyCodes));
253    if (this.isShiftKeyHold() && this.keyCodes.length == 2 && !this.isKeyCodeAZ(keyCode)) {
254      this.isSpecialKeyPress = true;
255      return false
256    }
257    if (this.isSpecialKeyPress || keyCode === KeyCode.KEYCODE_ALT_LEFT || keyCode === KeyCode.KEYCODE_ALT_RIGHT) {
258      return false
259    }
260    var keyValue = GetHardKeyValue(keyCode, this.isShiftKeyHold());
261    if (!keyValue) {
262      this.inputHandle.addLog('onKeyDown: unknown keyCode' );
263      this.isSpecialKeyPress = true;
264      return false;
265    }
266    try {
267      return this.inputHardKeyCode(keyValue, keyCode)
268    } catch (ex) {
269      //方向键api需要优化
270      this.inputHandle.addLog("inputHardKeyCode error: " + ex);
271      return false
272    }
273  }
274
275  public onKeyUp(keyEvent): boolean {
276    this.inputHandle.addLog('OnKeyUp: code = ' + keyEvent.keyCode);
277    var keyCode = keyEvent.keyCode;
278    let idx = this.keyCodes.indexOf(keyCode)
279    if (idx != -1) {
280      this.keyCodes.splice(idx, 1)
281    } else {
282      this.inputHandle.addLog("keyCode KeyUp is intercepted: " + keyCode);
283    }
284    this.inputHandle.addLog('OnKeyUp: this.keyCodes = ' + JSON.stringify(this.keyCodes));
285
286    // For KEYCODE_DEL/KEYCODE_FORWARD_DEL, processed in OnKeyDown, so just intercept it
287    if (keyCode == 2055 || keyCode == 2071 || (keyCode >= 2012 && keyCode <= 2016)) {
288      this.inputHandle.addLog('special code: ' + keyCode);
289      return true
290    }
291
292    if (this.isSpecialKeyPress) {
293      var keyValue = GetHardKeyValue(keyCode, this.isShiftKeyHold())
294      if (!keyValue) {
295        this.isSpecialKeyPress = true;
296      }
297      if (this.keyCodes.length == 0) {
298        this.isSpecialKeyPress = false;
299      }
300      this.inputHandle.addLog('OnKeyUp: this.isSpecialKeyPress: ' + this.isSpecialKeyPress);
301      return false
302    }
303    return true;
304  }
305
306  public isKeyCodeAZ(keyCode: number) {
307    return keyCode >= KeyCode.KEYCODE_A && keyCode <= KeyCode.KEYCODE_Z
308  }
309
310  public isKeyCodeNumber(keyCode: number) {
311    return (keyCode >= KeyCode.KEYCODE_0 && keyCode <= KeyCode.KEYCODE_9) || (keyCode >= KeyCode.KEYCODE_NUMPAD_0 && keyCode <= KeyCode.KEYCODE_NUMPAD_9)
312  }
313
314  public inputHardKeyCode(keyValue: string, keyCode: number): boolean {
315    this.inputHandle.addLog("keyValue is: " + keyValue);
316    if (this.processFunctionKeys(keyValue)) {
317      return true
318    }
319    if (this.shiftKeys(keyValue)) {
320      return false
321    }
322    this.inputHandle.insertText(keyValue);
323    return true
324  }
325
326  public shiftKeys(keyValue: string): boolean {
327    this.inputHandle.addLog("processFunctionKeys keyValue is: " + keyValue);
328    switch (keyValue) {
329      case 'KEYCODE_SHIFT_LEFT':
330      case 'KEYCODE_SHIFT_RIGHT':
331        return true
332      default:
333        return false
334    }
335  }
336
337  public processFunctionKeys(keyValue: string): boolean {
338    this.inputHandle.addLog("processFunctionKeys keyValue is: " + keyValue);
339    switch (keyValue) {
340      case "KEYCODE_DEL":
341        this.inputHandle.deleteBackward(1);
342        return true
343      case "KEYCODE_FORWARD_DEL":
344        this.inputHandle.deleteForward(1);
345        return true
346      case "KEYCODE_DPAD_UP":
347        inputMethodEngine.MoveCursor(inputMethodEngine.CURSOR_UP);
348        return true
349      case "KEYCODE_DPAD_DOWN":
350        inputMethodEngine.MoveCursor(inputMethodEngine.CURSOR_DOWN);
351        return true
352      case "KEYCODE_DPAD_LEFT":
353        inputMethodEngine.MoveCursor(inputMethodEngine.CURSOR_LEFT);
354        return true
355      case "KEYCODE_DPAD_RIGHT":
356        inputMethodEngine.MoveCursor(inputMethodEngine.CURSOR_RIGHT);
357        return true
358      default:
359        return false
360    }
361  }
362
363  private unRegisterListener(): void{
364    this.inputHandle.addLog('unRegisterListener')
365
366    InputMethodEngine.off('inputStop', ()=> {
367      this.inputHandle.addLog('inputStop off');
368    });
369
370    InputMethodEngine.off('keyboardShow');
371
372    InputMethodEngine.off('keyboardHide');
373
374    this.mKeyboardDelegate.off('keyDown');
375
376    this.mKeyboardDelegate.off('keyUp');
377
378    if (isDebug) {
379      this.mKeyboardDelegate.off('cursorContextChange');
380
381      this.mKeyboardDelegate.off('selectionChange');
382
383      this.mKeyboardDelegate.off('textChange');
384    }
385  }
386
387  private showWindow() {
388    try {
389      this.getValue('settings.display.navigationbar_status')
390    } catch (err) {
391      Log.showError(TAG, "get value failed" + err)
392    }
393    Log.showInfo(TAG, 'current navigation state is' + this.getValueRes)
394    this.inputHandle.addLog('showWindow');
395    windowManager.find(this.windowName).then((win) => {
396      win.show().then(() => {
397        this.inputHandle.addLog('showWindow finish');
398        this.isKeyboardShow = true;
399      })
400    })
401  }
402
403  private hideWindow() {
404    this.inputHandle.addLog('hideWindow');
405    windowManager.find(this.windowName).then((win) => {
406      win.hide().then(() => {
407        this.isKeyboardShow = false;
408        this.inputHandle.addLog('hideWindow finish');
409      })
410    })
411  }
412}
413
414export class InputHandler {
415  private static instance: InputHandler;
416  private mKbController;
417  private mTextInputClient;
418  private mEditorAttribute;
419  private cursorInfo;
420  private selectInfo;
421  private textInfo;
422  private inputInfo;
423  private constructor() {
424
425  }
426
427  public static getInstance() {
428    if (globalThis.instance == null) {
429      globalThis.instance = new InputHandler();
430    }
431    return globalThis.instance;
432  }
433
434  public onInputStart(kbController, textInputClient) {
435    globalThis.mKbController = kbController;
436    globalThis.mTextInputClient = textInputClient;
437    globalThis.mEditorAttribute = globalThis.mTextInputClient.getEditorAttribute();
438    this.addLog("onInputStart mEditorAttribute = " + globalThis.mEditorAttribute);
439    globalThis.mEditorAttribute.then(res => {
440      globalThis.enterKeyType = res.enterKeyType;
441      globalThis.inputPattern = res.inputPattern;
442      this.setInputInfo("EditorInfo:enterKeyType = " + globalThis.enterKeyType + ";inputPattern = " + globalThis.inputPattern);
443    })
444  }
445
446  public hideKeyboardSelf() {
447    this.addLog('hideKeyboardSelf')
448    if (globalThis.mKbController != undefined) {
449      globalThis.mKbController.hideKeyboard();
450    } else {
451      this.addLog('hideKeyboardSelf globalThis.mKbController is undefined')
452    }
453  }
454
455  public sendKeyFunction() {
456    this.addLog('sendKeyFunction')
457    if (globalThis.mTextInputClient != undefined) {
458      globalThis.mTextInputClient.sendKeyFunction(globalThis.enterKeyType);
459    } else {
460      this.addLog('sendKeyFunction globalThis.mTextInputClient is undefined')
461    }
462  }
463
464  public deleteBackward(length: number) {
465    this.addLog('deleteBackward')
466    if (globalThis.mTextInputClient != undefined) {
467      globalThis.mTextInputClient.deleteBackward(length);
468    } else {
469      this.addLog('deleteBackward globalThis.mTextInputClient is undefined')
470    }
471    if (isDebug) {
472      this.refreshInfo();
473    }
474  }
475
476  public deleteForward(length: number) {
477    this.addLog('deleteForward')
478    if (globalThis.mTextInputClient != undefined) {
479      globalThis.mTextInputClient.deleteForward(length);
480    } else {
481      this.addLog('deleteForward globalThis.mTextInputClient is undefined')
482    }
483    if (isDebug) {
484      this.refreshInfo();
485    }
486  }
487
488  public insertText(text: string) {
489    this.addLog('insertText')
490    if (globalThis.mTextInputClient != undefined) {
491      globalThis.mTextInputClient.insertText(text);
492    } else {
493      this.addLog('insertText globalThis.mTextInputClient is undefined')
494    }
495    if (isDebug) {
496      this.refreshInfo();
497    }
498  }
499
500  public addLog(message): void {
501    Log.showInfo(TAG, "kikaInput-js: " + message)
502  }
503
504  public setCursorInfo(info) {
505    globalThis.cursorInfo = info;
506    this.addLog("setCursorInfo info = " + info);
507  }
508
509  public setSelectInfo(info) {
510    globalThis.selectInfo = info;
511    this.addLog("setSelectInfo info = " + info);
512  }
513
514  public setTextInfo(info) {
515    globalThis.textInfo = info;
516    this.addLog("setTextInfo info = " + info);
517  }
518
519  public setInputInfo(info) {
520    globalThis.inputInfo = info;
521    this.addLog("setInputInfo info = " + info);
522  }
523
524  public refreshInfo() {
525    this.addLog('refreshInfo');
526    var showText = '';
527    this.addLog('refreshInfo1');
528    showText += 'five chars: before:[' + globalThis.mTextInputClient.getForward(5) + '];after:[' + globalThis.mTextInputClient.getBackward(5) + ']\n';
529    this.addLog('refreshInfo2');
530    showText += globalThis.cursorInfo + "\n";
531    this.addLog('refreshInfo3');
532    showText += globalThis.selectInfo + "\n";
533    this.addLog('refreshInfo4');
534    showText += globalThis.textInfo + "\n";
535    this.addLog('refreshInfo5');
536    showText += globalThis.inputInfo;
537    this.addLog('refreshInfo6');
538    AppStorage.Set<string>('showLog', showText);
539    this.addLog('refreshInfo7');
540    this.addLog('result is = ' + showText);
541  }
542}
543