diff --git a/README.md b/README.md index 738edce08..875707b92 100644 --- a/README.md +++ b/README.md @@ -213,9 +213,12 @@ class MainActivity: FlutterActivity(), GamepadsCompatibleActivity { var motionListener: ((MotionEvent) -> Boolean)? = null override fun dispatchGenericMotionEvent(motionEvent: MotionEvent): Boolean { - return motionListener?.invoke(motionEvent) ?: false + if (motionListener?.invoke(motionEvent) == true) { + return true + } + return super.dispatchGenericMotionEvent(motionEvent) } - + override fun dispatchKeyEvent(keyEvent: KeyEvent): Boolean { if (keyListener?.invoke(keyEvent) == true) { return true diff --git a/packages/gamepads_android/android/src/main/kotlin/org/flame_engine/gamepads_android/GamepadsAndroidPlugin.kt b/packages/gamepads_android/android/src/main/kotlin/org/flame_engine/gamepads_android/GamepadsAndroidPlugin.kt index 24dc85541..61e4c9312 100644 --- a/packages/gamepads_android/android/src/main/kotlin/org/flame_engine/gamepads_android/GamepadsAndroidPlugin.kt +++ b/packages/gamepads_android/android/src/main/kotlin/org/flame_engine/gamepads_android/GamepadsAndroidPlugin.kt @@ -35,6 +35,9 @@ class GamepadsAndroidPlugin: FlutterPlugin, MethodCallHandler, ActivityAware { private var genericMotionListener: View.OnGenericMotionListener? = null private fun listGamepads(): List> { + if (!::devices.isInitialized) { + return emptyList() + } return devices.getDevices().map { device -> mapOf( "id" to device.key.toString(), @@ -67,7 +70,16 @@ class GamepadsAndroidPlugin: FlutterPlugin, MethodCallHandler, ActivityAware { } fun onAttachedToActivityShared(activity: Activity) { - val compatibleActivity = activity as GamepadsCompatibleActivity + val compatibleActivity = activity as? GamepadsCompatibleActivity + if (compatibleActivity == null) { + Log.e( + TAG, + "Gamepad support is disabled: ${activity.javaClass.name} does not " + + "implement GamepadsCompatibleActivity. See the 'Android Integration' " + + "section of the gamepads README for the required MainActivity setup." + ) + return + } devices = DeviceListener { compatibleActivity.isGamepadsInputDevice(it) } events = EventListener() compatibleActivity.registerInputDeviceListener(devices, handler = null) diff --git a/packages/gamepads_android/android/src/main/kotlin/org/flame_engine/gamepads_android/GamepadsCompatibleActivity.kt b/packages/gamepads_android/android/src/main/kotlin/org/flame_engine/gamepads_android/GamepadsCompatibleActivity.kt index d34dc49a2..51a7d0cc5 100644 --- a/packages/gamepads_android/android/src/main/kotlin/org/flame_engine/gamepads_android/GamepadsCompatibleActivity.kt +++ b/packages/gamepads_android/android/src/main/kotlin/org/flame_engine/gamepads_android/GamepadsCompatibleActivity.kt @@ -8,10 +8,22 @@ import android.view.MotionEvent interface GamepadsCompatibleActivity { fun isGamepadsInputDevice(device: InputDevice): Boolean { - return device.sources and InputDevice.SOURCE_GAMEPAD == InputDevice.SOURCE_GAMEPAD - || device.sources and InputDevice.SOURCE_JOYSTICK == InputDevice.SOURCE_JOYSTICK - // Some bluetooth keyboards are identified as GamePad. Check if it is ALPHABETIC keyboard. - && device.keyboardType != InputDevice.KEYBOARD_TYPE_ALPHABETIC + val hasGamepadSource = + device.sources and InputDevice.SOURCE_GAMEPAD == InputDevice.SOURCE_GAMEPAD || + device.sources and InputDevice.SOURCE_JOYSTICK == InputDevice.SOURCE_JOYSTICK + if (!hasGamepadSource) { + return false + } + // Some bluetooth keyboards claim a gamepad source, while some real + // controllers expose an alphabetic keyboard profile, so keyboard type + // alone cannot tell them apart. Real controllers report joystick axes + // (sticks, hats or triggers) and keyboards do not. + val hasJoystickAxes = device.motionRanges.any { + it.source and InputDevice.SOURCE_JOYSTICK == InputDevice.SOURCE_JOYSTICK + } + val isKeyboard = + device.keyboardType == InputDevice.KEYBOARD_TYPE_ALPHABETIC && !hasJoystickAxes + return !isKeyboard } fun registerInputDeviceListener(listener: InputManager.InputDeviceListener, handler: Handler?)