Time for action – handling keyboard, D-Pad, and trackball events natively

Let's extend our new Input system with more event types:

  1. Open 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
  2. Update the method 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;
    }
    ...
  3. Modify the 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
  4. In 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;
    }
    ...
  5. Similarly, process trackball events in a new method 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;
            }
    ...
  6. When using a trackball that way, the ship moves until a "counter-movement" (for example, requesting to go to the right when going left) or action button is pressed (the last 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;
    }

What just happened?

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:

Method

Description

AKeyEvent_getAction()

Indicates whether the button is down (AKEY_EVENT_ACTION_DOWN) or released (AKEY_EVENT_ACTION_UP). Note that multiple key actions can be emitted in batch (AKEY_EVENT_ACTION_MULTIPLE).

AKeyEvent_getKeyCode()

To retrieve the actual button being pressed (defined in android/keycodes.h), for example, AKEYCODE_DPAD_LEFT for the left button.

AKeyEvent_getFlags()

Key events can be associated with one or more flags that give various kinds of information on the event, such as AKEY_EVENT_LONG_PRESS, AKEY_EVENT_FLAG_SOFT_KEYBOARD for the event originated from an emulated keyboard.

AKeyEvent_getScanCode()

Is similar to a key code except that this is the raw key ID, dependent and different from device to device.

AKeyEvent_getMetaState()

Meta states are flags that indicate whether some modifier keys, such as Alt or Shift, are pressed simultaneously (for example, AMETA_SHIFT_ON, AMETA_NONE, and so on).

AKeyEvent_getRepeatCount()

Indicates how many times the button event occurred, usually when you leave the button down.

AKeyEvent_getDownTime()

To know when a button was pressed.

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:

AMotionEvent_getAction()

To know whether an event represents a move action (as opposed to a press action).

AMotionEvent_getX()

AMotionEvent_getY()

To get trackball movement.

AKeyEvent_getDownTime()

To know whether the trackball is pressed (such as the D-Pad action button). Currently, most trackballs use an all-or-nothing pressure to indicate the press event.

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.

Tip

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.

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

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