jackpal/Android-Terminal-Emulator — Issue #79: Ctrl keys not working when using Bluetooth keyboard
Author: kiwidrew
Created: 2012-01-23 16:02:33+00:00
State: open

Keyboard: Logitech Tablet Keyboard for iPad (Bluetooth)
Device: Samsung Galaxy Tab 8.9 (with stock Honeycomb 3.1 firmware)
Summary: Ctrl key combinations don't work, but all other keys (including Tab) work as expected.

When pressing the following key sequence inside the terminal:

```
Enter
H
H
Shift+H
Alt+H
Cmd+H
Ctrl+H
Enter
```

The resulting log output from the EmulatorView (modified to output the full KeyEvent.toString() in the onKeyDown/onKeyUp handlers) is:

```
01-23 23:12:26.400 W/EmulatorView(3527): finishComposingText
01-23 23:12:27.590 W/EmulatorView(3527): onKeyDown 66 [KeyEvent{action=ACTION_DOWN keycode=KEYCODE_ENTER scancode=28 metaState=0 flags=0x8 repeat=0 device=7 source=0x701}]
01-23 23:12:27.820 W/EmulatorView(3527): onKeyUp 66 [KeyEvent{action=ACTION_UP keycode=KEYCODE_ENTER scancode=28 metaState=0 flags=0x8 repeat=0 device=7 source=0x701}]
01-23 23:12:29.390 W/EmulatorView(3527): getTextBeforeCursor(1,0)
01-23 23:12:29.400 W/EmulatorView(3527): finishComposingText
01-23 23:12:29.400 W/EmulatorView(3527): commitText("h", 1)
01-23 23:12:29.440 W/EmulatorView(3527): onKeyUp 36 [KeyEvent{action=ACTION_UP keycode=KEYCODE_H scancode=35 metaState=0 flags=0x8 repeat=0 device=7 source=0x701}]
01-23 23:12:30.050 W/EmulatorView(3527): getTextBeforeCursor(1,0)
01-23 23:12:30.050 W/EmulatorView(3527): finishComposingText
01-23 23:12:30.050 W/EmulatorView(3527): commitText("h", 1)
01-23 23:12:30.100 W/EmulatorView(3527): onKeyUp 36 [KeyEvent{action=ACTION_UP keycode=KEYCODE_H scancode=35 metaState=0 flags=0x8 repeat=0 device=7 source=0x701}]
01-23 23:12:30.720 W/EmulatorView(3527): onKeyDown 59 [KeyEvent{action=ACTION_DOWN keycode=KEYCODE_SHIFT_LEFT scancode=42 metaState=META_SHIFT_ON|META_SHIFT_LEFT_ON flags=0x8 repeat=0 device=7 source=0x701}]
01-23 23:12:30.970 W/EmulatorView(3527): onKeyDown 59 [KeyEvent{action=ACTION_DOWN keycode=KEYCODE_SHIFT_LEFT scancode=42 metaState=META_SHIFT_ON|META_SHIFT_LEFT_ON flags=0x88 repeat=1 device=7 source=0x701}]
01-23 23:12:30.990 W/EmulatorView(3527): getTextBeforeCursor(1,0)
01-23 23:12:31.000 W/EmulatorView(3527): finishComposingText
01-23 23:12:31.000 W/EmulatorView(3527): commitText("H", 1)
01-23 23:12:31.000 W/EmulatorView(3527): getTextBeforeCursor(1,0)
01-23 23:12:31.000 W/EmulatorView(3527): getTextBeforeCursor(2,0)
01-23 23:12:31.070 W/EmulatorView(3527): onKeyUp 36 [KeyEvent{action=ACTION_UP keycode=KEYCODE_H scancode=35 metaState=META_SHIFT_ON|META_SHIFT_LEFT_ON flags=0x8 repeat=0 device=7 source=0x701}]
01-23 23:12:31.410 W/EmulatorView(3527): onKeyUp 59 [KeyEvent{action=ACTION_UP keycode=KEYCODE_SHIFT_LEFT scancode=42 metaState=0 flags=0x8 repeat=0 device=7 source=0x701}]
01-23 23:12:31.800 W/EmulatorView(3527): onKeyDown 57 [KeyEvent{action=ACTION_DOWN keycode=KEYCODE_ALT_LEFT scancode=56 metaState=META_ALT_ON|META_ALT_LEFT_ON flags=0x8 repeat=0 device=7 source=0x701}]
01-23 23:12:32.040 W/EmulatorView(3527): getTextBeforeCursor(1,0)
01-23 23:12:32.040 W/EmulatorView(3527): finishComposingText
01-23 23:12:32.040 W/EmulatorView(3527): commitText("h", 1)
01-23 23:12:32.120 W/EmulatorView(3527): onKeyUp 36 [KeyEvent{action=ACTION_UP keycode=KEYCODE_H scancode=35 metaState=META_ALT_ON|META_ALT_LEFT_ON flags=0x8 repeat=0 device=7 source=0x701}]
01-23 23:12:32.430 W/EmulatorView(3527): onKeyUp 57 [KeyEvent{action=ACTION_UP keycode=KEYCODE_ALT_LEFT scancode=56 metaState=0 flags=0x8 repeat=0 device=7 source=0x701}]
01-23 23:12:32.810 W/EmulatorView(3527): onKeyDown 117 [KeyEvent{action=ACTION_DOWN keycode=KEYCODE_META_LEFT scancode=125 metaState=META_META_ON|META_META_LEFT_ON flags=0x8 repeat=0 device=7 source=0x701}]
01-23 23:12:33.080 W/EmulatorView(3527): onKeyDown 117 [KeyEvent{action=ACTION_DOWN keycode=KEYCODE_META_LEFT scancode=125 metaState=META_META_ON|META_META_LEFT_ON flags=0x88 repeat=1 device=7 source=0x701}]
01-23 23:12:33.080 W/EmulatorView(3527): getTextBeforeCursor(1,0)
01-23 23:12:33.080 W/EmulatorView(3527): finishComposingText
01-23 23:12:33.090 W/EmulatorView(3527): commitText("h", 1)
01-23 23:12:33.150 W/EmulatorView(3527): onKeyUp 36 [KeyEvent{action=ACTION_UP keycode=KEYCODE_H scancode=35 metaState=META_META_ON|META_META_LEFT_ON flags=0x8 repeat=0 device=7 source=0x701}]
01-23 23:12:33.470 W/EmulatorView(3527): onKeyUp 117 [KeyEvent{action=ACTION_UP keycode=KEYCODE_META_LEFT scancode=125 metaState=0 flags=0x8 repeat=0 device=7 source=0x701}]
01-23 23:12:34.570 W/EmulatorView(3527): onKeyUp 36 [KeyEvent{action=ACTION_UP keycode=KEYCODE_H scancode=35 metaState=META_CTRL_ON|META_CTRL_LEFT_ON flags=0x8 repeat=0 device=7 source=0x701}]
01-23 23:12:34.790 W/EmulatorView(3527): onKeyUp 113 [KeyEvent{action=ACTION_UP keycode=KEYCODE_CTRL_LEFT scancode=29 metaState=0 flags=0x8 repeat=0 device=7 source=0x701}]
01-23 23:12:35.570 W/EmulatorView(3527): onKeyDown 66 [KeyEvent{action=ACTION_DOWN keycode=KEYCODE_ENTER scancode=28 metaState=0 flags=0x8 repeat=0 device=7 source=0x701}]
01-23 23:12:35.650 W/EmulatorView(3527): onKeyUp 66 [KeyEvent{action=ACTION_UP keycode=KEYCODE_ENTER scancode=28 metaState=0 flags=0x8 repeat=0 device=7 source=0x701}]
```

As can be seen from the log, KEYCODE_H is delivered through the InputConnection and passed as synthetic input by commitText() while KEYCODE_ENTER, KEYCODE_SHIFT_LEFT, KEYCODE_ALT_LEFT, and KEYCODE_META_LEFT are delivered to onKeyDown() and onKeyUp(). KEYCODE_CTRL_LEFT, however, is only delivered to onKeyUp() -- the KeyDown event is missing in action, it seems. Furthermore, while the Ctrl key is pressed, the KEYCODE_H is not delivered through the InputConnection -- it is also missing in action.

The net effect is that pressing Ctrl+H does absolutely nothing.

Note that the onKeyUp() handler *does* receive a useful KeyEvent: it is an ACTION_UP event and has a keycode of KEYCODE_H with a metaState of META_CTRL_ON|META_CTRL_LEFT_ON. This is exactly what I want to handle, but the existing design of EmulatorView only handles key input on the ACTION_DOWN events and ignores the metaState flags; instead it tracks the modifier states explicitly. Why is this? The Android SDK documentation for KeyEvent says that (in order to properly handle FLAG_CANCELED) applications cannot perform an action for a key until it has received an ACTION_UP event.

[Educated guess: this has something to do with supporting early Android devices?]

For my immediate needs, I'm probably just going to patch the onKeyUp() handler to recognize ACTION_UP KeyEvents with the META_CTRL_ON flag. This should solve my problem, but I have to wonder how it is that the existing code works at all; I presume that Ctrl key handling works for everyone else. Is my keyboard or device misbehaving?