• 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.mContext.terminateSelf();
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 resizeWindow() {
116    this.inputHandle.addLog('resizeWindow');
117
118    display.getDefaultDisplay().then(dis => {
119      this.inputHandle.addLog("resizeWindow-oncall display");
120
121      var dWidth = dis.width;
122      var dHeight = dis.height;
123      var navigationbar_height = dHeight * 0.07; //todo:有些产品导航栏高度为0,默认为0.07
124      var keyHeightRate = 0.47;
125      AppStorage.SetOrCreate<number>('windowWidth', dis.width)
126      AppStorage.SetOrCreate<number>('windowHeight', dis.height)
127      if (dis.width > dis.height) {
128        AppStorage.Set('isLandscape', true)
129      } else {
130        AppStorage.Set('isLandscape', false)
131      }
132      if (dWidth == 1080 && dHeight == 2376) {
133        navigationbar_height = 105
134        keyHeightRate = 35 / 99
135      } else if(dWidth == 720 && dHeight == 1280) {
136        navigationbar_height = 72
137      } else if (dWidth == 2376 && dHeight == 1080) {
138        navigationbar_height = 90
139        keyHeightRate = 0.63
140      } else if (dWidth == 2560 && dHeight == 1600) {
141        navigationbar_height = 88
142        keyHeightRate = 0.5
143      } else if (dWidth == 1600 && dHeight == 2560) {
144        navigationbar_height = 88
145        keyHeightRate = 0.34
146      }
147      var keyHeight = dHeight * keyHeightRate;
148      this.inputHandle.addLog("resizeWindow-dWidth = " + dWidth + ";dHeight = " + dHeight + ";keyboard height = " + keyHeight + ";navibar height = " + navigationbar_height);
149
150      windowManager.find(this.windowName).then((win) => {
151        win.resetSize(dWidth, keyHeight).then(() => {
152          win.moveTo(0, dHeight - keyHeight - navigationbar_height).then(() => {
153            this.inputHandle.addLog('resizeWindow-moveTo success');
154          })
155
156        })
157      })
158    });
159  }
160
161  private registerListener(): void {
162    this.inputHandle.addLog('registerListener')
163
164    display.on('change', (screenEvent) => {
165      this.inputHandle.addLog('screenChangeEvent');
166      this.resizeWindow()
167    });
168    InputMethodEngine.on('inputStart', (kbController, textInputClient) => {
169      this.inputHandle.addLog('keyboard inputStart');
170      this.inputHandle.onInputStart(kbController, textInputClient);
171    })
172    InputMethodEngine.on('inputStop', () => {
173      this.inputHandle.addLog('keyboard inputStop');
174        this.onDestroy();
175    });
176    InputMethodEngine.on('keyboardShow', () => {
177      this.inputHandle.addLog('keyboard show');
178      this.showWindow()
179    });
180
181    InputMethodEngine.on('keyboardHide', () => {
182      this.inputHandle.addLog('keyboard hide');
183      this.hideWindow();
184    });
185
186    this.mKeyboardDelegate = inputMethodEngine.getKeyboardDelegate();
187
188    this.mKeyboardDelegate.on('keyDown', (keyEvent) => {
189      if (this.isKeyboardShow) {
190        this.inputHandle.hideKeyboardSelf();
191      }
192      this.inputHandle.addLog('keyDown: code = ' + keyEvent.keyCode);
193      let result = this.onKeyDown(keyEvent);
194      this.inputHandle.addLog('keyDown: result = ' + result);
195      return result
196    });
197
198    this.mKeyboardDelegate.on('keyUp', (keyEvent) => {
199      this.inputHandle.addLog('keyUp: code = ' + keyEvent.keyCode);
200      let result = this.onKeyUp(keyEvent);
201      this.inputHandle.addLog('keyUp: result = ' + result);
202      return result
203    });
204
205    if (isDebug) {
206      this.mKeyboardDelegate.on('cursorContextChange', (x, y, height) => {
207        this.inputHandle.setCursorInfo('cursorInfo:(' + x + ',' + y + '), h = ' + height);
208      });
209
210      this.mKeyboardDelegate.on('selectionChange', (oldBegin, oldEnd, newBegin, newEnd) => {
211        this.inputHandle.setSelectInfo('selectInfo: from(' + oldBegin + ',' + oldEnd + ') to (' + newBegin + ',' + newEnd + ')');
212      });
213
214      this.mKeyboardDelegate.on('textChange', (text) => {
215        this.inputHandle.setTextInfo('textInfo: ' + text);
216      });
217    }
218  }
219
220  public isShiftKeyHold(): boolean {
221    if (this.keyCodes.length === 0) {
222      return false
223    }
224    let preDownKey = this.keyCodes[0]
225    return preDownKey === KeyCode.KEYCODE_SHIFT_LEFT || preDownKey === KeyCode.KEYCODE_SHIFT_RIGHT
226  }
227
228  public onKeyDown(keyEvent): boolean {
229    this.inputHandle.addLog('onKeyDown: code = ' + keyEvent.keyCode);
230    var keyCode = keyEvent.keyCode
231    let idx = this.keyCodes.indexOf(keyCode)
232    if (idx == -1) {
233      this.keyCodes.push(keyCode)
234    } else {
235      this.inputHandle.addLog("keyCode down is intercepted: " + keyCode);
236    }
237    this.inputHandle.addLog('onKeyDown: this.keyCodes = ' + JSON.stringify(this.keyCodes));
238    if (this.isShiftKeyHold() && this.keyCodes.length == 2 && !this.isKeyCodeAZ(keyCode)) {
239      this.isSpecialKeyPress = true;
240      return false
241    }
242    if (this.isSpecialKeyPress || keyCode === KeyCode.KEYCODE_ALT_LEFT || keyCode === KeyCode.KEYCODE_ALT_RIGHT) {
243      return false
244    }
245    var keyValue = GetHardKeyValue(keyCode, this.isShiftKeyHold());
246    if (!keyValue) {
247      this.inputHandle.addLog('onKeyDown: unknown keyCode' );
248      this.isSpecialKeyPress = true;
249      return false;
250    }
251    try {
252      return this.inputHardKeyCode(keyValue, keyCode)
253    } catch (ex) {
254      //方向键api需要优化
255      this.inputHandle.addLog("inputHardKeyCode error: " + ex);
256      return false
257    }
258  }
259
260  public onKeyUp(keyEvent): boolean {
261    this.inputHandle.addLog('OnKeyUp: code = ' + keyEvent.keyCode);
262    var keyCode = keyEvent.keyCode;
263    let idx = this.keyCodes.indexOf(keyCode)
264    if (idx != -1) {
265      this.keyCodes.splice(idx, 1)
266    } else {
267      this.inputHandle.addLog("keyCode KeyUp is intercepted: " + keyCode);
268    }
269    this.inputHandle.addLog('OnKeyUp: this.keyCodes = ' + JSON.stringify(this.keyCodes));
270
271    // For KEYCODE_DEL/KEYCODE_FORWARD_DEL, processed in OnKeyDown, so just intercept it
272    if (keyCode == 2055 || keyCode == 2071 || (keyCode >= 2012 && keyCode <= 2016)) {
273      this.inputHandle.addLog('special code: ' + keyCode);
274      return true
275    }
276
277    if (this.isSpecialKeyPress) {
278      var keyValue = GetHardKeyValue(keyCode, this.isShiftKeyHold())
279      if (!keyValue) {
280        this.isSpecialKeyPress = true;
281      }
282      if (this.keyCodes.length == 0) {
283        this.isSpecialKeyPress = false;
284      }
285      this.inputHandle.addLog('OnKeyUp: this.isSpecialKeyPress: ' + this.isSpecialKeyPress);
286      return false
287    }
288    return true;
289  }
290
291  public isKeyCodeAZ(keyCode: number) {
292    return keyCode >= KeyCode.KEYCODE_A && keyCode <= KeyCode.KEYCODE_Z
293  }
294
295  public isKeyCodeNumber(keyCode: number) {
296    return (keyCode >= KeyCode.KEYCODE_0 && keyCode <= KeyCode.KEYCODE_9) || (keyCode >= KeyCode.KEYCODE_NUMPAD_0 && keyCode <= KeyCode.KEYCODE_NUMPAD_9)
297  }
298
299  public inputHardKeyCode(keyValue: string, keyCode: number): boolean {
300    this.inputHandle.addLog("keyValue is: " + keyValue);
301    if (this.processFunctionKeys(keyValue)) {
302      return true
303    }
304    if (this.shiftKeys(keyValue)) {
305      return false
306    }
307    this.inputHandle.insertText(keyValue);
308    return true
309  }
310
311  public shiftKeys(keyValue: string): boolean {
312    this.inputHandle.addLog("processFunctionKeys keyValue is: " + keyValue);
313    switch (keyValue) {
314      case 'KEYCODE_SHIFT_LEFT':
315      case 'KEYCODE_SHIFT_RIGHT':
316        return true
317      default:
318        return false
319    }
320  }
321
322  public processFunctionKeys(keyValue: string): boolean {
323    this.inputHandle.addLog("processFunctionKeys keyValue is: " + keyValue);
324    switch (keyValue) {
325      case "KEYCODE_DEL":
326        this.inputHandle.deleteBackward(1);
327        return true
328      case "KEYCODE_FORWARD_DEL":
329        this.inputHandle.deleteForward(1);
330        return true
331      case "KEYCODE_DPAD_UP":
332        inputMethodEngine.MoveCursor(inputMethodEngine.CURSOR_UP);
333        return true
334      case "KEYCODE_DPAD_DOWN":
335        inputMethodEngine.MoveCursor(inputMethodEngine.CURSOR_DOWN);
336        return true
337      case "KEYCODE_DPAD_LEFT":
338        inputMethodEngine.MoveCursor(inputMethodEngine.CURSOR_LEFT);
339        return true
340      case "KEYCODE_DPAD_RIGHT":
341        inputMethodEngine.MoveCursor(inputMethodEngine.CURSOR_RIGHT);
342        return true
343      default:
344        return false
345    }
346  }
347
348  private unRegisterListener(): void{
349    this.inputHandle.addLog('unRegisterListener')
350
351    InputMethodEngine.off('inputStop', ()=> {
352      this.inputHandle.addLog('inputStop off');
353    });
354
355    InputMethodEngine.off('keyboardShow');
356
357    InputMethodEngine.off('keyboardHide');
358
359    this.mKeyboardDelegate.off('keyDown');
360
361    this.mKeyboardDelegate.off('keyUp');
362
363    if (isDebug) {
364      this.mKeyboardDelegate.off('cursorContextChange');
365
366      this.mKeyboardDelegate.off('selectionChange');
367
368      this.mKeyboardDelegate.off('textChange');
369    }
370  }
371
372  private showWindow() {
373    try {
374      this.getValue('settings.display.navigationbar_status')
375    } catch (err) {
376      Log.showError(TAG, "get value failed" + err)
377    }
378    Log.showInfo(TAG, 'current navigation state is' + this.getValueRes)
379    this.inputHandle.addLog('showWindow');
380    windowManager.find(this.windowName).then((win) => {
381      win.show().then(() => {
382        this.inputHandle.addLog('showWindow finish');
383        this.isKeyboardShow = true;
384      })
385    })
386  }
387
388  private hideWindow() {
389    this.inputHandle.addLog('hideWindow');
390    windowManager.find(this.windowName).then((win) => {
391      win.hide().then(() => {
392        this.isKeyboardShow = false;
393        this.inputHandle.addLog('hideWindow finish');
394      })
395    })
396  }
397}
398
399export class InputHandler {
400  private static instance: InputHandler;
401  private mKbController;
402  private mTextInputClient;
403  private mEditorAttribute;
404  private cursorInfo;
405  private selectInfo;
406  private textInfo;
407  private inputInfo;
408  private constructor() {
409
410  }
411
412  public static getInstance() {
413    if (globalThis.instance == null) {
414      globalThis.instance = new InputHandler();
415    }
416    return globalThis.instance;
417  }
418
419  public onInputStart(kbController, textInputClient) {
420    globalThis.mKbController = kbController;
421    globalThis.mTextInputClient = textInputClient;
422    globalThis.mEditorAttribute = globalThis.mTextInputClient.getEditorAttribute();
423    this.addLog("onInputStart mEditorAttribute = " + globalThis.mEditorAttribute);
424    globalThis.mEditorAttribute.then(res => {
425      globalThis.enterKeyType = res.enterKeyType;
426      globalThis.inputPattern = res.inputPattern;
427      this.setInputInfo("EditorInfo:enterKeyType = " + globalThis.enterKeyType + ";inputPattern = " + globalThis.inputPattern);
428    })
429  }
430
431  public hideKeyboardSelf() {
432    this.addLog('hideKeyboardSelf')
433    if (globalThis.mKbController != undefined) {
434      globalThis.mKbController.hideKeyboard();
435    } else {
436      this.addLog('hideKeyboardSelf globalThis.mKbController is undefined')
437    }
438  }
439
440  public sendKeyFunction() {
441    this.addLog('sendKeyFunction')
442    if (globalThis.mTextInputClient != undefined) {
443      globalThis.mTextInputClient.sendKeyFunction(globalThis.enterKeyType);
444    } else {
445      this.addLog('sendKeyFunction globalThis.mTextInputClient is undefined')
446    }
447  }
448
449  public deleteBackward(length: number) {
450    this.addLog('deleteBackward')
451    if (globalThis.mTextInputClient != undefined) {
452      globalThis.mTextInputClient.deleteBackward(length);
453    } else {
454      this.addLog('deleteBackward globalThis.mTextInputClient is undefined')
455    }
456    if (isDebug) {
457      this.refreshInfo();
458    }
459  }
460
461  public deleteForward(length: number) {
462    this.addLog('deleteForward')
463    if (globalThis.mTextInputClient != undefined) {
464      globalThis.mTextInputClient.deleteForward(length);
465    } else {
466      this.addLog('deleteForward globalThis.mTextInputClient is undefined')
467    }
468    if (isDebug) {
469      this.refreshInfo();
470    }
471  }
472
473  public insertText(text: string) {
474    this.addLog('insertText')
475    if (globalThis.mTextInputClient != undefined) {
476      globalThis.mTextInputClient.insertText(text);
477    } else {
478      this.addLog('insertText globalThis.mTextInputClient is undefined')
479    }
480    if (isDebug) {
481      this.refreshInfo();
482    }
483  }
484
485  public addLog(message): void {
486    Log.showInfo(TAG, "kikaInput-js: " + message)
487  }
488
489  public setCursorInfo(info) {
490    globalThis.cursorInfo = info;
491    this.addLog("setCursorInfo info = " + info);
492  }
493
494  public setSelectInfo(info) {
495    globalThis.selectInfo = info;
496    this.addLog("setSelectInfo info = " + info);
497  }
498
499  public setTextInfo(info) {
500    globalThis.textInfo = info;
501    this.addLog("setTextInfo info = " + info);
502  }
503
504  public setInputInfo(info) {
505    globalThis.inputInfo = info;
506    this.addLog("setInputInfo info = " + info);
507  }
508
509  public refreshInfo() {
510    this.addLog('refreshInfo');
511    var showText = '';
512    this.addLog('refreshInfo1');
513    showText += 'five chars: before:[' + globalThis.mTextInputClient.getForward(5) + '];after:[' + globalThis.mTextInputClient.getBackward(5) + ']\n';
514    this.addLog('refreshInfo2');
515    showText += globalThis.cursorInfo + "\n";
516    this.addLog('refreshInfo3');
517    showText += globalThis.selectInfo + "\n";
518    this.addLog('refreshInfo4');
519    showText += globalThis.textInfo + "\n";
520    this.addLog('refreshInfo5');
521    showText += globalThis.inputInfo;
522    this.addLog('refreshInfo6');
523    AppStorage.Set<string>('showLog', showText);
524    this.addLog('refreshInfo7');
525    this.addLog('result is = ' + showText);
526  }
527}
528