Let's extend our new Input system with more event types:
jni/InputHandler.hpp
and add the keyboard and trackball event handlers:#ifndef _PACKT_INPUTHANDLER_HPP_ #define _PACKT_INPUTHANDLER_HPP_ #include <android/input.h> class InputHandler { public: virtual ~InputHandler() {}; virtual bool onTouchEvent(AInputEvent* pEvent) = 0; virtual bool onKeyboardEvent(AInputEvent* pEvent) = 0; virtual bool onTrackballEvent(AInputEvent* pEvent) = 0; }; #endif
processInputEvent()
inside the existing file jni/EventLoop.cpp
to redirect the keyboard and trackball events to InputHandler
.Trackballs and touch events are assimilated to motion events and can be discriminated according to their source. On the opposite side, key events are discriminated according to their type. Indeed, there exists two dedicated APIs for MotionEvents
(the same for trackballs and touch events) and for KeyEvents
(identical for keyboard, D-Pad, and so on):
... int32_t EventLoop::processInputEvent(AInputEvent* pEvent) { if (!mEnabled) return 0; int32_t eventType = AInputEvent_getType(pEvent); switch (eventType) { case AINPUT_EVENT_TYPE_MOTION: switch (AInputEvent_getSource(pEvent)) { case AINPUT_SOURCE_TOUCHSCREEN: return mInputHandler.onTouchEvent(pEvent); break; case AINPUT_SOURCE_TRACKBALL: return mInputHandler.onTrackballEvent(pEvent); break; } break; case AINPUT_EVENT_TYPE_KEY: return mInputHandler.onKeyboardEvent(pEvent); break; } return 0; } ...
jni/InputManager.hpp
file to override these new methods:... class InputManager : public InputHandler { ... protected: bool onTouchEvent(AInputEvent* pEvent); bool onKeyboardEvent(AInputEvent* pEvent); bool onTrackballEvent(AInputEvent* pEvent); ... }; #endif
jni/InputManager.cpp
, process the keyboard events in onKeyboardEvent()
using:AKeyEvent_getAction()
to get the event type (that is, pressed or not).AKeyEvent_getKeyCode()
to get the button identity.In the following code, when left, right, up, or down buttons are pressed, InputManager
calculates the direction and saves it into mDirectionX
and mDirectionY
. The movement starts when the button is down and stops when it is up.
Return true
when the key has been consumed and false
when it has not. Indeed, if a user has pressed, for example, the back button (AKEYCODE_BACK
) or volume buttons (AKEYCODE_VOLUME_UP
, AKEYCODE_VOLUME_DOWN
), then we let the system react appropriately for us:
... bool InputManager::onKeyboardEvent(AInputEvent* pEvent) { static const float ORTHOGONAL_MOVE = 1.0f; if (AKeyEvent_getAction(pEvent) == AKEY_EVENT_ACTION_DOWN) { switch (AKeyEvent_getKeyCode(pEvent)) { case AKEYCODE_DPAD_LEFT: mDirectionX = -ORTHOGONAL_MOVE; return true; case AKEYCODE_DPAD_RIGHT: mDirectionX = ORTHOGONAL_MOVE; return true; case AKEYCODE_DPAD_DOWN: mDirectionY = -ORTHOGONAL_MOVE; return true; case AKEYCODE_DPAD_UP: mDirectionY = ORTHOGONAL_MOVE; return true; } } else { switch (AKeyEvent_getKeyCode(pEvent)) { case AKEYCODE_DPAD_LEFT: case AKEYCODE_DPAD_RIGHT: mDirectionX = 0.0f; return true; case AKEYCODE_DPAD_DOWN: case AKEYCODE_DPAD_UP: mDirectionY = 0.0f; return true; } } return false; } ...
onTrackballEvent()
. Retrieve the trackball magnitude with AMotionEvent_getX()
and AMotionEvent_getY()
. Because some trackballs do not offer a gradated magnitude, the movements are quantified with plain constants. The possible noise is ignored with an arbitrary trigger threshold:... bool InputManager::onTrackballEvent(AInputEvent* pEvent) { static const float ORTHOGONAL_MOVE = 1.0f; static const float DIAGONAL_MOVE = 0.707f; static const float THRESHOLD = (1/100.0f); if (AMotionEvent_getAction(pEvent) == AMOTION_EVENT_ACTION_MOVE) { float directionX = AMotionEvent_getX(pEvent, 0); float directionY = AMotionEvent_getY(pEvent, 0); float horizontal, vertical; if (directionX < -THRESHOLD) { if (directionY < -THRESHOLD) { horizontal = -DIAGONAL_MOVE; vertical = DIAGONAL_MOVE; } else if (directionY > THRESHOLD) { horizontal = -DIAGONAL_MOVE; vertical = -DIAGONAL_MOVE; } else { horizontal = -ORTHOGONAL_MOVE; vertical = 0.0f; } } else if (directionX > THRESHOLD) { if (directionY < -THRESHOLD) { horizontal = DIAGONAL_MOVE; vertical = DIAGONAL_MOVE; } else if (directionY > THRESHOLD) { horizontal = DIAGONAL_MOVE; vertical = -DIAGONAL_MOVE; } else { horizontal = ORTHOGONAL_MOVE; vertical = 0.0f; } } else if (directionY < -THRESHOLD) { horizontal = 0.0f; vertical = ORTHOGONAL_MOVE; } else if (directionY > THRESHOLD) { horizontal = 0.0f; vertical = -ORTHOGONAL_MOVE; } ...
else
section):... // Ends movement if there is a counter movement. if ((horizontal < 0.0f) && (mDirectionX > 0.0f)) { mDirectionX = 0.0f; } else if ((horizontal > 0.0f) && (mDirectionX < 0.0f)) { mDirectionX = 0.0f; } else { mDirectionX = horizontal; } if ((vertical < 0.0f) && (mDirectionY > 0.0f)) { mDirectionY = 0.0f; } else if ((vertical > 0.0f) && (mDirectionY < 0.0f)) { mDirectionY = 0.0f; } else { mDirectionY = vertical; } } else { mDirectionX = 0.0f; mDirectionY = 0.0f; } return true; }
We extended our input system to handle the keyboard, D-Pad, and trackball events. D-Pad can be considered as a keyboard extension and is processed the same way. Indeed, D-Pad and keyboard events are transported in the same structure (AInputEvent
) and handled by the same API (prefixed with AKeyEvent
).
The following table lists the main key event methods:
Although some of them (especially optical ones) behave like a D-Pad, trackballs do not use the same API. Actually, trackballs are handled through the AMotionEvent
API (such as touch events). Of course, some information provided for touch events is not always available on trackballs. The most important functions to look at are as follows:
A tricky point to keep in mind when dealing with trackballs is that no event is generated to indicate that the trackball is not moving. Moreover, trackball events are generated as a "burst", which makes it harder to detect when the movement is finished. There is no easy way to handle this, except using a manual timer and checking regularly that no event has happened for a sufficient amount of time.
Never expect peripherals to behave exactly the same on all phones. Trackballs are a very good example; they can either indicate a direction like an analogical pad or a straight direction like a D-Pad (for example, optical trackballs). There is currently no way to differentiate device characteristics from the available APIs. The only solutions are to either calibrate the device or configure it at runtime or save a kind of device database.
3.133.111.85