diff --git a/qgroundcontrol.pro b/qgroundcontrol.pro index 6ca491818d5b0f697a76a5f13f97ac9d094e5918..b5f815f875367666d036567e5763c8574ff7628f 100644 --- a/qgroundcontrol.pro +++ b/qgroundcontrol.pro @@ -498,6 +498,11 @@ HEADERS += \ src/uas/UASInterface.h \ src/uas/UASMessageHandler.h \ +AndroidBuild { +HEADERS += \ + src/Joystick/JoystickAndroid.h \ +} + DebugBuild { HEADERS += \ src/comm/MockLink.h \ @@ -587,6 +592,7 @@ iOSBuild { AndroidBuild { SOURCES += src/MobileScreenMgr.cc \ + src/Joystick/JoystickAndroid.cc \ } SOURCES += \ diff --git a/src/Joystick/Joystick.h b/src/Joystick/Joystick.h index 10d65fbfbee2fa0958faee9070027723585357d3..def702b8370d267254a1e7dd6f9af7a5b18691b1 100644 --- a/src/Joystick/Joystick.h +++ b/src/Joystick/Joystick.h @@ -95,12 +95,12 @@ public: QVariantList buttonActions(void); QString name(void) { return _name; } - +/* // Joystick index used by sdl library // Settable because sdl library remaps indicies after certain events virtual int index(void) = 0; virtual void setIndex(int index) = 0; - +*/ virtual bool requiresCalibration(void) { return true; } int throttleMode(void); diff --git a/src/Joystick/JoystickAndroid.cc b/src/Joystick/JoystickAndroid.cc new file mode 100644 index 0000000000000000000000000000000000000000..b965820322c0b3650b2dc1744fd8876cfdd42b27 --- /dev/null +++ b/src/Joystick/JoystickAndroid.cc @@ -0,0 +1,227 @@ +#include "JoystickAndroid.h" + +#include "QGCApplication.h" + +#include + +int JoystickAndroid::_androidBtnListCount; +int *JoystickAndroid::_androidBtnList; +int JoystickAndroid::ACTION_DOWN; +int JoystickAndroid::ACTION_UP; +QMutex JoystickAndroid::m_mutex; + +JoystickAndroid::JoystickAndroid(const QString& name, int axisCount, int buttonCount, int id, MultiVehicleManager* multiVehicleManager) + : Joystick(name,axisCount,buttonCount,0,multiVehicleManager) + , deviceId(id) +{ + int i; + + QAndroidJniEnvironment env; + QAndroidJniObject inputDevice = QAndroidJniObject::callStaticObjectMethod("android/view/InputDevice", "getDevice", "(I)Landroid/view/InputDevice;", id); + + //set button mapping (number->code) + jintArray b = env->NewIntArray(_androidBtnListCount); + env->SetIntArrayRegion(b,0,_androidBtnListCount,_androidBtnList); + + QAndroidJniObject btns = inputDevice.callObjectMethod("hasKeys", "([I)[Z", b); + jbooleanArray jSupportedButtons = btns.object(); + jboolean* supportedButtons = env->GetBooleanArrayElements(jSupportedButtons, nullptr); + //create a mapping table (btnCode) that maps button number with button code + btnValue = new bool[_buttonCount]; + btnCode = new int[_buttonCount]; + int c = 0; + for (i=0;i<_androidBtnListCount;i++) + if (supportedButtons[i]) { + btnValue[c] = false; + btnCode[c] = _androidBtnList[i]; + c++; + } + + env->ReleaseBooleanArrayElements(jSupportedButtons, supportedButtons, 0); + + //set axis mapping (number->code) + axisValue = new int[_axisCount]; + axisCode = new int[_axisCount]; + QAndroidJniObject rangeListNative = inputDevice.callObjectMethod("getMotionRanges", "()Ljava/util/List;"); + for (i=0;i<_axisCount;i++) { + QAndroidJniObject range = rangeListNative.callObjectMethod("get", "(I)Ljava/lang/Object;",i); + axisCode[i] = range.callMethod("getAxis"); + axisValue[i] = 0; + } + + + qCDebug(JoystickLog) << "axis:" <<_axisCount << "buttons:" <<_buttonCount; + QtAndroidPrivate::registerGenericMotionEventListener(this); + QtAndroidPrivate::registerKeyEventListener(this); +} + +JoystickAndroid::~JoystickAndroid() { + delete btnCode; + delete axisCode; + delete btnValue; + delete axisValue; + + QtAndroidPrivate::unregisterGenericMotionEventListener(this); + QtAndroidPrivate::unregisterKeyEventListener(this); +} + + +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; + QAndroidJniObject o = QAndroidJniObject::callStaticObjectMethod("android/view/InputDevice", "getDeviceIds"); + jintArray jarr = o.object(); + int sz = env->GetArrayLength(jarr); + jint *buff = env->GetIntArrayElements(jarr, nullptr); + + int SOURCE_GAMEPAD = QAndroidJniObject::getStaticField("android/view/InputDevice", "SOURCE_GAMEPAD"); + int SOURCE_JOYSTICK = QAndroidJniObject::getStaticField("android/view/InputDevice", "SOURCE_JOYSTICK"); + + 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 + QString id = inputDevice.callObjectMethod("getDescriptor", "()Ljava/lang/String;").toString(); + QString name = inputDevice.callObjectMethod("getName", "()Ljava/lang/String;").toString(); + + + if (joystickFound) { //skipping { + qWarning() << "Skipping joystick:" << name; + continue; + } + + //get number of axis + QAndroidJniObject rangeListNative = inputDevice.callObjectMethod("getMotionRanges", "()Ljava/util/List;"); + int axisCount = rangeListNative.callMethod("size"); + + //get number of buttons + jintArray a = env->NewIntArray(_androidBtnListCount); + env->SetIntArrayRegion(a,0,_androidBtnListCount,_androidBtnList); + QAndroidJniObject btns = inputDevice.callObjectMethod("hasKeys", "([I)[Z", a); + jbooleanArray jSupportedButtons = btns.object(); + jboolean* supportedButtons = env->GetBooleanArrayElements(jSupportedButtons, nullptr); + int buttonCount = 0; + for (int j=0;j<_androidBtnListCount;j++) + if (supportedButtons[j]) buttonCount++; + env->ReleaseBooleanArrayElements(jSupportedButtons, supportedButtons, 0); + + 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); + + + return ret; +} + + +bool JoystickAndroid::handleKeyEvent(jobject event) { + QJNIObjectPrivate ev(event); + QMutexLocker lock(&m_mutex); + const int _deviceId = ev.callMethod("getDeviceId", "()I"); + if (_deviceId!=deviceId) return false; + + const int action = ev.callMethod("getAction", "()I"); + const int keyCode = ev.callMethod("getKeyCode", "()I"); + + for (int i=0;i<_buttonCount;i++) { + if (btnCode[i]==keyCode) { + if (action==ACTION_DOWN) btnValue[i] = true; + if (action==ACTION_UP) btnValue[i] = false; + return true; + } + } + return false; +} + +bool JoystickAndroid::handleGenericMotionEvent(jobject event) { + QJNIObjectPrivate ev(event); + QMutexLocker lock(&m_mutex); + const int _deviceId = ev.callMethod("getDeviceId", "()I"); + if (_deviceId!=deviceId) return false; + + for (int i=0;i<_axisCount;i++) { + const float v = ev.callMethod("getAxisValue", "(I)F",axisCode[i]); + axisValue[i] = (int)(v*32767.f); + } + return true; +} + + +bool JoystickAndroid::_open(void) { + return true; +} + +void JoystickAndroid::_close(void) { +} + +bool JoystickAndroid::_update(void) +{ + return true; +} + +bool JoystickAndroid::_getButton(int i) { + return btnValue[ i ]; +} + +int JoystickAndroid::_getAxis(int i) { + return axisValue[ i ]; +} + +uint8_t JoystickAndroid::_getHat(int hat,int i) { + Q_UNUSED(hat); + Q_UNUSED(i); + + return 0; +} + +//helper method +void JoystickAndroid::_initStatic() { + //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 + + //int JoystickAndroid::_androidBtnListCount; + _androidBtnListCount = 31; + static int ret[31]; //there are 31 buttons in total accordingy to the API + int i; + //int *JoystickAndroid:: + _androidBtnList = ret; + + for (i=1;i<=16;i++) { + QString name = "KEYCODE_BUTTON_"+QString::number(i); + ret[i-1] = QAndroidJniObject::getStaticField("android/view/KeyEvent", name.toStdString().c_str()); + } + i--; + + ret[i++] = QAndroidJniObject::getStaticField("android/view/KeyEvent", "KEYCODE_BUTTON_A"); + ret[i++] = QAndroidJniObject::getStaticField("android/view/KeyEvent", "KEYCODE_BUTTON_B"); + ret[i++] = QAndroidJniObject::getStaticField("android/view/KeyEvent", "KEYCODE_BUTTON_C"); + ret[i++] = QAndroidJniObject::getStaticField("android/view/KeyEvent", "KEYCODE_BUTTON_L1"); + ret[i++] = QAndroidJniObject::getStaticField("android/view/KeyEvent", "KEYCODE_BUTTON_L2"); + ret[i++] = QAndroidJniObject::getStaticField("android/view/KeyEvent", "KEYCODE_BUTTON_R1"); + ret[i++] = QAndroidJniObject::getStaticField("android/view/KeyEvent", "KEYCODE_BUTTON_R2"); + ret[i++] = QAndroidJniObject::getStaticField("android/view/KeyEvent", "KEYCODE_BUTTON_MODE"); + ret[i++] = QAndroidJniObject::getStaticField("android/view/KeyEvent", "KEYCODE_BUTTON_SELECT"); + ret[i++] = QAndroidJniObject::getStaticField("android/view/KeyEvent", "KEYCODE_BUTTON_START"); + ret[i++] = QAndroidJniObject::getStaticField("android/view/KeyEvent", "KEYCODE_BUTTON_THUMBL"); + ret[i++] = QAndroidJniObject::getStaticField("android/view/KeyEvent", "KEYCODE_BUTTON_THUMBR"); + ret[i++] = QAndroidJniObject::getStaticField("android/view/KeyEvent", "KEYCODE_BUTTON_X"); + ret[i++] = QAndroidJniObject::getStaticField("android/view/KeyEvent", "KEYCODE_BUTTON_Y"); + ret[i++] = QAndroidJniObject::getStaticField("android/view/KeyEvent", "KEYCODE_BUTTON_Z"); + + ACTION_DOWN = QAndroidJniObject::getStaticField("android/view/KeyEvent", "ACTION_DOWN"); + ACTION_UP = QAndroidJniObject::getStaticField("android/view/KeyEvent", "ACTION_UP"); +} + diff --git a/src/Joystick/JoystickAndroid.h b/src/Joystick/JoystickAndroid.h new file mode 100644 index 0000000000000000000000000000000000000000..b50c7f919825977a539ecfc05bbc8df1bd2243bd --- /dev/null +++ b/src/Joystick/JoystickAndroid.h @@ -0,0 +1,50 @@ +#ifndef JOYSTICKANDROID_H +#define JOYSTICKANDROID_H + +#include "Joystick.h" +#include "Vehicle.h" +#include "MultiVehicleManager.h" + +#include +#include +#include +#include +#include + + +class JoystickAndroid : public Joystick, public QtAndroidPrivate::GenericMotionEventListener, public QtAndroidPrivate::KeyEventListener +{ +public: + JoystickAndroid(const QString& name, int axisCount, int buttonCount, int id, MultiVehicleManager* multiVehicleManager); + ~JoystickAndroid(); + + static QMap discover(MultiVehicleManager* _multiVehicleManager); + +private: + bool handleKeyEvent(jobject event); + bool handleGenericMotionEvent(jobject event); + + virtual bool _open(); + virtual void _close(); + virtual bool _update(); + + virtual bool _getButton(int i); + virtual int _getAxis(int i); + virtual uint8_t _getHat(int hat,int i); + + int *btnCode; + int *axisCode; + bool *btnValue; + int *axisValue; + + static void _initStatic(); + static int * _androidBtnList; //list of all possible android buttons + static int _androidBtnListCount; + + static int ACTION_DOWN, ACTION_UP; + static QMutex m_mutex; + + int deviceId; +}; + +#endif // JOYSTICKANDROID_H diff --git a/src/Joystick/JoystickManager.cc b/src/Joystick/JoystickManager.cc index a36f4a64839e38af4e249d6e0e0ba05fc49ed1bf..617d414d2554ac45ed3a3a4ee0169035e004159f 100644 --- a/src/Joystick/JoystickManager.cc +++ b/src/Joystick/JoystickManager.cc @@ -19,10 +19,7 @@ #endif #ifdef __android__ - /* - * Android Joystick not yet supported - * #include "JoystickAndroid.h" - */ + #include "JoystickAndroid.h" #endif QGC_LOGGING_CATEGORY(JoystickManagerLog, "JoystickManagerLog") @@ -55,8 +52,7 @@ void JoystickManager::setToolbox(QGCToolbox *toolbox) QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership); } -void JoystickManager::init() -{ +void JoystickManager::init() { #ifdef __sdljoystick__ if (JoystickSDL::init()) { _setActiveJoystickFromSettings(); @@ -64,9 +60,8 @@ void JoystickManager::init() _joystickCheckTimer.start(250); } #elif defined(__android__) - /* - * Android Joystick not yet supported - */ + _setActiveJoystickFromSettings(); + //TODO: Investigate Android events for Joystick hot plugging & run _joystickCheckTimer if possible #endif } @@ -78,10 +73,7 @@ void JoystickManager::_setActiveJoystickFromSettings(void) // Get the latest joystick mapping newMap = JoystickSDL::discover(_multiVehicleManager); #elif defined(__android__) - /* - * Android Joystick not yet supported - * _name2JoystickMap = JoystickAndroid::discover(_multiVehicleManager); - */ + _name2JoystickMap = JoystickAndroid::discover(_multiVehicleManager); #endif if (_activeJoystick && !newMap.contains(_activeJoystick->name())) { @@ -188,6 +180,9 @@ void JoystickManager::setActiveJoystickName(const QString& name) setActiveJoystick(_name2JoystickMap[name]); } +/* + * TODO: move this to the right place: JoystickSDL.cc and JoystickAndroid.cc respectively and call through Joystick.cc + */ void JoystickManager::_updateAvailableJoysticks(void) { #ifdef __sdljoystick__ @@ -211,7 +206,7 @@ void JoystickManager::_updateAvailableJoysticks(void) } #elif defined(__android__) /* - * Android Joystick not yet supported + * TODO: Investigate Android events for Joystick hot plugging */ #endif } diff --git a/src/Joystick/JoystickSDL.cc b/src/Joystick/JoystickSDL.cc index cf7f105b93f069bfa7d9994db019e8f83e7bfc3e..3a230e4ab25835e03d58ac74d1f33263849b468e 100644 --- a/src/Joystick/JoystickSDL.cc +++ b/src/Joystick/JoystickSDL.cc @@ -68,8 +68,9 @@ QMap JoystickSDL::discover(MultiVehicleManager* _multiVehicl newRet[name] = new JoystickSDL(name, qMax(0,axisCount), qMax(0,buttonCount), qMax(0,hatCount), i, isGameController, _multiVehicleManager); } else { newRet[name] = ret[name]; - if (newRet[name]->index() != i) { - newRet[name]->setIndex(i); // This joystick index has been remapped by SDL + JoystickSDL *j = (JoystickSDL*)newRet[name]; + if (j->index() != i) { + j->setIndex(i); // This joystick index has been remapped by SDL } // Anything left in ret after we exit the loop has been removed (unplugged) and needs to be cleaned up. // We will handle that in JoystickManager in case the removed joystick was in use. diff --git a/src/Joystick/JoystickSDL.h b/src/Joystick/JoystickSDL.h index ba74e0a328f68569ee96ec40bfb0b7b7fa32e279..905b01da4c4cb3de7375cfcb22c707b44254b684 100644 --- a/src/Joystick/JoystickSDL.h +++ b/src/Joystick/JoystickSDL.h @@ -36,6 +36,7 @@ private: SDL_GameController *sdlController; bool _isGameController; int _index; ///< Index for SDL_JoystickOpen + }; #endif // JOYSTICKSDL_H