From e474718f2cdcc14e18ebcd1ebf7cf3f256fdfb5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Fran=C4=8De=C5=A1kin?= <Matej.Franceskin@gmail.com> Date: Mon, 7 Jan 2019 15:27:43 +0100 Subject: [PATCH 1/6] Make Android Joystick init behave in the same was as for SDL --- src/Joystick/JoystickAndroid.cc | 6 +++--- src/Joystick/JoystickAndroid.h | 6 ++++-- src/Joystick/JoystickManager.cc | 7 +++++-- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/Joystick/JoystickAndroid.cc b/src/Joystick/JoystickAndroid.cc index 7ec59a4..bc9f22b 100644 --- a/src/Joystick/JoystickAndroid.cc +++ b/src/Joystick/JoystickAndroid.cc @@ -76,8 +76,6 @@ QMap<QString, Joystick*> JoystickAndroid::discover(MultiVehicleManager* _multiVe bool joystickFound = false; static QMap<QString, Joystick*> ret; - _initStatic(); //it's enough to run it once, should be in a static constructor - QMutexLocker lock(&m_mutex); QAndroidJniEnvironment env; @@ -194,7 +192,7 @@ uint8_t JoystickAndroid::_getHat(int hat,int i) { } //helper method -void JoystickAndroid::_initStatic() { +bool JoystickAndroid::init() { //this gets list of all possible buttons - this is needed to check how many buttons our gamepad supports //instead of the whole logic below we could have just a simple array of hardcoded int values as these 'should' not change @@ -229,5 +227,7 @@ void JoystickAndroid::_initStatic() { ACTION_DOWN = QAndroidJniObject::getStaticField<jint>("android/view/KeyEvent", "ACTION_DOWN"); ACTION_UP = QAndroidJniObject::getStaticField<jint>("android/view/KeyEvent", "ACTION_UP"); + + return true; } diff --git a/src/Joystick/JoystickAndroid.h b/src/Joystick/JoystickAndroid.h index b50c7f9..78defed 100644 --- a/src/Joystick/JoystickAndroid.h +++ b/src/Joystick/JoystickAndroid.h @@ -16,9 +16,12 @@ class JoystickAndroid : public Joystick, public QtAndroidPrivate::GenericMotionE { public: JoystickAndroid(const QString& name, int axisCount, int buttonCount, int id, MultiVehicleManager* multiVehicleManager); + ~JoystickAndroid(); - static QMap<QString, Joystick*> discover(MultiVehicleManager* _multiVehicleManager); + static bool init(); + + static QMap<QString, Joystick*> discover(MultiVehicleManager* _multiVehicleManager); private: bool handleKeyEvent(jobject event); @@ -37,7 +40,6 @@ private: bool *btnValue; int *axisValue; - static void _initStatic(); static int * _androidBtnList; //list of all possible android buttons static int _androidBtnListCount; diff --git a/src/Joystick/JoystickManager.cc b/src/Joystick/JoystickManager.cc index 001418e..f546609 100644 --- a/src/Joystick/JoystickManager.cc +++ b/src/Joystick/JoystickManager.cc @@ -60,8 +60,11 @@ void JoystickManager::init() { _joystickCheckTimer.start(250); } #elif defined(__android__) - _setActiveJoystickFromSettings(); - //TODO: Investigate Android events for Joystick hot plugging & run _joystickCheckTimer if possible + if (JoystickAndroid::init()) { + _setActiveJoystickFromSettings(); + connect(&_joystickCheckTimer, &QTimer::timeout, this, &JoystickManager::_updateAvailableJoysticks); + _joystickCheckTimer.start(250); + } #endif } From cc9c2de50ad027e7cfeea3fea05e9d4f6e9114ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Fran=C4=8De=C5=A1kin?= <Matej.Franceskin@gmail.com> Date: Tue, 8 Jan 2019 14:15:41 +0100 Subject: [PATCH 2/6] Bugfix: [Android] Joystick is not visible if connected after start of QGC When starting up, if you start the GS before connecting the bluetooth joystick the QGC needs to be closed and restarted to get it to recognize the joystick --- src/Joystick/JoystickAndroid.cc | 22 +++++++++++++++------- src/Joystick/JoystickManager.cc | 33 +++++++++++++++------------------ 2 files changed, 30 insertions(+), 25 deletions(-) diff --git a/src/Joystick/JoystickAndroid.cc b/src/Joystick/JoystickAndroid.cc index bc9f22b..d66f0c0 100644 --- a/src/Joystick/JoystickAndroid.cc +++ b/src/Joystick/JoystickAndroid.cc @@ -73,7 +73,6 @@ JoystickAndroid::~JoystickAndroid() { QMap<QString, Joystick*> JoystickAndroid::discover(MultiVehicleManager* _multiVehicleManager) { - bool joystickFound = false; static QMap<QString, Joystick*> ret; QMutexLocker lock(&m_mutex); @@ -87,27 +86,29 @@ QMap<QString, Joystick*> JoystickAndroid::discover(MultiVehicleManager* _multiVe int SOURCE_GAMEPAD = QAndroidJniObject::getStaticField<jint>("android/view/InputDevice", "SOURCE_GAMEPAD"); int SOURCE_JOYSTICK = QAndroidJniObject::getStaticField<jint>("android/view/InputDevice", "SOURCE_JOYSTICK"); + QList<QString> names; + for (int i = 0; i < sz; ++i) { QAndroidJniObject inputDevice = QAndroidJniObject::callStaticObjectMethod("android/view/InputDevice", "getDevice", "(I)Landroid/view/InputDevice;", buff[i]); int sources = inputDevice.callMethod<jint>("getSources", "()I"); if (((sources & SOURCE_GAMEPAD) != SOURCE_GAMEPAD) //check if the input device is interesting to us && ((sources & SOURCE_JOYSTICK) != SOURCE_JOYSTICK)) continue; - //get id and name + // get id and name QString id = inputDevice.callObjectMethod("getDescriptor", "()Ljava/lang/String;").toString(); QString name = inputDevice.callObjectMethod("getName", "()Ljava/lang/String;").toString(); + names.push_back(name); - if (joystickFound) { //skipping { - qWarning() << "Skipping joystick:" << name; + if (ret.contains(name)) { continue; } - //get number of axis + // get number of axis QAndroidJniObject rangeListNative = inputDevice.callObjectMethod("getMotionRanges", "()Ljava/util/List;"); int axisCount = rangeListNative.callMethod<jint>("size"); - //get number of buttons + // get number of buttons jintArray a = env->NewIntArray(_androidBtnListCount); env->SetIntArrayRegion(a,0,_androidBtnListCount,_androidBtnList); QAndroidJniObject btns = inputDevice.callObjectMethod("hasKeys", "([I)[Z", a); @@ -121,7 +122,14 @@ QMap<QString, Joystick*> JoystickAndroid::discover(MultiVehicleManager* _multiVe qCDebug(JoystickLog) << "\t" << name << "id:" << buff[i] << "axes:" << axisCount << "buttons:" << buttonCount; ret[name] = new JoystickAndroid(name, axisCount, buttonCount, buff[i], _multiVehicleManager); - joystickFound = true; + } + + for (auto i = ret.begin(); i != ret.end();) { + if (!names.contains(i.key())) { + i = ret.erase(i); + } else { + i++; + } } env->ReleaseIntArrayElements(jarr, buff, 0); diff --git a/src/Joystick/JoystickManager.cc b/src/Joystick/JoystickManager.cc index f546609..2383a4b 100644 --- a/src/Joystick/JoystickManager.cc +++ b/src/Joystick/JoystickManager.cc @@ -1,6 +1,6 @@ /**************************************************************************** * - * (c) 2009-2016 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org> + * (c) 2009-2019 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org> * * QGroundControl is licensed according to the terms in the file * COPYING.md in the root of the source code directory. @@ -29,8 +29,8 @@ const char * JoystickManager::_settingsKeyActiveJoystick = "ActiveJoystick"; JoystickManager::JoystickManager(QGCApplication* app, QGCToolbox* toolbox) : QGCTool(app, toolbox) - , _activeJoystick(NULL) - , _multiVehicleManager(NULL) + , _activeJoystick(nullptr) + , _multiVehicleManager(nullptr) { } @@ -54,18 +54,17 @@ void JoystickManager::setToolbox(QGCToolbox *toolbox) void JoystickManager::init() { #ifdef __sdljoystick__ - if (JoystickSDL::init()) { - _setActiveJoystickFromSettings(); - connect(&_joystickCheckTimer, &QTimer::timeout, this, &JoystickManager::_updateAvailableJoysticks); - _joystickCheckTimer.start(250); + if (!JoystickSDL::init()) { + return; } + _setActiveJoystickFromSettings(); #elif defined(__android__) - if (JoystickAndroid::init()) { - _setActiveJoystickFromSettings(); - connect(&_joystickCheckTimer, &QTimer::timeout, this, &JoystickManager::_updateAvailableJoysticks); - _joystickCheckTimer.start(250); + if (!JoystickAndroid::init()) { + return; } #endif + connect(&_joystickCheckTimer, &QTimer::timeout, this, &JoystickManager::_updateAvailableJoysticks); + _joystickCheckTimer.start(1000); } void JoystickManager::_setActiveJoystickFromSettings(void) @@ -81,7 +80,7 @@ void JoystickManager::_setActiveJoystickFromSettings(void) if (_activeJoystick && !newMap.contains(_activeJoystick->name())) { qCDebug(JoystickManagerLog) << "Active joystick removed"; - setActiveJoystick(NULL); + setActiveJoystick(nullptr); } // Check to see if our current mapping contains any joysticks that are not in the new mapping @@ -100,7 +99,7 @@ void JoystickManager::_setActiveJoystickFromSettings(void) emit availableJoysticksChanged(); if (!_name2JoystickMap.count()) { - setActiveJoystick(NULL); + setActiveJoystick(nullptr); return; } @@ -126,7 +125,7 @@ void JoystickManager::setActiveJoystick(Joystick* joystick) { QSettings settings; - if (joystick != NULL && !_name2JoystickMap.contains(joystick->name())) { + if (joystick != nullptr && !_name2JoystickMap.contains(joystick->name())) { qCWarning(JoystickManagerLog) << "Set active not in map" << joystick->name(); return; } @@ -141,7 +140,7 @@ void JoystickManager::setActiveJoystick(Joystick* joystick) _activeJoystick = joystick; - if (_activeJoystick != NULL) { + if (_activeJoystick != nullptr) { qCDebug(JoystickManagerLog) << "Set active:" << _activeJoystick->name(); settings.beginGroup(_settingsGroup); @@ -208,8 +207,6 @@ void JoystickManager::_updateAvailableJoysticks(void) } } #elif defined(__android__) - /* - * TODO: Investigate Android events for Joystick hot plugging - */ + _setActiveJoystickFromSettings(); #endif } From afc2ee52eba5dcfad04c462fee31d307e47311ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Fran=C4=8De=C5=A1kin?= <Matej.Franceskin@gmail.com> Date: Tue, 5 Feb 2019 17:51:29 +0100 Subject: [PATCH 3/6] Detect bluetooth gamepad connect/disconnect events --- android/AndroidManifest.xml | 2 + .../org/mavlink/qgroundcontrol/QGCActivity.java | 11 ++++ src/Joystick/JoystickAndroid.cc | 59 ++++++++++++++++++++-- src/Joystick/JoystickAndroid.h | 4 +- src/Joystick/JoystickManager.cc | 16 +++++- src/Joystick/JoystickManager.h | 4 ++ 6 files changed, 90 insertions(+), 6 deletions(-) diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml index 28751cd..4cfc937 100644 --- a/android/AndroidManifest.xml +++ b/android/AndroidManifest.xml @@ -7,6 +7,8 @@ <category android:name="android.intent.category.LAUNCHER"/> <action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"/> <action android:name="android.hardware.usb.action.USB_DEVICE_DETACHED"/> + <action android:name="android.bluetooth.device.action.ACL_CONNECTED" /> + <action android:name="android.bluetooth.device.action.ACL_DISCONNECTED" /> </intent-filter> <meta-data android:resource="@xml/device_filter" android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"/> diff --git a/android/src/org/mavlink/qgroundcontrol/QGCActivity.java b/android/src/org/mavlink/qgroundcontrol/QGCActivity.java index a716392..c3f4fbe 100644 --- a/android/src/org/mavlink/qgroundcontrol/QGCActivity.java +++ b/android/src/org/mavlink/qgroundcontrol/QGCActivity.java @@ -48,6 +48,7 @@ import android.util.Log; import android.os.PowerManager; import android.view.WindowManager; import android.os.Bundle; +import android.bluetooth.BluetoothDevice; import com.hoho.android.usbserial.driver.*; import org.qtproject.qt5.android.bindings.QtActivity; @@ -133,6 +134,12 @@ public class QGCActivity extends QtActivity } } } + + try { + nativeUpdateAvailableJoysticks(); + } catch(Exception e) { + Log.e(TAG, "Exception nativeUpdateAvailableJoysticks()"); + } } }; @@ -140,6 +147,7 @@ public class QGCActivity extends QtActivity private static native void nativeDeviceHasDisconnected(int userData); private static native void nativeDeviceException(int userData, String messageA); private static native void nativeDeviceNewData(int userData, byte[] dataA); + private static native void nativeUpdateAvailableJoysticks(); // Native C++ functions called to log output public static native void qgcLogDebug(String message); @@ -171,8 +179,11 @@ public class QGCActivity extends QtActivity // Register for USB Detach and USB Permission intent IntentFilter filter = new IntentFilter(); + filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED); filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED); filter.addAction(ACTION_USB_PERMISSION); + filter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED); + filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED); _instance.registerReceiver(_instance._usbReceiver, filter); // Create intent for usb permission request diff --git a/src/Joystick/JoystickAndroid.cc b/src/Joystick/JoystickAndroid.cc index d66f0c0..3a1a1da 100644 --- a/src/Joystick/JoystickAndroid.cc +++ b/src/Joystick/JoystickAndroid.cc @@ -134,7 +134,6 @@ QMap<QString, Joystick*> JoystickAndroid::discover(MultiVehicleManager* _multiVe env->ReleaseIntArrayElements(jarr, buff, 0); - return ret; } @@ -171,7 +170,6 @@ bool JoystickAndroid::handleGenericMotionEvent(jobject event) { return true; } - bool JoystickAndroid::_open(void) { return true; } @@ -199,8 +197,14 @@ uint8_t JoystickAndroid::_getHat(int hat,int i) { return 0; } +static JoystickManager *_manager = nullptr; + //helper method -bool JoystickAndroid::init() { +bool JoystickAndroid::init(JoystickManager *manager) { + if (_manager == nullptr) { + setNativeMethods(manager); + } + //this gets list of all possible buttons - this is needed to check how many buttons our gamepad supports //instead of the whole logic below we could have just a simple array of hardcoded int values as these 'should' not change @@ -239,3 +243,52 @@ bool JoystickAndroid::init() { return true; } +static const char kJniClassName[] {"org/mavlink/qgroundcontrol/QGCActivity"}; + +static void jniUpdateAvailableJoysticks(JNIEnv *envA, jobject thizA) +{ + Q_UNUSED(envA); + Q_UNUSED(thizA); + + if (_manager != nullptr) { + qCDebug(JoystickLog) << "jniUpdateAvailableJoysticks triggered"; + emit _manager->updateAvailableJoysticksSignal(); + } +} + +void JoystickAndroid::setNativeMethods(JoystickManager *manager) +{ + qCDebug(JoystickLog) << "Registering Native Functions"; + + _manager = manager; + + // REGISTER THE C++ FUNCTION WITH JNI + JNINativeMethod javaMethods[] { + {"nativeUpdateAvailableJoysticks", "()V", reinterpret_cast<void *>(jniUpdateAvailableJoysticks)} + }; + + QAndroidJniEnvironment jniEnv; + if (jniEnv->ExceptionCheck()) { + jniEnv->ExceptionDescribe(); + jniEnv->ExceptionClear(); + } + + jclass objectClass = jniEnv->FindClass(kJniClassName); + if(!objectClass) { + qWarning() << "Couldn't find class:" << kJniClassName; + return; + } + + jint val = jniEnv->RegisterNatives(objectClass, javaMethods, sizeof(javaMethods) / sizeof(javaMethods[0])); + + if (val < 0) { + qWarning() << "Error registering methods: " << val; + } else { + qCDebug(JoystickLog) << "Native Functions Registered"; + } + + if (jniEnv->ExceptionCheck()) { + jniEnv->ExceptionDescribe(); + jniEnv->ExceptionClear(); + } +} diff --git a/src/Joystick/JoystickAndroid.h b/src/Joystick/JoystickAndroid.h index 78defed..329194e 100644 --- a/src/Joystick/JoystickAndroid.h +++ b/src/Joystick/JoystickAndroid.h @@ -19,7 +19,7 @@ public: ~JoystickAndroid(); - static bool init(); + static bool init(JoystickManager *manager); static QMap<QString, Joystick*> discover(MultiVehicleManager* _multiVehicleManager); @@ -27,6 +27,8 @@ private: bool handleKeyEvent(jobject event); bool handleGenericMotionEvent(jobject event); + static void setNativeMethods(JoystickManager *manager); + virtual bool _open(); virtual void _close(); virtual bool _update(); diff --git a/src/Joystick/JoystickManager.cc b/src/Joystick/JoystickManager.cc index 2383a4b..1cac139 100644 --- a/src/Joystick/JoystickManager.cc +++ b/src/Joystick/JoystickManager.cc @@ -59,11 +59,13 @@ void JoystickManager::init() { } _setActiveJoystickFromSettings(); #elif defined(__android__) - if (!JoystickAndroid::init()) { + if (!JoystickAndroid::init(this)) { return; } + connect(this, &JoystickManager::updateAvailableJoysticksSignal, this, &JoystickManager::restartJoystickCheckTimer); #endif connect(&_joystickCheckTimer, &QTimer::timeout, this, &JoystickManager::_updateAvailableJoysticks); + _joystickCheckTimerCounter = 5; _joystickCheckTimer.start(1000); } @@ -185,7 +187,7 @@ void JoystickManager::setActiveJoystickName(const QString& name) /* * TODO: move this to the right place: JoystickSDL.cc and JoystickAndroid.cc respectively and call through Joystick.cc */ -void JoystickManager::_updateAvailableJoysticks(void) +void JoystickManager::_updateAvailableJoysticks() { #ifdef __sdljoystick__ SDL_Event event; @@ -207,6 +209,16 @@ void JoystickManager::_updateAvailableJoysticks(void) } } #elif defined(__android__) + _joystickCheckTimerCounter--; _setActiveJoystickFromSettings(); + if (_joystickCheckTimerCounter <= 0) { + _joystickCheckTimer.stop(); + } #endif } + +void JoystickManager::restartJoystickCheckTimer() +{ + _joystickCheckTimerCounter = 5; + _joystickCheckTimer.start(1000); +} diff --git a/src/Joystick/JoystickManager.h b/src/Joystick/JoystickManager.h index 6418886..f6736ff 100644 --- a/src/Joystick/JoystickManager.h +++ b/src/Joystick/JoystickManager.h @@ -45,6 +45,8 @@ public: QString activeJoystickName(void); void setActiveJoystickName(const QString& name); + void restartJoystickCheckTimer(void); + // Override from QGCTool virtual void setToolbox(QGCToolbox *toolbox); @@ -55,6 +57,7 @@ signals: void activeJoystickChanged(Joystick* joystick); void activeJoystickNameChanged(const QString& name); void availableJoysticksChanged(void); + void updateAvailableJoysticksSignal(); private slots: void _updateAvailableJoysticks(void); @@ -70,6 +73,7 @@ private: static const char * _settingsGroup; static const char * _settingsKeyActiveJoystick; + int _joystickCheckTimerCounter; QTimer _joystickCheckTimer; }; From a7381c4e0854c03a8238775457982b03620a17cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Fran=C4=8De=C5=A1kin?= <Matej.Franceskin@gmail.com> Date: Mon, 7 Jan 2019 15:27:43 +0100 Subject: [PATCH 4/6] Make Android Joystick init behave in the same was as for SDL --- src/Joystick/JoystickAndroid.cc | 6 +++--- src/Joystick/JoystickAndroid.h | 6 ++++-- src/Joystick/JoystickManager.cc | 7 +++++-- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/Joystick/JoystickAndroid.cc b/src/Joystick/JoystickAndroid.cc index 7ec59a4..bc9f22b 100644 --- a/src/Joystick/JoystickAndroid.cc +++ b/src/Joystick/JoystickAndroid.cc @@ -76,8 +76,6 @@ QMap<QString, Joystick*> JoystickAndroid::discover(MultiVehicleManager* _multiVe bool joystickFound = false; static QMap<QString, Joystick*> ret; - _initStatic(); //it's enough to run it once, should be in a static constructor - QMutexLocker lock(&m_mutex); QAndroidJniEnvironment env; @@ -194,7 +192,7 @@ uint8_t JoystickAndroid::_getHat(int hat,int i) { } //helper method -void JoystickAndroid::_initStatic() { +bool JoystickAndroid::init() { //this gets list of all possible buttons - this is needed to check how many buttons our gamepad supports //instead of the whole logic below we could have just a simple array of hardcoded int values as these 'should' not change @@ -229,5 +227,7 @@ void JoystickAndroid::_initStatic() { ACTION_DOWN = QAndroidJniObject::getStaticField<jint>("android/view/KeyEvent", "ACTION_DOWN"); ACTION_UP = QAndroidJniObject::getStaticField<jint>("android/view/KeyEvent", "ACTION_UP"); + + return true; } diff --git a/src/Joystick/JoystickAndroid.h b/src/Joystick/JoystickAndroid.h index b50c7f9..78defed 100644 --- a/src/Joystick/JoystickAndroid.h +++ b/src/Joystick/JoystickAndroid.h @@ -16,9 +16,12 @@ class JoystickAndroid : public Joystick, public QtAndroidPrivate::GenericMotionE { public: JoystickAndroid(const QString& name, int axisCount, int buttonCount, int id, MultiVehicleManager* multiVehicleManager); + ~JoystickAndroid(); - static QMap<QString, Joystick*> discover(MultiVehicleManager* _multiVehicleManager); + static bool init(); + + static QMap<QString, Joystick*> discover(MultiVehicleManager* _multiVehicleManager); private: bool handleKeyEvent(jobject event); @@ -37,7 +40,6 @@ private: bool *btnValue; int *axisValue; - static void _initStatic(); static int * _androidBtnList; //list of all possible android buttons static int _androidBtnListCount; diff --git a/src/Joystick/JoystickManager.cc b/src/Joystick/JoystickManager.cc index 001418e..f546609 100644 --- a/src/Joystick/JoystickManager.cc +++ b/src/Joystick/JoystickManager.cc @@ -60,8 +60,11 @@ void JoystickManager::init() { _joystickCheckTimer.start(250); } #elif defined(__android__) - _setActiveJoystickFromSettings(); - //TODO: Investigate Android events for Joystick hot plugging & run _joystickCheckTimer if possible + if (JoystickAndroid::init()) { + _setActiveJoystickFromSettings(); + connect(&_joystickCheckTimer, &QTimer::timeout, this, &JoystickManager::_updateAvailableJoysticks); + _joystickCheckTimer.start(250); + } #endif } From 5464e10758a27c7c90ce06e7e9ce85b59c490dac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Fran=C4=8De=C5=A1kin?= <Matej.Franceskin@gmail.com> Date: Tue, 8 Jan 2019 14:15:41 +0100 Subject: [PATCH 5/6] Bugfix: [Android] Joystick is not visible if connected after start of QGC When starting up, if you start the GS before connecting the bluetooth joystick the QGC needs to be closed and restarted to get it to recognize the joystick --- src/Joystick/JoystickAndroid.cc | 22 +++++++++++++++------- src/Joystick/JoystickManager.cc | 33 +++++++++++++++------------------ 2 files changed, 30 insertions(+), 25 deletions(-) diff --git a/src/Joystick/JoystickAndroid.cc b/src/Joystick/JoystickAndroid.cc index bc9f22b..d66f0c0 100644 --- a/src/Joystick/JoystickAndroid.cc +++ b/src/Joystick/JoystickAndroid.cc @@ -73,7 +73,6 @@ JoystickAndroid::~JoystickAndroid() { QMap<QString, Joystick*> JoystickAndroid::discover(MultiVehicleManager* _multiVehicleManager) { - bool joystickFound = false; static QMap<QString, Joystick*> ret; QMutexLocker lock(&m_mutex); @@ -87,27 +86,29 @@ QMap<QString, Joystick*> JoystickAndroid::discover(MultiVehicleManager* _multiVe int SOURCE_GAMEPAD = QAndroidJniObject::getStaticField<jint>("android/view/InputDevice", "SOURCE_GAMEPAD"); int SOURCE_JOYSTICK = QAndroidJniObject::getStaticField<jint>("android/view/InputDevice", "SOURCE_JOYSTICK"); + QList<QString> names; + for (int i = 0; i < sz; ++i) { QAndroidJniObject inputDevice = QAndroidJniObject::callStaticObjectMethod("android/view/InputDevice", "getDevice", "(I)Landroid/view/InputDevice;", buff[i]); int sources = inputDevice.callMethod<jint>("getSources", "()I"); if (((sources & SOURCE_GAMEPAD) != SOURCE_GAMEPAD) //check if the input device is interesting to us && ((sources & SOURCE_JOYSTICK) != SOURCE_JOYSTICK)) continue; - //get id and name + // get id and name QString id = inputDevice.callObjectMethod("getDescriptor", "()Ljava/lang/String;").toString(); QString name = inputDevice.callObjectMethod("getName", "()Ljava/lang/String;").toString(); + names.push_back(name); - if (joystickFound) { //skipping { - qWarning() << "Skipping joystick:" << name; + if (ret.contains(name)) { continue; } - //get number of axis + // get number of axis QAndroidJniObject rangeListNative = inputDevice.callObjectMethod("getMotionRanges", "()Ljava/util/List;"); int axisCount = rangeListNative.callMethod<jint>("size"); - //get number of buttons + // get number of buttons jintArray a = env->NewIntArray(_androidBtnListCount); env->SetIntArrayRegion(a,0,_androidBtnListCount,_androidBtnList); QAndroidJniObject btns = inputDevice.callObjectMethod("hasKeys", "([I)[Z", a); @@ -121,7 +122,14 @@ QMap<QString, Joystick*> JoystickAndroid::discover(MultiVehicleManager* _multiVe qCDebug(JoystickLog) << "\t" << name << "id:" << buff[i] << "axes:" << axisCount << "buttons:" << buttonCount; ret[name] = new JoystickAndroid(name, axisCount, buttonCount, buff[i], _multiVehicleManager); - joystickFound = true; + } + + for (auto i = ret.begin(); i != ret.end();) { + if (!names.contains(i.key())) { + i = ret.erase(i); + } else { + i++; + } } env->ReleaseIntArrayElements(jarr, buff, 0); diff --git a/src/Joystick/JoystickManager.cc b/src/Joystick/JoystickManager.cc index f546609..2383a4b 100644 --- a/src/Joystick/JoystickManager.cc +++ b/src/Joystick/JoystickManager.cc @@ -1,6 +1,6 @@ /**************************************************************************** * - * (c) 2009-2016 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org> + * (c) 2009-2019 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org> * * QGroundControl is licensed according to the terms in the file * COPYING.md in the root of the source code directory. @@ -29,8 +29,8 @@ const char * JoystickManager::_settingsKeyActiveJoystick = "ActiveJoystick"; JoystickManager::JoystickManager(QGCApplication* app, QGCToolbox* toolbox) : QGCTool(app, toolbox) - , _activeJoystick(NULL) - , _multiVehicleManager(NULL) + , _activeJoystick(nullptr) + , _multiVehicleManager(nullptr) { } @@ -54,18 +54,17 @@ void JoystickManager::setToolbox(QGCToolbox *toolbox) void JoystickManager::init() { #ifdef __sdljoystick__ - if (JoystickSDL::init()) { - _setActiveJoystickFromSettings(); - connect(&_joystickCheckTimer, &QTimer::timeout, this, &JoystickManager::_updateAvailableJoysticks); - _joystickCheckTimer.start(250); + if (!JoystickSDL::init()) { + return; } + _setActiveJoystickFromSettings(); #elif defined(__android__) - if (JoystickAndroid::init()) { - _setActiveJoystickFromSettings(); - connect(&_joystickCheckTimer, &QTimer::timeout, this, &JoystickManager::_updateAvailableJoysticks); - _joystickCheckTimer.start(250); + if (!JoystickAndroid::init()) { + return; } #endif + connect(&_joystickCheckTimer, &QTimer::timeout, this, &JoystickManager::_updateAvailableJoysticks); + _joystickCheckTimer.start(1000); } void JoystickManager::_setActiveJoystickFromSettings(void) @@ -81,7 +80,7 @@ void JoystickManager::_setActiveJoystickFromSettings(void) if (_activeJoystick && !newMap.contains(_activeJoystick->name())) { qCDebug(JoystickManagerLog) << "Active joystick removed"; - setActiveJoystick(NULL); + setActiveJoystick(nullptr); } // Check to see if our current mapping contains any joysticks that are not in the new mapping @@ -100,7 +99,7 @@ void JoystickManager::_setActiveJoystickFromSettings(void) emit availableJoysticksChanged(); if (!_name2JoystickMap.count()) { - setActiveJoystick(NULL); + setActiveJoystick(nullptr); return; } @@ -126,7 +125,7 @@ void JoystickManager::setActiveJoystick(Joystick* joystick) { QSettings settings; - if (joystick != NULL && !_name2JoystickMap.contains(joystick->name())) { + if (joystick != nullptr && !_name2JoystickMap.contains(joystick->name())) { qCWarning(JoystickManagerLog) << "Set active not in map" << joystick->name(); return; } @@ -141,7 +140,7 @@ void JoystickManager::setActiveJoystick(Joystick* joystick) _activeJoystick = joystick; - if (_activeJoystick != NULL) { + if (_activeJoystick != nullptr) { qCDebug(JoystickManagerLog) << "Set active:" << _activeJoystick->name(); settings.beginGroup(_settingsGroup); @@ -208,8 +207,6 @@ void JoystickManager::_updateAvailableJoysticks(void) } } #elif defined(__android__) - /* - * TODO: Investigate Android events for Joystick hot plugging - */ + _setActiveJoystickFromSettings(); #endif } From fe63cf82016dd07efe4f4a4680eee6dccb6db848 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Fran=C4=8De=C5=A1kin?= <Matej.Franceskin@gmail.com> Date: Tue, 5 Feb 2019 17:51:29 +0100 Subject: [PATCH 6/6] Detect bluetooth gamepad connect/disconnect events --- android/AndroidManifest.xml | 2 + .../org/mavlink/qgroundcontrol/QGCActivity.java | 12 +++++ src/Joystick/JoystickAndroid.cc | 59 ++++++++++++++++++++-- src/Joystick/JoystickAndroid.h | 4 +- src/Joystick/JoystickManager.cc | 16 +++++- src/Joystick/JoystickManager.h | 4 ++ 6 files changed, 91 insertions(+), 6 deletions(-) diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml index 49581f6..1f309ba 100644 --- a/android/AndroidManifest.xml +++ b/android/AndroidManifest.xml @@ -7,6 +7,8 @@ <category android:name="android.intent.category.LAUNCHER"/> <action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"/> <action android:name="android.hardware.usb.action.USB_DEVICE_DETACHED"/> + <action android:name="android.bluetooth.device.action.ACL_CONNECTED" /> + <action android:name="android.bluetooth.device.action.ACL_DISCONNECTED" /> <action android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED"/> </intent-filter> diff --git a/android/src/org/mavlink/qgroundcontrol/QGCActivity.java b/android/src/org/mavlink/qgroundcontrol/QGCActivity.java index f76242f..9923c8f 100644 --- a/android/src/org/mavlink/qgroundcontrol/QGCActivity.java +++ b/android/src/org/mavlink/qgroundcontrol/QGCActivity.java @@ -55,6 +55,8 @@ import android.os.PowerManager; import android.os.Bundle; import android.app.PendingIntent; import android.view.WindowManager; +import android.os.Bundle; +import android.bluetooth.BluetoothDevice; import com.hoho.android.usbserial.driver.*; import org.qtproject.qt5.android.bindings.QtActivity; @@ -162,6 +164,12 @@ public class QGCActivity extends QtActivity } } } + + try { + nativeUpdateAvailableJoysticks(); + } catch(Exception e) { + Log.e(TAG, "Exception nativeUpdateAvailableJoysticks()"); + } } }; @@ -169,6 +177,7 @@ public class QGCActivity extends QtActivity private static native void nativeDeviceHasDisconnected(int userData); private static native void nativeDeviceException(int userData, String messageA); private static native void nativeDeviceNewData(int userData, byte[] dataA); + private static native void nativeUpdateAvailableJoysticks(); // Native C++ functions called to log output public static native void qgcLogDebug(String message); @@ -200,8 +209,11 @@ public class QGCActivity extends QtActivity // Register for USB Detach and USB Permission intent IntentFilter filter = new IntentFilter(); + filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED); filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED); filter.addAction(ACTION_USB_PERMISSION); + filter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED); + filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED); _instance.registerReceiver(_instance._usbReceiver, filter); // Create intent for usb permission request diff --git a/src/Joystick/JoystickAndroid.cc b/src/Joystick/JoystickAndroid.cc index d66f0c0..3a1a1da 100644 --- a/src/Joystick/JoystickAndroid.cc +++ b/src/Joystick/JoystickAndroid.cc @@ -134,7 +134,6 @@ QMap<QString, Joystick*> JoystickAndroid::discover(MultiVehicleManager* _multiVe env->ReleaseIntArrayElements(jarr, buff, 0); - return ret; } @@ -171,7 +170,6 @@ bool JoystickAndroid::handleGenericMotionEvent(jobject event) { return true; } - bool JoystickAndroid::_open(void) { return true; } @@ -199,8 +197,14 @@ uint8_t JoystickAndroid::_getHat(int hat,int i) { return 0; } +static JoystickManager *_manager = nullptr; + //helper method -bool JoystickAndroid::init() { +bool JoystickAndroid::init(JoystickManager *manager) { + if (_manager == nullptr) { + setNativeMethods(manager); + } + //this gets list of all possible buttons - this is needed to check how many buttons our gamepad supports //instead of the whole logic below we could have just a simple array of hardcoded int values as these 'should' not change @@ -239,3 +243,52 @@ bool JoystickAndroid::init() { return true; } +static const char kJniClassName[] {"org/mavlink/qgroundcontrol/QGCActivity"}; + +static void jniUpdateAvailableJoysticks(JNIEnv *envA, jobject thizA) +{ + Q_UNUSED(envA); + Q_UNUSED(thizA); + + if (_manager != nullptr) { + qCDebug(JoystickLog) << "jniUpdateAvailableJoysticks triggered"; + emit _manager->updateAvailableJoysticksSignal(); + } +} + +void JoystickAndroid::setNativeMethods(JoystickManager *manager) +{ + qCDebug(JoystickLog) << "Registering Native Functions"; + + _manager = manager; + + // REGISTER THE C++ FUNCTION WITH JNI + JNINativeMethod javaMethods[] { + {"nativeUpdateAvailableJoysticks", "()V", reinterpret_cast<void *>(jniUpdateAvailableJoysticks)} + }; + + QAndroidJniEnvironment jniEnv; + if (jniEnv->ExceptionCheck()) { + jniEnv->ExceptionDescribe(); + jniEnv->ExceptionClear(); + } + + jclass objectClass = jniEnv->FindClass(kJniClassName); + if(!objectClass) { + qWarning() << "Couldn't find class:" << kJniClassName; + return; + } + + jint val = jniEnv->RegisterNatives(objectClass, javaMethods, sizeof(javaMethods) / sizeof(javaMethods[0])); + + if (val < 0) { + qWarning() << "Error registering methods: " << val; + } else { + qCDebug(JoystickLog) << "Native Functions Registered"; + } + + if (jniEnv->ExceptionCheck()) { + jniEnv->ExceptionDescribe(); + jniEnv->ExceptionClear(); + } +} diff --git a/src/Joystick/JoystickAndroid.h b/src/Joystick/JoystickAndroid.h index 78defed..329194e 100644 --- a/src/Joystick/JoystickAndroid.h +++ b/src/Joystick/JoystickAndroid.h @@ -19,7 +19,7 @@ public: ~JoystickAndroid(); - static bool init(); + static bool init(JoystickManager *manager); static QMap<QString, Joystick*> discover(MultiVehicleManager* _multiVehicleManager); @@ -27,6 +27,8 @@ private: bool handleKeyEvent(jobject event); bool handleGenericMotionEvent(jobject event); + static void setNativeMethods(JoystickManager *manager); + virtual bool _open(); virtual void _close(); virtual bool _update(); diff --git a/src/Joystick/JoystickManager.cc b/src/Joystick/JoystickManager.cc index 2383a4b..1cac139 100644 --- a/src/Joystick/JoystickManager.cc +++ b/src/Joystick/JoystickManager.cc @@ -59,11 +59,13 @@ void JoystickManager::init() { } _setActiveJoystickFromSettings(); #elif defined(__android__) - if (!JoystickAndroid::init()) { + if (!JoystickAndroid::init(this)) { return; } + connect(this, &JoystickManager::updateAvailableJoysticksSignal, this, &JoystickManager::restartJoystickCheckTimer); #endif connect(&_joystickCheckTimer, &QTimer::timeout, this, &JoystickManager::_updateAvailableJoysticks); + _joystickCheckTimerCounter = 5; _joystickCheckTimer.start(1000); } @@ -185,7 +187,7 @@ void JoystickManager::setActiveJoystickName(const QString& name) /* * TODO: move this to the right place: JoystickSDL.cc and JoystickAndroid.cc respectively and call through Joystick.cc */ -void JoystickManager::_updateAvailableJoysticks(void) +void JoystickManager::_updateAvailableJoysticks() { #ifdef __sdljoystick__ SDL_Event event; @@ -207,6 +209,16 @@ void JoystickManager::_updateAvailableJoysticks(void) } } #elif defined(__android__) + _joystickCheckTimerCounter--; _setActiveJoystickFromSettings(); + if (_joystickCheckTimerCounter <= 0) { + _joystickCheckTimer.stop(); + } #endif } + +void JoystickManager::restartJoystickCheckTimer() +{ + _joystickCheckTimerCounter = 5; + _joystickCheckTimer.start(1000); +} diff --git a/src/Joystick/JoystickManager.h b/src/Joystick/JoystickManager.h index 6418886..f6736ff 100644 --- a/src/Joystick/JoystickManager.h +++ b/src/Joystick/JoystickManager.h @@ -45,6 +45,8 @@ public: QString activeJoystickName(void); void setActiveJoystickName(const QString& name); + void restartJoystickCheckTimer(void); + // Override from QGCTool virtual void setToolbox(QGCToolbox *toolbox); @@ -55,6 +57,7 @@ signals: void activeJoystickChanged(Joystick* joystick); void activeJoystickNameChanged(const QString& name); void availableJoysticksChanged(void); + void updateAvailableJoysticksSignal(); private slots: void _updateAvailableJoysticks(void); @@ -70,6 +73,7 @@ private: static const char * _settingsGroup; static const char * _settingsKeyActiveJoystick; + int _joystickCheckTimerCounter; QTimer _joystickCheckTimer; };