Commit a3b56659 authored by Don Gagne's avatar Don Gagne Committed by GitHub

Merge pull request #4179 from gregd72002/AndroidJoystick

Android joystick
parents 48370879 a18ca238
......@@ -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 += \
......
......@@ -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);
......
#include "JoystickAndroid.h"
#include "QGCApplication.h"
#include <QQmlEngine>
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<jbooleanArray>();
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<jint>("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<QString, Joystick*> JoystickAndroid::discover(MultiVehicleManager* _multiVehicleManager) {
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;
QAndroidJniObject o = QAndroidJniObject::callStaticObjectMethod<jintArray>("android/view/InputDevice", "getDeviceIds");
jintArray jarr = o.object<jintArray>();
int sz = env->GetArrayLength(jarr);
jint *buff = env->GetIntArrayElements(jarr, nullptr);
int SOURCE_GAMEPAD = QAndroidJniObject::getStaticField<jint>("android/view/InputDevice", "SOURCE_GAMEPAD");
int SOURCE_JOYSTICK = QAndroidJniObject::getStaticField<jint>("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<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
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<jint>("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<jbooleanArray>();
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<jint>("getDeviceId", "()I");
if (_deviceId!=deviceId) return false;
const int action = ev.callMethod<jint>("getAction", "()I");
const int keyCode = ev.callMethod<jint>("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<jint>("getDeviceId", "()I");
if (_deviceId!=deviceId) return false;
for (int i=0;i<_axisCount;i++) {
const float v = ev.callMethod<jfloat>("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<jint>("android/view/KeyEvent", name.toStdString().c_str());
}
i--;
ret[i++] = QAndroidJniObject::getStaticField<jint>("android/view/KeyEvent", "KEYCODE_BUTTON_A");
ret[i++] = QAndroidJniObject::getStaticField<jint>("android/view/KeyEvent", "KEYCODE_BUTTON_B");
ret[i++] = QAndroidJniObject::getStaticField<jint>("android/view/KeyEvent", "KEYCODE_BUTTON_C");
ret[i++] = QAndroidJniObject::getStaticField<jint>("android/view/KeyEvent", "KEYCODE_BUTTON_L1");
ret[i++] = QAndroidJniObject::getStaticField<jint>("android/view/KeyEvent", "KEYCODE_BUTTON_L2");
ret[i++] = QAndroidJniObject::getStaticField<jint>("android/view/KeyEvent", "KEYCODE_BUTTON_R1");
ret[i++] = QAndroidJniObject::getStaticField<jint>("android/view/KeyEvent", "KEYCODE_BUTTON_R2");
ret[i++] = QAndroidJniObject::getStaticField<jint>("android/view/KeyEvent", "KEYCODE_BUTTON_MODE");
ret[i++] = QAndroidJniObject::getStaticField<jint>("android/view/KeyEvent", "KEYCODE_BUTTON_SELECT");
ret[i++] = QAndroidJniObject::getStaticField<jint>("android/view/KeyEvent", "KEYCODE_BUTTON_START");
ret[i++] = QAndroidJniObject::getStaticField<jint>("android/view/KeyEvent", "KEYCODE_BUTTON_THUMBL");
ret[i++] = QAndroidJniObject::getStaticField<jint>("android/view/KeyEvent", "KEYCODE_BUTTON_THUMBR");
ret[i++] = QAndroidJniObject::getStaticField<jint>("android/view/KeyEvent", "KEYCODE_BUTTON_X");
ret[i++] = QAndroidJniObject::getStaticField<jint>("android/view/KeyEvent", "KEYCODE_BUTTON_Y");
ret[i++] = QAndroidJniObject::getStaticField<jint>("android/view/KeyEvent", "KEYCODE_BUTTON_Z");
ACTION_DOWN = QAndroidJniObject::getStaticField<jint>("android/view/KeyEvent", "ACTION_DOWN");
ACTION_UP = QAndroidJniObject::getStaticField<jint>("android/view/KeyEvent", "ACTION_UP");
}
#ifndef JOYSTICKANDROID_H
#define JOYSTICKANDROID_H
#include "Joystick.h"
#include "Vehicle.h"
#include "MultiVehicleManager.h"
#include <jni.h>
#include <QtCore/private/qjni_p.h>
#include <QtCore/private/qjnihelpers_p.h>
#include <QtAndroidExtras/QtAndroidExtras>
#include <QtAndroidExtras/QAndroidJniObject>
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<QString, Joystick*> 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
......@@ -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
}
......@@ -68,8 +68,9 @@ QMap<QString, Joystick*> 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.
......
......@@ -36,6 +36,7 @@ private:
SDL_GameController *sdlController;
bool _isGameController;
int _index; ///< Index for SDL_JoystickOpen
};
#endif // JOYSTICKSDL_H
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment