Skip to content

fix(android): Correct gamepad device detection and stop breaking keyboard input - #127

Open
spydon wants to merge 1 commit into
mainfrom
fix/android-keyboard-gamepad-detection
Open

fix(android): Correct gamepad device detection and stop breaking keyboard input#127
spydon wants to merge 1 commit into
mainfrom
fix/android-keyboard-gamepad-detection

Conversation

@spydon

@spydon spydon commented Aug 14, 2026

Copy link
Copy Markdown
Member

Description

Fixes the Android device filter so bluetooth keyboards are no longer registered as gamepads (which caused all their key events to be consumed, breaking keyboard input app-wide), and so real controllers that expose a keyboard profile are no longer rejected.

Root causes

  • The alphabetic-keyboard exclusion added in fix: Bluetooth keyboards should not be recognized as gamepads #66 never worked for its target case: due to Kotlin operator precedence (&& binds tighter than ||), the check parsed as GAMEPAD || (JOYSTICK && notAlphabetic), so a keyboard claiming SOURCE_GAMEPAD still passed the filter. Since EventListener.onKeyEvent always returns true, every keystroke from such a keyboard was consumed and never reached Flutter.
  • The flip side: controllers that report KEYBOARD_TYPE_ALPHABETIC (common for 8BitDo and some bluetooth controllers) but only claim SOURCE_JOYSTICK were rejected entirely, so they produced no gamepad events at all.

Changes

  • GamepadsCompatibleActivity.isGamepadsInputDevice: rewritten with explicit logic. A device must have a gamepad or joystick source, and keyboards are distinguished from controllers by whether the device reports any joystick-source motion ranges (sticks, hats, triggers) instead of by keyboard type alone. This fixes both directions of the misclassification.
  • GamepadsAndroidPlugin: the hard as GamepadsCompatibleActivity cast is now a safe cast with a descriptive Log.e pointing to the README setup, so apps whose MainActivity is missing the boilerplate no longer crash at startup with an opaque ClassCastException. listGamepads returns an empty list instead of throwing in that state.
  • README (package copies are symlinks): the example dispatchGenericMotionEvent now falls back to super.dispatchGenericMotionEvent instead of returning false, which previously swallowed all generic motion events (mouse hover, scroll wheel, trackpad) for the whole app.

Note: the unconditional consumption of key events from registered gamepads (dpad/back navigation, #61) is intentionally left out of scope.

Fixes #70

Testing instructions

Verified that both the plugin and the README boilerplate compile by building a scratch Flutter app against the local gamepads_android, once with a plain FlutterActivity (graceful-degradation path) and once with the README MainActivity.

To verify on hardware:

  1. Run an app using gamepads with the README MainActivity setup on an Android device.
  2. Connect a bluetooth keyboard: typing should reach the app (previously dead if the keyboard claimed a gamepad source), and it should not appear in Gamepads.list().
  3. Connect a controller (ideally an 8BitDo or another controller that exposes a keyboard profile): it should appear in Gamepads.list() and emit button and axis events.
  4. With a mouse connected, scroll and hover should still work in the app.
  5. Remove the GamepadsCompatibleActivity implementation from MainActivity: the app should start normally and log an error explaining the missing setup instead of crashing.

…oard input

The alphabetic-keyboard exclusion added in #66 only applied to the
SOURCE_JOYSTICK branch due to operator precedence, so bluetooth
keyboards claiming SOURCE_GAMEPAD were still registered as gamepads
and had all their key events consumed. Conversely, controllers that
expose an alphabetic keyboard profile but only claim SOURCE_JOYSTICK
were rejected entirely.

Devices are now classified by whether they report joystick-source
motion ranges, which distinguishes keyboards from real controllers
in both directions.

Also degrade gracefully with a descriptive log instead of a
ClassCastException when the host Activity does not implement
GamepadsCompatibleActivity, and fix the README boilerplate so
dispatchGenericMotionEvent falls back to super instead of swallowing
all generic motion events.

Fixes #70
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Android] - Not Working

1 participant