diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml index 49581f61050e4c8ec5fc6946ab805384f7cbcd9c..1f309baa47c404a4d88cbbbc85f8d6cc42025398 100644 --- a/android/AndroidManifest.xml +++ b/android/AndroidManifest.xml @@ -7,6 +7,8 @@ + + diff --git a/android/src/org/mavlink/qgroundcontrol/QGCActivity.java b/android/src/org/mavlink/qgroundcontrol/QGCActivity.java index f76242f5e67adfc71d7e3d5d362f693c0faf5ff4..9923c8f4f24145ffdc5563b4459f41613c452715 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 7ec59a47783cecf419c80876d6aa5f3f664bf7e0..3a1a1da31d9bbd9f277ef3cefc2551b35955b684 100644 --- a/src/Joystick/JoystickAndroid.cc +++ b/src/Joystick/JoystickAndroid.cc @@ -73,11 +73,8 @@ JoystickAndroid::~JoystickAndroid() { QMap JoystickAndroid::discover(MultiVehicleManager* _multiVehicleManager) { - bool joystickFound = false; static QMap ret; - _initStatic(); //it's enough to run it once, should be in a static constructor - QMutexLocker lock(&m_mutex); QAndroidJniEnvironment env; @@ -89,27 +86,29 @@ QMap JoystickAndroid::discover(MultiVehicleManager* _multiVe int SOURCE_GAMEPAD = QAndroidJniObject::getStaticField("android/view/InputDevice", "SOURCE_GAMEPAD"); int SOURCE_JOYSTICK = QAndroidJniObject::getStaticField("android/view/InputDevice", "SOURCE_JOYSTICK"); + QList 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("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("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); @@ -123,11 +122,17 @@ QMap 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; } - env->ReleaseIntArrayElements(jarr, buff, 0); + for (auto i = ret.begin(); i != ret.end();) { + if (!names.contains(i.key())) { + i = ret.erase(i); + } else { + i++; + } + } + env->ReleaseIntArrayElements(jarr, buff, 0); return ret; } @@ -165,7 +170,6 @@ bool JoystickAndroid::handleGenericMotionEvent(jobject event) { return true; } - bool JoystickAndroid::_open(void) { return true; } @@ -193,8 +197,14 @@ uint8_t JoystickAndroid::_getHat(int hat,int i) { return 0; } +static JoystickManager *_manager = nullptr; + //helper method -void JoystickAndroid::_initStatic() { +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 @@ -229,5 +239,56 @@ void JoystickAndroid::_initStatic() { ACTION_DOWN = QAndroidJniObject::getStaticField("android/view/KeyEvent", "ACTION_DOWN"); ACTION_UP = QAndroidJniObject::getStaticField("android/view/KeyEvent", "ACTION_UP"); + + 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(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 b50c7f919825977a539ecfc05bbc8df1bd2243bd..329194ed65d9273ba216abc6736504829a5777ce 100644 --- a/src/Joystick/JoystickAndroid.h +++ b/src/Joystick/JoystickAndroid.h @@ -16,14 +16,19 @@ class JoystickAndroid : public Joystick, public QtAndroidPrivate::GenericMotionE { public: JoystickAndroid(const QString& name, int axisCount, int buttonCount, int id, MultiVehicleManager* multiVehicleManager); + ~JoystickAndroid(); - static QMap discover(MultiVehicleManager* _multiVehicleManager); + static bool init(JoystickManager *manager); + + static QMap discover(MultiVehicleManager* _multiVehicleManager); private: bool handleKeyEvent(jobject event); bool handleGenericMotionEvent(jobject event); + static void setNativeMethods(JoystickManager *manager); + virtual bool _open(); virtual void _close(); virtual bool _update(); @@ -37,7 +42,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 001418e52edd3a2fc750e3ee4ef4333fb2315e22..1cac13960e590b2b8424914a6e0a0b3d713a5f44 100644 --- a/src/Joystick/JoystickManager.cc +++ b/src/Joystick/JoystickManager.cc @@ -1,6 +1,6 @@ /**************************************************************************** * - * (c) 2009-2016 QGROUNDCONTROL PROJECT + * (c) 2009-2019 QGROUNDCONTROL PROJECT * * 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,15 +54,19 @@ 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; } -#elif defined(__android__) _setActiveJoystickFromSettings(); - //TODO: Investigate Android events for Joystick hot plugging & run _joystickCheckTimer if possible +#elif defined(__android__) + 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); } void JoystickManager::_setActiveJoystickFromSettings(void) @@ -78,7 +82,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 @@ -97,7 +101,7 @@ void JoystickManager::_setActiveJoystickFromSettings(void) emit availableJoysticksChanged(); if (!_name2JoystickMap.count()) { - setActiveJoystick(NULL); + setActiveJoystick(nullptr); return; } @@ -123,7 +127,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; } @@ -138,7 +142,7 @@ void JoystickManager::setActiveJoystick(Joystick* joystick) _activeJoystick = joystick; - if (_activeJoystick != NULL) { + if (_activeJoystick != nullptr) { qCDebug(JoystickManagerLog) << "Set active:" << _activeJoystick->name(); settings.beginGroup(_settingsGroup); @@ -183,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; @@ -205,8 +209,16 @@ void JoystickManager::_updateAvailableJoysticks(void) } } #elif defined(__android__) - /* - * TODO: Investigate Android events for Joystick hot plugging - */ + _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 6418886b404868e3fcfe07f39baa785a373b4af1..f6736ff8a91b094c5a8b7db776e5a9ad927548f0 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; };