OK, as mentioned in the UPDATE of my original question, this is a bug somewhere inside the Android OS. We've found a workaround, it is really ugly but it works so someone might find it useful.
First you'll need to modify the file
<NDK>/sources/android/native_app_glue/android_native_app_glue.c
by changing the function process_input to look like this:
// When user closes the software keyboard, this function is normally not
// called at all. On the buggy devices, it is called as if AKEYCODE_BACK
// was pressed. This event then gets consumed by the
// AInputQueue_preDispatchEvent. There should be some mechanism that then
// calls the process_input again to finish processing the input.
// But it never does and AInputQueue_finishEvent is never called, the OS
// notices this and closes our app.
static void process_input( struct android_app* app
, struct android_poll_source* source) {
AInputEvent* event = NULL;
if (AInputQueue_getEvent(app->inputQueue, &event) >= 0) {
int type = AInputEvent_getType(event);
LOGV("New input event: type=%d\n", AInputEvent_getType(event));
int skip_predispatch
= AInputEvent_getType(event) == AINPUT_EVENT_TYPE_KEY
&& AKeyEvent_getKeyCode(event) == AKEYCODE_BACK;
// TODO: Not sure if we should skip the predispatch all together
// or run it but not return afterwards. The main thing
// is that the code below this 'if' block will be called.
if (!skip_predispatch) {
if (AInputQueue_preDispatchEvent(app->inputQueue, event)) {
return;
}
}
int32_t handled = 0;
if (app->onInputEvent != NULL) handled = app->onInputEvent(app, event);
AInputQueue_finishEvent(app->inputQueue, event, handled);
} else {
LOGE("Failure reading next input event: %s\n", strerror(errno));
}
}
Then you should have a code that looks something like this inside your own input event handler:
static int32_t handle_input(android_app* app, AInputEvent* event) {
int32_t handled = 0;
struct engine* engine = (struct engine*) app->userData;
switch (AInputEvent_getType(event)) {
case AINPUT_EVENT_TYPE_KEY:
switch (AKeyEvent_getAction(event)) {
case AKEY_EVENT_ACTION_DOWN:
int key = AKeyEvent_getKeyCode(event);
if (os_version_major == 4 && os_version_minor == 2) {
if (m_keyboard_is_visible && key == AKEYCODE_BACK) {
// You should set this to true when showing the keyboard.
m_keyboard_is_visible = false;
hide_keyboard();
handled = 1;
break;
}
}
... // your own "key down" event handling code.
break;
}
break;
...
}
return handled;
}
To get the OS version numbers we use another JNI calls to get it from android.os.Build.VERSION.RELEASE android.os.Build.VERSION.SDK_INT. To implement the show_keyboard and hide_keyboard use information from Ratamovics answer in this post.
NOTE To have the android_native_app_glue.c compiled automatically and to avoid doing changes directly to the NDK tree, you might want to copy the file to the jni/ directory of your project and ditch these two lines from your Android.mk
LOCAL_STATIC_LIBRARIES := android_native_app_glue
$(call import-module,android/native_app_glue)