diff --git a/android-project/app/proguard-rules.pro b/android-project/app/proguard-rules.pro index a13768966c..077024606e 100644 --- a/android-project/app/proguard-rules.pro +++ b/android-project/app/proguard-rules.pro @@ -69,7 +69,7 @@ -keep,includedescriptorclasses,allowoptimization class org.libsdl.app.SDLControllerManager { void joystickSetSensorsEnabled(int, boolean); - void pollInputDevices(); + void detectDevices(); void joystickSetLED(int, int, int, int); void pollHapticDevices(); void hapticRun(int, float, int); diff --git a/android-project/app/src/main/java/org/libsdl/app/SDLActivity.java b/android-project/app/src/main/java/org/libsdl/app/SDLActivity.java index f382854023..4edeecc33c 100644 --- a/android-project/app/src/main/java/org/libsdl/app/SDLActivity.java +++ b/android-project/app/src/main/java/org/libsdl/app/SDLActivity.java @@ -465,6 +465,8 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh mSingleton = this; SDL.setContext(this); + SDLControllerManager.initializeDeviceListener(); + mClipboardHandler = new SDLClipboardHandler(); mHIDDeviceManager = HIDDeviceManager.acquire(this); @@ -545,7 +547,7 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh if (mHIDDeviceManager != null) { mHIDDeviceManager.setFrozen(true); - } + } if (!mHasMultiWindow) { pauseNativeThread(); @@ -559,7 +561,7 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh if (mHIDDeviceManager != null) { mHIDDeviceManager.setFrozen(false); - } + } if (!mHasMultiWindow) { resumeNativeThread(); @@ -638,7 +640,7 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh if (hasFocus || !SDLActivity.nativeGetHintBoolean("SDL_JOYSTICK_ALLOW_BACKGROUND_EVENTS", false)) { if (mHIDDeviceManager != null) { mHIDDeviceManager.setFrozen(!hasFocus); - } + } } if (SDLActivity.mBrokenLibraries) { diff --git a/android-project/app/src/main/java/org/libsdl/app/SDLControllerManager.java b/android-project/app/src/main/java/org/libsdl/app/SDLControllerManager.java index 8681d050b7..5d6f64315e 100644 --- a/android-project/app/src/main/java/org/libsdl/app/SDLControllerManager.java +++ b/android-project/app/src/main/java/org/libsdl/app/SDLControllerManager.java @@ -6,6 +6,8 @@ import java.util.Comparator; import java.util.List; import android.content.Context; +import android.hardware.input.InputManager; +import android.hardware.input.InputManager.InputDeviceListener; import android.hardware.lights.Light; import android.hardware.lights.LightsRequest; import android.hardware.lights.LightsManager; @@ -68,6 +70,13 @@ public class SDLControllerManager } } + static void initializeDeviceListener() { + // TODO need SDL context here + InputManager im = (InputManager) SDL.getContext().getSystemService(Context.INPUT_SERVICE); + im.registerInputDeviceListener(new SDLDeviceListener(), null); + } + + // Joystick glue code, just a series of stubs that redirect to the SDLJoystickHandler instance static public boolean handleJoystickMotionEvent(MotionEvent event) { return mJoystickHandler.handleMotionEvent(event); @@ -76,8 +85,8 @@ public class SDLControllerManager /** * This method is called by SDL using JNI. */ - static void pollInputDevices() { - mJoystickHandler.pollInputDevices(); + static void detectDevices() { + mJoystickHandler.detectDevices(); } /** @@ -151,7 +160,27 @@ public class SDLControllerManager ((sources & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD) ); } +} + +class SDLDeviceListener implements InputDeviceListener +{ + @Override + public void onInputDeviceAdded(int deviceId) { + if (SDLControllerManager.isDeviceSDLJoystick(deviceId)) { + SDLControllerManager.mJoystickHandler.deviceAdded(deviceId); + } + } + + @Override + public void onInputDeviceChanged(int deviceId) { + } + + @Override + public void onInputDeviceRemoved(int deviceId) { + // InputDevice.getDevice(deviceId) returns NULL. so we cannot check device type + SDLControllerManager.mJoystickHandler.deviceRemoved(deviceId); + } } @@ -228,16 +257,21 @@ class SDLJoystickHandler { /** * Handles adding and removing of input devices. */ - synchronized void pollInputDevices() { - int[] deviceIds = InputDevice.getDeviceIds(); + synchronized void detectDevices() { + int[] deviceIds = InputDevice.getDeviceIds(); + for (int device_id : deviceIds) { + if (SDLControllerManager.isDeviceSDLJoystick(device_id)) { + deviceAdded(device_id); + } + } + } - for (int device_id : deviceIds) { - if (SDLControllerManager.isDeviceSDLJoystick(device_id)) { - SDLJoystick joystick = getJoystick(device_id); - if (joystick == null) { - InputDevice joystickDevice = InputDevice.getDevice(device_id); - joystick = new SDLJoystick(); - joystick.device_id = device_id; + void deviceAdded(int device_id) { + SDLJoystick joystick = getJoystick(device_id); + if (joystick == null) { + InputDevice joystickDevice = InputDevice.getDevice(device_id); + joystick = new SDLJoystick(); + joystick.device_id = device_id; joystick.name = joystickDevice.getName(); joystick.desc = getJoystickDescriptor(joystickDevice); joystick.axes = new ArrayList(); @@ -299,44 +333,27 @@ class SDLJoystickHandler { getButtonMask(joystickDevice), joystick.axes.size(), getAxisMask(joystick.axes), joystick.hats.size()/2, can_rumble, has_rgb_led, has_accelerometer, has_gyroscope); } - } - } - /* Check removed devices */ - ArrayList removedDevices = null; - for (SDLJoystick joystick : mJoysticks) { - int device_id = joystick.device_id; - int i; - for (i = 0; i < deviceIds.length; i++) { - if (device_id == deviceIds[i]) break; - } - if (i == deviceIds.length) { - if (removedDevices == null) { - removedDevices = new ArrayList(); - } - removedDevices.add(device_id); - } - } + } + + void deviceRemoved(int device_id) { + for (int i = 0; i < mJoysticks.size(); i++) { + if (mJoysticks.get(i).device_id == device_id) { - if (removedDevices != null) { - for (int device_id : removedDevices) { SDLControllerManager.nativeRemoveJoystick(device_id); - for (int i = 0; i < mJoysticks.size(); i++) { - if (mJoysticks.get(i).device_id == device_id) { - if (Build.VERSION.SDK_INT >= 31 /* Android 12.0 (S) */) { - if (mJoysticks.get(i).lightsSession != null) { - try { - mJoysticks.get(i).lightsSession.close(); - } catch (Exception e) { - // Session may already be unregistered when device disconnects - } - mJoysticks.get(i).lightsSession = null; - } + + if (Build.VERSION.SDK_INT >= 31 /* Android 12.0 (S) */) { + if (mJoysticks.get(i).lightsSession != null) { + try { + mJoysticks.get(i).lightsSession.close(); + } catch (Exception e) { + // Session may already be unregistered when device disconnects } - mJoysticks.remove(i); - break; + mJoysticks.get(i).lightsSession = null; } } + mJoysticks.remove(i); + break; } } } diff --git a/src/core/android/SDL_android.c b/src/core/android/SDL_android.c index c14f425d86..84b1f0c6c2 100644 --- a/src/core/android/SDL_android.c +++ b/src/core/android/SDL_android.c @@ -407,7 +407,7 @@ static jmethodID midAudioSetThreadPriority; static jclass mControllerManagerClass; // method signatures -static jmethodID midPollInputDevices; +static jmethodID midDetectDevices; static jmethodID midJoystickSetLED; static jmethodID midJoystickSetSensorsEnabled; static jmethodID midPollHapticDevices; @@ -752,8 +752,8 @@ JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeSetupJNI)(JNIEnv *env mControllerManagerClass = (jclass)((*env)->NewGlobalRef(env, cls)); - midPollInputDevices = (*env)->GetStaticMethodID(env, mControllerManagerClass, - "pollInputDevices", "()V"); + midDetectDevices = (*env)->GetStaticMethodID(env, mControllerManagerClass, + "detectDevices", "()V"); midJoystickSetLED = (*env)->GetStaticMethodID(env, mControllerManagerClass, "joystickSetLED", "(IIII)V"); midJoystickSetSensorsEnabled = (*env)->GetStaticMethodID(env, mControllerManagerClass, @@ -767,7 +767,7 @@ JNIEXPORT void JNICALL SDL_JAVA_CONTROLLER_INTERFACE(nativeSetupJNI)(JNIEnv *env midHapticStop = (*env)->GetStaticMethodID(env, mControllerManagerClass, "hapticStop", "(I)V"); - if (!midPollInputDevices || !midJoystickSetLED || !midJoystickSetSensorsEnabled || !midPollHapticDevices || !midHapticRun || !midHapticRumble || !midHapticStop) { + if (!midDetectDevices || !midJoystickSetLED || !midJoystickSetSensorsEnabled || !midPollHapticDevices || !midHapticRun || !midHapticRumble || !midHapticStop) { __android_log_print(ANDROID_LOG_WARN, "SDL", "Missing some Java callbacks, do you have the latest version of SDLControllerManager.java?"); } @@ -2639,10 +2639,10 @@ void Android_JNI_InitTouch(void) (*env)->CallStaticVoidMethod(env, mActivityClass, midInitTouch); } -void Android_JNI_PollInputDevices(void) +void Android_JNI_DetectDevices(void) { JNIEnv *env = Android_JNI_GetEnv(); - (*env)->CallStaticVoidMethod(env, mControllerManagerClass, midPollInputDevices); + (*env)->CallStaticVoidMethod(env, mControllerManagerClass, midDetectDevices); } void Android_JNI_JoystickSetLED(int device_id, int red, int green, int blue) diff --git a/src/core/android/SDL_android.h b/src/core/android/SDL_android.h index a1bc1c7c31..4ce234c696 100644 --- a/src/core/android/SDL_android.h +++ b/src/core/android/SDL_android.h @@ -102,7 +102,7 @@ bool Android_JNI_HasClipboardText(void); int Android_JNI_GetPowerInfo(int *plugged, int *charged, int *battery, int *seconds, int *percent); // Joystick support -void Android_JNI_PollInputDevices(void); +void Android_JNI_DetectDevices(void); void Android_JNI_JoystickSetLED(int device_id, int red, int green, int blue); void Android_JNI_JoystickSetSensorsEnabled(int device_id, bool enabled); diff --git a/src/joystick/android/SDL_sysjoystick.c b/src/joystick/android/SDL_sysjoystick.c index 16992e8255..fc3368ed78 100644 --- a/src/joystick/android/SDL_sysjoystick.c +++ b/src/joystick/android/SDL_sysjoystick.c @@ -535,11 +535,9 @@ done: SDL_UnlockJoysticks(); } -static void ANDROID_JoystickDetect(void); - static bool ANDROID_JoystickInit(void) { - ANDROID_JoystickDetect(); + Android_JNI_DetectDevices(); return true; } @@ -550,16 +548,9 @@ static int ANDROID_JoystickGetCount(void) static void ANDROID_JoystickDetect(void) { - /* Support for device connect/disconnect is API >= 16 only, - * so we poll every three seconds + /* Support for device connect/disconnect is implemented using InputDeviceListener * Ref: http://developer.android.com/reference/android/hardware/input/InputManager.InputDeviceListener.html */ - static Uint64 timeout = 0; - Uint64 now = SDL_GetTicks(); - if (!timeout || now >= timeout) { - timeout = now + 3000; - Android_JNI_PollInputDevices(); - } } static bool ANDROID_JoystickIsDevicePresent(Uint16 vendor_id, Uint16 product_id, Uint16 version, const char *name) @@ -596,16 +587,6 @@ static SDL_joylist_item *JoystickByDeviceId(int device_id) item = item->next; } - // Joystick not found, try adding it - ANDROID_JoystickDetect(); - - while (item) { - if (item->device_id == device_id) { - return item; - } - item = item->next; - } - return NULL; }