5

I need to have two different behaviors, one for D-pad and another for the analog Joystick (on the same gamepad).

The problem is that on the onGenericMotionEvent callback, both have the same information on the MotionEvent and I am not able to distinguish them.

// d-pad
MotionEvent { action=ACTION_MOVE, id[0]=0, x[0]=-1.5259255E-5, y[0]=-1.5259255E-5, toolType[0]=TOOL_TYPE_UNKNOWN, buttonState=0, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=1, historySize=0, eventTime=151637936, downTime=0, deviceId=5, source=0x1000010 }

// analog joystick
MotionEvent { action=ACTION_MOVE, id[0]=0, x[0]=0.64507514, y[0]=0.710811, toolType[0]=TOOL_TYPE_UNKNOWN, buttonState=0, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=1, historySize=0, eventTime=151650802, downTime=0, deviceId=5, source=0x1000010 }

Is it possible to identify which kind of input is being used? How?

thiagolr
  • 6,909
  • 6
  • 44
  • 64
  • Did you ever found a solution? I have the same problem but it seems there is no way to distinguish between these two input methods. Would be really annoying... – grill2010 Aug 12 '18 at 19:46
  • Seems like DS4 controllers do not claim DPAD source when you use the DPAD. You get a Joystick source event with the Hat values set to min/max depending on what you pressed. To support DS4 anyway it looks like you need to query X and XHat, see which is larger (abs) and use that. – Halsafar Aug 01 '20 at 18:34

3 Answers3

4

I ran into this same issue, and I had to dig through this helpful Git user's project to figure out how he did it. The way you differentiate between the different joysticks (and the D-pad) is to use each direction's specific axis.

If you read the Android documentation's page very carefully (I didn't pick up on it), it does show how you can differentiate between the various joysticks and their directions:

Android documentation for different axis/labels

This image shows that the left joystick uses axes AXIS_X and AXIS_Y, whereas the right joystick uses AXIS_Z and and AXIS_RZ. For the D-pad, I used AXIS_HAT_X and AXIS_HAT_Y. The following snippet from my code (in Kotlin) shows how you can access each of these individually:

Note: I also had my seek bars set to a range of 0-100, which is why I have the conversion math at the bottom of processJoystickInput().

private fun processJoystickInput(event: MotionEvent, historyPos: Int) {

    val inputDevice = event.device

    val newJoystickValues = floatArrayOf(
            getCenteredAxis(event, inputDevice, MotionEvent.AXIS_X, historyPos),
            getCenteredAxis(event, inputDevice, MotionEvent.AXIS_Y, historyPos),
            getCenteredAxis(event, inputDevice, MotionEvent.AXIS_Z, historyPos),
            getCenteredAxis(event, inputDevice, MotionEvent.AXIS_RZ, historyPos),
            getCenteredAxis(event, inputDevice, MotionEvent.AXIS_HAT_X, historyPos),
            getCenteredAxis(event, inputDevice, MotionEvent.AXIS_HAT_Y, historyPos))

    // Update based on the new x and y values
    val throttleSeekBar = findViewById<SeekBar>(R.id.throttle_seekBar)
    val yawSeekBar = findViewById<SeekBar>(R.id.yaw_seekBar)
    val pitchSeekBar = findViewById<SeekBar>(R.id.pitch_seekBar)
    val rollSeekBar = findViewById<SeekBar>(R.id.roll_seekBar)
    val dpadXSeekBar = findViewById<SeekBar>(R.id.dpadX_seekBar)
    val dpadYSeekBar = findViewById<SeekBar>(R.id.dpadY_seekBar)

    // Convert the float range (-1.00 to 1.00) to Int (0 to 100)
    yawSeekBar.progress = ((newJoystickValues[0] + 1) * 50).toInt()
    throttleSeekBar.progress = ((newJoystickValues[1] + 1) * 50).toInt()
    rollSeekBar.progress = ((newJoystickValues[2] + 1) * 50).toInt()
    pitchSeekBar.progress = ((newJoystickValues[3] + 1) * 50).toInt()
    dpadXSeekBar.progress = ((newJoystickValues[4] + 1) * 50).toInt()
    dpadYSeekBar.progress = ((newJoystickValues[5] + 1) * 50).toInt()
}

override fun onGenericMotionEvent(event: MotionEvent): Boolean {
    // Check that the event came from a game controller

    return if (event.source and(InputDevice.SOURCE_JOYSTICK) == InputDevice.SOURCE_JOYSTICK
            && event.action == MotionEvent.ACTION_MOVE) {

        // Process the movements starting from the
        // earliest historical position in the batch
        (0 until event.historySize).forEach { i ->
            // Process the event at historical position i
            processJoystickInput(event, i)
        }

        // Process the current movement sample in the batch (position -1)
        processJoystickInput(event, -1)
        true
    } else {
        super.onGenericMotionEvent(event)
    }
}
ConcernedHobbit
  • 764
  • 1
  • 8
  • 17
0

Looking into your provided information, they look like from the same source, which is Joystick (0x1000010). You can check these information on Input Device object.

The following information were from Handling Controller Actions:

To verify that a connected input device is a game controller, call getSources() to obtain a combined bit field of input source types supported on that device.

A source type of SOURCE_GAMEPAD indicates that the input device has gamepad buttons (for example, BUTTON_A). Note that this source type does not strictly indicate if the game controller has D-pad buttons, although most gamepads typically have directional controls.

A source type of SOURCE_DPAD indicates that the input device has D-pad buttons (for example, DPAD_UP).

A source type of SOURCE_JOYSTICK indicates that the input device has analog control sticks (for example, a joystick that records movements along AXIS_X and AXIS_Y).

You may also want to check Supporting Multiple Controller Input.

Community
  • 1
  • 1
LexJulienne
  • 160
  • 7
  • I need to distinguish the two different inputs (dpad and analog) from the same joystick source, not two different sources – thiagolr Jan 04 '16 at 15:01
0

From the Android docs (https://developer.android.com/training/game-controllers/controller-input):

Android reports D-pad UP and DOWN presses as AXIS_HAT_Y events with a range from -1.0 (up) to 1.0 (down), and D-pad LEFT or RIGHT presses as AXIS_HAT_X events with a range from -1.0 (left) to 1.0 (right).

Some controllers instead report D-pad presses with a key code. If your game cares about D-pad presses, you should treat the hat axis events and the D-pad key codes as the same input events, as recommended in table 2.

The two controllers I've worked with (NVIDIA SHIELD BlueTooth and Microsoft XBOX360 wired) both generate AXIS_HAT_* movement events.

ehacinom
  • 8,070
  • 7
  • 43
  • 65
  • DS4 as well. DPad is always a MotionEvent, source=Joystick, DPad has to be read from XHat/YHat values. – Halsafar Aug 01 '20 at 18:36