Input events are essential for user interaction in Android apps. This recipe discusses how to detect and handle input events in Android NDK.
We will further develop the example in last recipe. Please read the Managing native windows at Android NDK recipe before going through this one.
The following steps create a sample application, which detects and handles input events at the native code:
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.NativeInputs
project, select Android Tools | Add Native Support.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
.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); } } } }
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
.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.
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.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.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
.