Detecting and handling input events at Android NDK

Input events are essential for user interaction in Android apps. This recipe discusses how to detect and handle input events in Android NDK.

Getting ready

We will further develop the example in last recipe. Please read the Managing native windows at Android NDK recipe before going through this one.

How to do it…

The following steps create a sample application, which detects and handles input events at the native code:

  1. Create an Android application named NativeInputs. Set the package name as cookbook.chapter5.nativeinputs. Please refer to the Loading native libraries and registering native methods recipe of Chapter 2, Java Native Interface, if you want more detailed instructions.
  2. Right-click on the NativeInputs project, select Android Tools | Add Native Support.
  3. Update AndroidManifest.xml. Please refer to previous recipe or the downloaded code for details. Note that the metadata android.app.lib_name must have a value as NativeInputs.
  4. Add two files named NativeInputs.cpp and mylog.h under the jni folder. NativeInputs.cpp is modified based on the previous recipe. Let us see a part of its code here:
    • handle_input_events: This is the event handler method for input events. Note that when a motion event with move action (AINPUT_EVENT_TYPE_MOTION) is detected, we update app->userData and set app->redrawNeeded to 1:
      int mPreviousX = -1;
      int32_t handle_input_events(struct android_app* app, AInputEvent* event) {
        int etype = AInputEvent_getType(event);
        switch (etype) {
        case AINPUT_EVENT_TYPE_KEY:
      … ...    
          break;
        case AINPUT_EVENT_TYPE_MOTION:
          int32_t action, posX, pointer_index;
          action = AMotionEvent_getAction(event);
          pointer_index = (action&AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;
          posX = AMotionEvent_getX(event, pointer_index);
          if (action == AMOTION_EVENT_ACTION_MOVE) {
            int xMove = posX - mPreviousX;
            USERDATA* userData = (USERDATA*)app->userData;
            userData->xMove = xMove;
            app->redrawNeeded = 1;
          }
          mPreviousX = posX;
          break;
        }
      }
    • android_main: We update the while true loop. When app->redrawNeeded is set, we redraw the rectangle:
      void android_main(struct android_app* app) {
      … ...
      while (1) {
          int ident, events;
          struct android_poll_source* source;
          if ((ident=ALooper_pollOnce(app->redrawNeeded?0:-1, NULL, &events, (void**)&source)) >= 0) {
            if (NULL!=source) {
              source->process(app, source);
            }
            if (app->redrawNeeded) {
              drawSomething(app);
            }
          }
      }
      }
  5. Add the Android.mk file under the jni folder, which is similar to previous recipe. We just need to replace the module name as NativeInputs and the source file as NativeInputs.cpp.
  6. Build the Android application and run it on an emulator or device. We can move a figure across the screen to see the rectangle moving horizontally:
    How to do it…

How it works…

This recipe discusses input events handling with the android_native_app_glue library at Android NDK.

Input event queue in android_native_app_glue: android_native_app_glue attaches the input event queue for us by default.

  1. When the input queue is created for an activity, the onInputQueueCreated callback is called on the main thread, which writes APP_CMD_INPUT_CHANGED to the write end of the pipe we described in previous recipe. The background thread will receive the command and call AInputQueue_attachLooper the function to attach the input queue to the background thread looper.
  2. When an input event occurs, it will be handled by process_input (the function pointer source->process in the while true loop we called points to process_input if the event is an input event). Inside process_input, AInputQueue_getEvent is firstly called to retrieve the event. Then, AInputQueue_preDispatchEvent is called to send the key for pre-dispatching. This could possibly result in it being consumed by the current Input Method Editor (IME) before the app. Followed by this is the android_app->onInputEvent, which is a function pointer-pointing to an event handler provided by us. If no event handler is provided by us, it's set to NULL. After that, AInputQueue_finishEvent is called to indicate that event handling is over.
  3. Lastly, when the input queue is destroyed, the onInputQueueDestroyed callback is called on the main thread, which also writes APP_CMD_INPUT_CHANGED. The background thread will read the command and call a function named AInputQueue_detachLooper to detach the input queue from the thread looper.

Event handler: In the handle_input_events function, we first called AInputEvent_getType to get the input event type. The android/input.h header file defines two input event types, namely AINPUT_EVENT_TYPE_KEY and AINPUT_EVENT_TYPE_MOTION. The first event type indicates that the input event is a key event, while the second one indicates that it is a motion event.

We called AKeyEvent_getAction, AKeyEvent_getFlags, and AKeyEvent_getKeyCode to get the action, flags, and key code of a key event and printed a string to describe it. On the other hand, we called AMotionEvent_getAction and AMotionEvent_getX to get the action and the x position of a motion event. Note that the AMotionEvent_getX function requires the second input argument as the pointer index. The pointer index is obtained by using the following code:

pointer_index = (action&AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT;

There are a lot more input event functions, which can be found at andoid/input.h.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset