Failure to close soft keyboard when using native activity

We are developing an indie game for Android and would like the user to choose their nickname. We decided to use the Native Activity, which is provided by the NDK, as this seemed to be the easiest way.

The first problem we encountered with the keyboard was that the ANativeActivity_showSoftInput () function seems to be doing nothing (as described, for example here ), so we bring the keyboard using JNI calls to work:

static void showKeyboard(Activity activity) {
  String s = Context.INPUT_METHOD_SERVICE;
  InputMethodManager m = (InputMethodManager)activity.getSystemService(s);
  View w = activity.getWindow().getDecorView();
  m.showSoftInput(w, 0);
}

This is great for creating keyboards and works great on some devices. But on other devices (for example, Nexus 7), when the user tries to close the keyboard by clicking the "Hide Keyboard" button, the application freezes with this debug output:

I/InputDispatcher(  453): Application is not responding: AppWindowToken{429b54a8 token=Token{42661288 ActivityRecord{41bb0b00 u0 com.example.project/android.app.NativeActivity}}} - Window{420d6138 u0 com.example.project/android.app.NativeActivity}.  It has been 5006.7ms since event, 5005.6ms since wait started.  Reason: Waiting because the focused window has not finished processing the input events that were previously delivered to it.
I/WindowManager(  453): Input event dispatching timed out sending to com.example.project/android.app.NativeActivity

Then the user is presented with a dialog box with the message:

Project isn't responding. Do you want to close it? [Wait]/[OK]

Is there something we are clearly doing wrong? Or could it be a mistake? Problems like this seem to say that keyboard functionality has never been properly implemented in native bite.

, , , , - , ​​ Android. , , , , , backward arrow shaped button, , : V shaped button. , , , ? .

, - , , , .

UPDATE

Android , . , ( ).

+5
2

Peter . , native_app_glue: , process_input . process_input, Peter:

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));

        bool skip_predispatch
              =  AInputEvent_getType(event)  == AINPUT_EVENT_TYPE_KEY
              && AKeyEvent_getKeyCode(event) == AKEYCODE_BACK;

        // skip predispatch (all it does is send to the IME)
        if (!skip_predispatch && 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));
    }
}

android_main process_input android_app- > inputPollSource.process.

, (AKEYCODE_BACK) , , .

, , , Android 4.1 4.2 - 4.3

+3

, UPDATE , - Android. , , , - .

<NDK>/sources/android/native_app_glue/android_native_app_glue.c

process_input, :

// 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));
    }
}

, :

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;  
}

, JNI, android.os.Build.VERSION.RELEASE android.os.Build.VERSION.SDK_INT. show_keyboard hide_keyboard Ratamovics .

android_native_app_glue.c NDK, jni/ Android.mk

LOCAL_STATIC_LIBRARIES := android_native_app_glue
$(call import-module,android/native_app_glue)
+2

All Articles