@@ -271,6 +271,10 @@ public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
271
271
// Some keyboards seems do not reset the internal state on TYPE_NULL.
272
272
// Affects mostly Samsung stock keyboards.
273
273
// https://github.com/termux/termux-app/issues/686
274
+ // However, this is not a valid value as per AOSP since `InputType.TYPE_CLASS_*` is
275
+ // not set and it logs a warning:
276
+ // W/InputAttributes: Unexpected input class: inputType=0x00080090 imeOptions=0x02000000
277
+ // https://cs.android.com/android/platform/superproject/+/android-11.0.0_r40:packages/inputmethods/LatinIME/java/src/com/android/inputmethod/latin/InputAttributes.java;l=79
274
278
outAttrs .inputType = InputType .TYPE_TEXT_VARIATION_VISIBLE_PASSWORD | InputType .TYPE_TEXT_FLAG_NO_SUGGESTIONS ;
275
279
} else {
276
280
// Using InputType.NULL is the most correct input type and avoids issues with other hacks.
@@ -346,6 +350,10 @@ void sendTextToTerminal(CharSequence text) {
346
350
codePoint = firstChar ;
347
351
}
348
352
353
+ // Check onKeyDown() for details.
354
+ if (mClient .readShiftKey ())
355
+ codePoint = Character .toUpperCase (codePoint );
356
+
349
357
boolean ctrlHeld = false ;
350
358
if (codePoint <= 31 && codePoint != 27 ) {
351
359
if (codePoint == '\n' ) {
@@ -576,6 +584,102 @@ public boolean onKeyPreIme(int keyCode, KeyEvent event) {
576
584
return super .onKeyPreIme (keyCode , event );
577
585
}
578
586
587
+ /**
588
+ * Key presses in software keyboards will generally NOT trigger this listener, although some
589
+ * may elect to do so in some situations. Do not rely on this to catch software key presses.
590
+ * Gboard calls this when shouldEnforceCharBasedInput() is disabled (InputType.TYPE_NULL) instead
591
+ * of calling commitText(), with deviceId=-1. However, Hacker's Keyboard, OpenBoard, LG Keyboard
592
+ * call commitText().
593
+ *
594
+ * This function may also be called directly without android calling it, like by
595
+ * `TerminalExtraKeys` which generates a KeyEvent manually which uses {@link KeyCharacterMap#VIRTUAL_KEYBOARD}
596
+ * as the device (deviceId=-1), as does Gboard. That would normally use mappings defined in
597
+ * `/system/usr/keychars/Virtual.kcm`. You can run `dumpsys input` to find the `KeyCharacterMapFile`
598
+ * used by virtual keyboard or hardware keyboard. Note that virtual keyboard device is not the
599
+ * same as software keyboard, like Gboard, etc. Its a fake device used for generating events and
600
+ * for testing.
601
+ *
602
+ * We handle shift key in `commitText()` to convert codepoint to uppercase case there with a
603
+ * call to {@link Character#toUpperCase(int)}, but here we instead rely on getUnicodeChar() for
604
+ * conversion of keyCode, for both hardware keyboard shift key (via effectiveMetaState) and
605
+ * `mClient.readShiftKey()`, based on value in kcm files.
606
+ * This may result in different behaviour depending on keyboard and android kcm files set for the
607
+ * InputDevice for the event passed to this function. This will likely be an issue for non-english
608
+ * languages since `Virtual.kcm` in english only by default or at least in AOSP. For both hardware
609
+ * shift key (via effectiveMetaState) and `mClient.readShiftKey()`, `getUnicodeChar()` is used
610
+ * for shift specific behaviour which usually is to uppercase.
611
+ *
612
+ * For fn key on hardware keyboard, android checks kcm files for hardware keyboards, which is
613
+ * `Generic.kcm` by default, unless a vendor specific one is defined. The event passed will have
614
+ * {@link KeyEvent#META_FUNCTION_ON} set. If the kcm file only defines a single character or unicode
615
+ * code point `\\uxxxx`, then only one event is passed with that value. However, if kcm defines
616
+ * a `fallback` key for fn or others, like `key DPAD_UP { ... fn: fallback PAGE_UP }`, then
617
+ * android will first pass an event with original key `DPAD_UP` and {@link KeyEvent#META_FUNCTION_ON}
618
+ * set. But this function will not consume it and android will pass another event with `PAGE_UP`
619
+ * and {@link KeyEvent#META_FUNCTION_ON} not set, which will be consumed.
620
+ *
621
+ * Now there are some other issues as well, firstly ctrl and alt flags are not passed to
622
+ * `getUnicodeChar()`, so modified key values in kcm are not used. Secondly, if the kcm file
623
+ * for other modifiers like shift or fn define a non-alphabet, like { fn: '\u0015' } to act as
624
+ * DPAD_LEFT, the `getUnicodeChar()` will correctly return `21` as the code point but action will
625
+ * not happen because the `handleKeyCode()` function that transforms DPAD_LEFT to `\033[D`
626
+ * escape sequence for the terminal to perform the left action would not be called since its
627
+ * called before `getUnicodeChar()` and terminal will instead get `21 0x15 Negative Acknowledgement`.
628
+ * The solution to such issues is calling `getUnicodeChar()` before the call to `handleKeyCode()`
629
+ * if user has defined a custom kcm file, like done in POC mentioned in #2237. Note that
630
+ * Hacker's Keyboard calls `commitText()` so don't test fn/shift with it for this function.
631
+ * https://github.com/termux/termux-app/pull/2237
632
+ * https://github.com/agnostic-apollo/termux-app/blob/terminal-code-point-custom-mapping/terminal-view/src/main/java/com/termux/view/TerminalView.java
633
+ *
634
+ * Key Character Map (kcm) and Key Layout (kl) files info:
635
+ * https://source.android.com/devices/input/key-character-map-files
636
+ * https://source.android.com/devices/input/key-layout-files
637
+ * https://source.android.com/devices/input/keyboard-devices
638
+ * AOSP kcm and kl files:
639
+ * https://cs.android.com/android/platform/superproject/+/android-11.0.0_r40:frameworks/base/data/keyboards
640
+ * https://cs.android.com/android/platform/superproject/+/android-11.0.0_r40:frameworks/base/packages/InputDevices/res/raw
641
+ *
642
+ * KeyCodes:
643
+ * https://cs.android.com/android/platform/superproject/+/android-11.0.0_r40:frameworks/base/core/java/android/view/KeyEvent.java
644
+ * https://cs.android.com/android/platform/superproject/+/master:frameworks/native/include/android/keycodes.h
645
+ *
646
+ * `dumpsys input`:
647
+ * https://cs.android.com/android/platform/superproject/+/android-11.0.0_r40:frameworks/native/services/inputflinger/reader/EventHub.cpp;l=1917
648
+ *
649
+ * Loading of keymap:
650
+ * https://cs.android.com/android/platform/superproject/+/android-11.0.0_r40:frameworks/native/services/inputflinger/reader/EventHub.cpp;l=1644
651
+ * https://cs.android.com/android/platform/superproject/+/android-11.0.0_r40:frameworks/native/libs/input/Keyboard.cpp;l=41
652
+ * https://cs.android.com/android/platform/superproject/+/android-11.0.0_r40:frameworks/native/libs/input/InputDevice.cpp
653
+ * OVERLAY keymaps for hardware keyboards may be combined as well:
654
+ * https://cs.android.com/android/platform/superproject/+/android-11.0.0_r40:frameworks/native/libs/input/KeyCharacterMap.cpp;l=165
655
+ * https://cs.android.com/android/platform/superproject/+/android-11.0.0_r40:frameworks/native/libs/input/KeyCharacterMap.cpp;l=831
656
+ *
657
+ * Parse kcm file:
658
+ * https://cs.android.com/android/platform/superproject/+/android-11.0.0_r40:frameworks/native/libs/input/KeyCharacterMap.cpp;l=727
659
+ * Parse key value:
660
+ * https://cs.android.com/android/platform/superproject/+/android-11.0.0_r40:frameworks/native/libs/input/KeyCharacterMap.cpp;l=981
661
+ *
662
+ * `KeyEvent.getUnicodeChar()`
663
+ * https://cs.android.com/android/platform/superproject/+/android-11.0.0_r40:frameworks/base/core/java/android/view/KeyEvent.java;l=2716
664
+ * https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/view/KeyCharacterMap.java;l=368
665
+ * https://cs.android.com/android/platform/superproject/+/android-11.0.0_r40:frameworks/base/core/jni/android_view_KeyCharacterMap.cpp;l=117
666
+ * https://cs.android.com/android/platform/superproject/+/android-11.0.0_r40:frameworks/native/libs/input/KeyCharacterMap.cpp;l=231
667
+ *
668
+ * Keyboard layouts advertised by applications, like for hardware keyboards via #ACTION_QUERY_KEYBOARD_LAYOUTS
669
+ * Config is stored in `/data/system/input-manager-state.xml`
670
+ * https://github.com/ris58h/custom-keyboard-layout
671
+ * Loading from apps:
672
+ * https://cs.android.com/android/platform/superproject/+/master:frameworks/base/services/core/java/com/android/server/input/InputManagerService.java;l=1221
673
+ * Set:
674
+ * https://cs.android.com/android/platform/superproject/+/android-11.0.0_r40:frameworks/base/core/java/android/hardware/input/InputManager.java;l=89
675
+ * https://cs.android.com/android/platform/superproject/+/android-11.0.0_r40:frameworks/base/core/java/android/hardware/input/InputManager.java;l=543
676
+ * https://cs.android.com/android/platform/superproject/+/android-11.0.0_r40:packages/apps/Settings/src/com/android/settings/inputmethod/KeyboardLayoutDialogFragment.java;l=167
677
+ * https://cs.android.com/android/platform/superproject/+/master:frameworks/base/services/core/java/com/android/server/input/InputManagerService.java;l=1385
678
+ * https://cs.android.com/android/platform/superproject/+/master:frameworks/base/services/core/java/com/android/server/input/PersistentDataStore.java
679
+ * Get overlay keyboard layout
680
+ * https://cs.android.com/android/platform/superproject/+/master:frameworks/base/services/core/java/com/android/server/input/InputManagerService.java;l=2158
681
+ * https://cs.android.com/android/platform/superproject/+/android-11.0.0_r40:frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp;l=616
682
+ */
579
683
@ Override
580
684
public boolean onKeyDown (int keyCode , KeyEvent event ) {
581
685
if (TERMINAL_VIEW_KEY_LOGGING_ENABLED )
@@ -599,7 +703,6 @@ public boolean onKeyDown(int keyCode, KeyEvent event) {
599
703
final boolean controlDown = event .isCtrlPressed () || mClient .readControlKey ();
600
704
final boolean leftAltDown = (metaState & KeyEvent .META_ALT_LEFT_ON ) != 0 || mClient .readAltKey ();
601
705
final boolean shiftDown = event .isShiftPressed () || mClient .readShiftKey ();
602
- final boolean fnDown = event .isFunctionPressed () || mClient .readFnKey ();
603
706
final boolean rightAltDownFromEvent = (metaState & KeyEvent .META_ALT_RIGHT_ON ) != 0 ;
604
707
605
708
int keyMod = 0 ;
@@ -608,7 +711,7 @@ public boolean onKeyDown(int keyCode, KeyEvent event) {
608
711
if (shiftDown ) keyMod |= KeyHandler .KEYMOD_SHIFT ;
609
712
if (event .isNumLockOn ()) keyMod |= KeyHandler .KEYMOD_NUM_LOCK ;
610
713
// https://github.com/termux/termux-app/issues/731
611
- if (!fnDown && handleKeyCode (keyCode , keyMod )) {
714
+ if (!event . isFunctionPressed () && handleKeyCode (keyCode , keyMod )) {
612
715
if (TERMINAL_VIEW_KEY_LOGGING_ENABLED ) mClient .logInfo (LOG_TAG , "handleKeyCode() took key event" );
613
716
return true ;
614
717
}
@@ -624,7 +727,7 @@ public boolean onKeyDown(int keyCode, KeyEvent event) {
624
727
int effectiveMetaState = event .getMetaState () & ~bitsToClear ;
625
728
626
729
if (shiftDown ) effectiveMetaState |= KeyEvent .META_SHIFT_ON | KeyEvent .META_SHIFT_LEFT_ON ;
627
- if (fnDown ) effectiveMetaState |= KeyEvent .META_FUNCTION_ON ;
730
+ if (mClient . readFnKey () ) effectiveMetaState |= KeyEvent .META_FUNCTION_ON ;
628
731
629
732
int result = event .getUnicodeChar (effectiveMetaState );
630
733
if (TERMINAL_VIEW_KEY_LOGGING_ENABLED )
0 commit comments