diff --git a/android-project/app/proguard-rules.pro b/android-project/app/proguard-rules.pro index a13768966c..975554b8a5 100644 --- a/android-project/app/proguard-rules.pro +++ b/android-project/app/proguard-rules.pro @@ -69,9 +69,9 @@ -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 detectHapticDevices(); void hapticRun(int, float, int); void hapticRumble(int, float, float, int); void hapticStop(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..c86ba69c19 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,12 @@ public class SDLControllerManager } } + static void initializeDeviceListener() { + 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 +84,8 @@ public class SDLControllerManager /** * This method is called by SDL using JNI. */ - static void pollInputDevices() { - mJoystickHandler.pollInputDevices(); + static void detectDevices() { + mJoystickHandler.detectDevices(); } /** @@ -97,8 +105,8 @@ public class SDLControllerManager /** * This method is called by SDL using JNI. */ - static void pollHapticDevices() { - mHapticHandler.pollHapticDevices(); + static void detectHapticDevices() { + mHapticHandler.detectHapticDevices(); } /** @@ -151,7 +159,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 +256,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 +332,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; } } } @@ -701,7 +717,7 @@ class SDLHapticHandler { } } - synchronized void pollHapticDevices() { + synchronized void detectHapticDevices() { final int deviceId_VIBRATOR_SERVICE = 999999; boolean hasVibratorService = false; diff --git a/src/core/android/SDL_android.c b/src/core/android/SDL_android.c index c14f425d86..ba11cb44a2 100644 --- a/src/core/android/SDL_android.c +++ b/src/core/android/SDL_android.c @@ -407,10 +407,10 @@ static jmethodID midAudioSetThreadPriority; static jclass mControllerManagerClass; // method signatures -static jmethodID midPollInputDevices; +static jmethodID midDetectDevices; static jmethodID midJoystickSetLED; static jmethodID midJoystickSetSensorsEnabled; -static jmethodID midPollHapticDevices; +static jmethodID midDetectHapticDevices; static jmethodID midHapticRun; static jmethodID midHapticRumble; static jmethodID midHapticStop; @@ -752,14 +752,14 @@ 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, "joystickSetSensorsEnabled", "(IZ)V"); - midPollHapticDevices = (*env)->GetStaticMethodID(env, mControllerManagerClass, - "pollHapticDevices", "()V"); + midDetectHapticDevices = (*env)->GetStaticMethodID(env, mControllerManagerClass, + "detectHapticDevices", "()V"); midHapticRun = (*env)->GetStaticMethodID(env, mControllerManagerClass, "hapticRun", "(IFI)V"); midHapticRumble = (*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 || !midDetectHapticDevices || !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) @@ -2657,10 +2657,10 @@ void Android_JNI_JoystickSetSensorsEnabled(int device_id, bool enabled) (*env)->CallStaticVoidMethod(env, mControllerManagerClass, midJoystickSetSensorsEnabled, device_id, (enabled == 1)); } -void Android_JNI_PollHapticDevices(void) +void Android_JNI_DetectHapticDevices(void) { JNIEnv *env = Android_JNI_GetEnv(); - (*env)->CallStaticVoidMethod(env, mControllerManagerClass, midPollHapticDevices); + (*env)->CallStaticVoidMethod(env, mControllerManagerClass, midDetectHapticDevices); } void Android_JNI_HapticRun(int device_id, float intensity, int length) diff --git a/src/core/android/SDL_android.h b/src/core/android/SDL_android.h index a1bc1c7c31..fd16927595 100644 --- a/src/core/android/SDL_android.h +++ b/src/core/android/SDL_android.h @@ -102,12 +102,12 @@ 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); // Haptic support -void Android_JNI_PollHapticDevices(void); +void Android_JNI_DetectHapticDevices(void); void Android_JNI_HapticRun(int device_id, float intensity, int length); void Android_JNI_HapticRumble(int device_id, float low_frequency_intensity, float high_frequency_intensity, int length); void Android_JNI_HapticStop(int device_id); diff --git a/src/haptic/android/SDL_syshaptic.c b/src/haptic/android/SDL_syshaptic.c index 760854e9f1..da36eb9595 100644 --- a/src/haptic/android/SDL_syshaptic.c +++ b/src/haptic/android/SDL_syshaptic.c @@ -41,8 +41,7 @@ static int numhaptics = 0; bool SDL_SYS_HapticInit(void) { - Android_JNI_PollHapticDevices(); - + Android_JNI_DetectHapticDevices(); return true; } 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; }