diff options
-rw-r--r-- | .qmake.conf | 2 | ||||
-rw-r--r-- | src/plugins/position/android/jar/src/org/qtproject/qt5/android/positioning/QtPositioning.java | 92 | ||||
-rw-r--r-- | src/plugins/position/android/src/jnipositioning.cpp | 162 |
3 files changed, 233 insertions, 23 deletions
diff --git a/.qmake.conf b/.qmake.conf index a6fe7a56..092e259b 100644 --- a/.qmake.conf +++ b/.qmake.conf @@ -3,7 +3,7 @@ CONFIG += warning_clean DEFINES += QT_NO_JAVA_STYLE_ITERATORS QT_NO_LINKED_LIST -MODULE_VERSION = 5.15.12 +MODULE_VERSION = 5.15.13 # Adds a way to debug location. The define is needed for multiple subprojects as they # include the essential headers. diff --git a/src/plugins/position/android/jar/src/org/qtproject/qt5/android/positioning/QtPositioning.java b/src/plugins/position/android/jar/src/org/qtproject/qt5/android/positioning/QtPositioning.java index d819e627..3df31d70 100644 --- a/src/plugins/position/android/jar/src/org/qtproject/qt5/android/positioning/QtPositioning.java +++ b/src/plugins/position/android/jar/src/org/qtproject/qt5/android/positioning/QtPositioning.java @@ -45,9 +45,12 @@ import android.location.GpsStatus; import android.location.Location; import android.location.LocationListener; import android.location.LocationManager; +import android.location.GnssStatus; +import android.location.GnssStatus.Callback; import android.os.Bundle; import android.os.Handler; import android.os.Looper; +import android.os.Build; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; @@ -97,7 +100,7 @@ public class QtPositioning implements LocationListener /* If true this class acts as satellite signal monitor rather than location monitor */ private boolean isSatelliteUpdate = false; - private PositioningLooper looperThread; + private PositioningLooperBase looperThread; static public void setContext(Context context) { @@ -404,7 +407,12 @@ public class QtPositioning implements LocationListener public QtPositioning() { - looperThread = new PositioningLooper(); + // Use GpsStatus for API Level <= 23 (version M and below) and + // GnssStatus for other API levels. + if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.M) + looperThread = new PositioningLooperGps(); + else + looperThread = new PositioningLooperGnss(); } public Looper looper() @@ -438,13 +446,16 @@ public class QtPositioning implements LocationListener } } - private class PositioningLooper extends Thread implements GpsStatus.Listener{ + private abstract class PositioningLooperBase extends Thread + { private boolean looperRunning; private Looper posLooper; private boolean isSatelliteLooper = false; - private LocationManager locManager = null; - private PositioningLooper() + abstract protected void addSatelliteInfoListener(); + abstract protected void removeSatelliteInfoListener(); + + private PositioningLooperBase() { looperRunning = false; } @@ -454,13 +465,8 @@ public class QtPositioning implements LocationListener Looper.prepare(); Handler handler = new Handler(); - if (isSatelliteLooper) { - try { - locationManager.addGpsStatusListener(this); - } catch(Exception e) { - e.printStackTrace(); - } - } + if (isSatelliteLooper) + addSatelliteInfoListener(); posLooper = Looper.myLooper(); synchronized (this) { @@ -475,7 +481,7 @@ public class QtPositioning implements LocationListener public void quitLooper() { if (isSatelliteLooper) - locationManager.removeGpsStatusListener(this); + removeSatelliteInfoListener(); looper().quit(); } @@ -494,6 +500,26 @@ public class QtPositioning implements LocationListener return posLooper; } + } + + private class PositioningLooperGps extends PositioningLooperBase implements GpsStatus.Listener + { + @Override + protected void addSatelliteInfoListener() + { + try { + locationManager.addGpsStatusListener(this); + } catch(Exception e) { + e.printStackTrace(); + } + } + + @Override + protected void removeSatelliteInfoListener() + { + locationManager.removeGpsStatusListener(this); + } + @Override public void onGpsStatusChanged(int event) { switch (event) { @@ -510,7 +536,7 @@ public class QtPositioning implements LocationListener list.add(sat); } GpsSatellite[] sats = list.toArray(new GpsSatellite[list.size()]); - satelliteUpdated(sats, nativeClassReference, isSingleUpdate); + satelliteGpsUpdated(sats, nativeClassReference, isSingleUpdate); break; case GpsStatus.GPS_EVENT_STARTED: @@ -521,10 +547,46 @@ public class QtPositioning implements LocationListener } } + private class PositioningGnssListener extends GnssStatus.Callback + { + @Override + public void onSatelliteStatusChanged(GnssStatus status) + { + satelliteGnssUpdated(status, nativeClassReference, isSingleUpdate); + } + } + + private class PositioningLooperGnss extends PositioningLooperBase + { + private PositioningGnssListener gnssListener; + + private PositioningLooperGnss() + { + gnssListener = new PositioningGnssListener(); + } + + @Override + protected void addSatelliteInfoListener() + { + try { + locationManager.registerGnssStatusCallback(gnssListener); + } catch(Exception e) { + e.printStackTrace(); + } + } + + @Override + protected void removeSatelliteInfoListener() + { + locationManager.unregisterGnssStatusCallback(gnssListener); + } + } + public static native void positionUpdated(Location update, int androidClassKey, boolean isSingleUpdate); public static native void locationProvidersDisabled(int androidClassKey); public static native void locationProvidersChanged(int androidClassKey); - public static native void satelliteUpdated(GpsSatellite[] update, int androidClassKey, boolean isSingleUpdate); + public static native void satelliteGpsUpdated(GpsSatellite[] update, int androidClassKey, boolean isSingleUpdate); + public static native void satelliteGnssUpdated(GnssStatus update, int androidClassKey, boolean isSingleUpdate); @Override public void onLocationChanged(Location location) { diff --git a/src/plugins/position/android/src/jnipositioning.cpp b/src/plugins/position/android/src/jnipositioning.cpp index 8854a677..8e38d55c 100644 --- a/src/plugins/position/android/src/jnipositioning.cpp +++ b/src/plugins/position/android/src/jnipositioning.cpp @@ -64,6 +64,58 @@ static const char logTag[] = "QtPositioning"; static const char classErrorMsg[] = "Can't find class \"%s\""; static const char methodErrorMsg[] = "Can't find method \"%s%s\""; +namespace { + +/*! + \internal + This class encapsulates satellite system types, as defined by Android + GnssStatus API. Initialize during JNI_OnLoad() by the init() method, from + the Java side, rather than hard-coding. +*/ +class ConstellationMapper +{ +public: + static bool init(JNIEnv *jniEnv) + { + if (QtAndroidPrivate::androidSdkVersion() > 23) { + jclass gnssStatusObject = jniEnv->FindClass("android/location/GnssStatus"); + if (!gnssStatusObject) + return false; + + jfieldID gpsFieldId = jniEnv->GetStaticFieldID(gnssStatusObject, + "CONSTELLATION_GPS", "I"); + jfieldID glonassFieldId = jniEnv->GetStaticFieldID(gnssStatusObject, + "CONSTELLATION_GLONASS", "I"); + if (!gpsFieldId || !glonassFieldId) + return false; + + m_gpsId = jniEnv->GetStaticIntField(gnssStatusObject, gpsFieldId); + m_glonassId = jniEnv->GetStaticIntField(gnssStatusObject, glonassFieldId); + } + // no need to query it for API level <= 23 + return true; + } + + static QGeoSatelliteInfo::SatelliteSystem toSatelliteSystem(int constellationType) + { + if (constellationType == m_gpsId) + return QGeoSatelliteInfo::GPS; + else if (constellationType == m_glonassId) + return QGeoSatelliteInfo::GLONASS; + + return QGeoSatelliteInfo::Undefined; + } + +private: + static int m_gpsId; + static int m_glonassId; +}; + +int ConstellationMapper::m_gpsId = -1; +int ConstellationMapper::m_glonassId = -1; + +} // anonymous namespace + namespace AndroidPositioning { typedef QMap<int, QGeoPositionInfoSourceAndroid * > PositionSourceMap; typedef QMap<int, QGeoSatelliteInfoSourceAndroid * > SatelliteSourceMap; @@ -362,6 +414,74 @@ namespace AndroidPositioning { return sats; } + QList<QGeoSatelliteInfo> satelliteInfoFromJavaGnssStatus(JNIEnv *jniEnv, jobject gnssStatus, + QList<QGeoSatelliteInfo>* usedInFix) + { + QList<QGeoSatelliteInfo> sats; + + jclass statusClass = jniEnv->GetObjectClass(gnssStatus); + if (!statusClass) + return sats; + + jmethodID satCountMethod = getCachedMethodID(jniEnv, statusClass, + "getSatelliteCount", "()I"); + jmethodID sigStrengthMethod = getCachedMethodID(jniEnv, statusClass, "getCn0DbHz", "(I)F"); + jmethodID constTypeMethod = getCachedMethodID(jniEnv, statusClass, + "getConstellationType", "(I)I"); + jmethodID svIdMethod = getCachedMethodID(jniEnv, statusClass, "getSvid", "(I)I"); + jmethodID azimuthMethod = getCachedMethodID(jniEnv, statusClass, + "getAzimuthDegrees", "(I)F"); + jmethodID elevationMethod = getCachedMethodID(jniEnv, statusClass, + "getElevationDegrees", "(I)F"); + jmethodID usedInFixMethod = getCachedMethodID(jniEnv, statusClass, + "usedInFix", "(I)Z"); + + if (!satCountMethod || !sigStrengthMethod || !constTypeMethod || !svIdMethod + || !azimuthMethod || !elevationMethod || !usedInFixMethod) { + jniEnv->DeleteLocalRef(statusClass); + return sats; + } + + const int satellitesCount = jniEnv->CallIntMethod(gnssStatus, satCountMethod); + for (int i = 0; i < satellitesCount; ++i) { + QGeoSatelliteInfo info; + + // signal strength - this is actually a carrier-to-noise density, + // but the values are very close to what was previously returned by + // getSnr() method of the GpsSatellite API. + const jfloat cn0 = jniEnv->CallFloatMethod(gnssStatus, sigStrengthMethod, i); + info.setSignalStrength(static_cast<int>(cn0)); + + // satellite system + const jint constellationType = jniEnv->CallIntMethod(gnssStatus, constTypeMethod, i); + info.setSatelliteSystem(ConstellationMapper::toSatelliteSystem(constellationType)); + + // satellite identifier + const jint svId = jniEnv->CallIntMethod(gnssStatus, svIdMethod, i); + info.setSatelliteIdentifier(svId); + + // azimuth + const jfloat azimuth = jniEnv->CallFloatMethod(gnssStatus, azimuthMethod, i); + info.setAttribute(QGeoSatelliteInfo::Azimuth, static_cast<qreal>(azimuth)); + + // elevation + const jfloat elevation = jniEnv->CallFloatMethod(gnssStatus, elevationMethod, i); + info.setAttribute(QGeoSatelliteInfo::Elevation, static_cast<qreal>(elevation)); + + // Used in fix - true if this satellite is actually used in + // determining the position. + const jboolean inFix = jniEnv->CallBooleanMethod(gnssStatus, usedInFixMethod, i); + + sats.append(info); + + if (inFix) + usedInFix->append(info); + } + + jniEnv->DeleteLocalRef(statusClass); + return sats; + } + QGeoPositionInfo lastKnownPosition(bool fromSatellitePositioningMethodsOnly) { AttachedJNIEnv env; @@ -562,11 +682,10 @@ static void locationProvidersChanged(JNIEnv *env, jobject /*thiz*/, jint android QMetaObject::invokeMethod(source, "locationProvidersChanged", Qt::AutoConnection); } -static void satelliteUpdated(JNIEnv *env, jobject /*thiz*/, jobjectArray satellites, jint androidClassKey, jboolean isSingleUpdate) +static void notifySatelliteInfoUpdated(const QList<QGeoSatelliteInfo> &inView, + const QList<QGeoSatelliteInfo> &inUse, + jint androidClassKey, jboolean isSingleUpdate) { - QList<QGeoSatelliteInfo> inUse; - QList<QGeoSatelliteInfo> sats = AndroidPositioning::satelliteInfoFromJavaLocation(env, satellites, &inUse); - QGeoSatelliteInfoSourceAndroid *source = AndroidPositioning::idToSatSource()->value(androidClassKey); if (!source) { qWarning("satelliteUpdated: source == 0"); @@ -574,12 +693,34 @@ static void satelliteUpdated(JNIEnv *env, jobject /*thiz*/, jobjectArray satelli } QMetaObject::invokeMethod(source, "processSatelliteUpdateInView", Qt::AutoConnection, - Q_ARG(QList<QGeoSatelliteInfo>, sats), Q_ARG(bool, isSingleUpdate)); + Q_ARG(QList<QGeoSatelliteInfo>, inView), Q_ARG(bool, isSingleUpdate)); QMetaObject::invokeMethod(source, "processSatelliteUpdateInUse", Qt::AutoConnection, Q_ARG(QList<QGeoSatelliteInfo>, inUse), Q_ARG(bool, isSingleUpdate)); } +static void satelliteGpsUpdated(JNIEnv *env, jobject thiz, jobjectArray satellites, + jint androidClassKey, jboolean isSingleUpdate) +{ + Q_UNUSED(thiz); + QList<QGeoSatelliteInfo> inUse; + QList<QGeoSatelliteInfo> sats = + AndroidPositioning::satelliteInfoFromJavaLocation(env, satellites, &inUse); + + notifySatelliteInfoUpdated(sats, inUse, androidClassKey, isSingleUpdate); +} + +static void satelliteGnssUpdated(JNIEnv *env, jobject thiz, jobject gnssStatus, + jint androidClassKey, jboolean isSingleUpdate) +{ + Q_UNUSED(thiz); + QList<QGeoSatelliteInfo> inUse; + QList<QGeoSatelliteInfo> sats = + AndroidPositioning::satelliteInfoFromJavaGnssStatus(env, gnssStatus, &inUse); + + notifySatelliteInfoUpdated(sats, inUse, androidClassKey, isSingleUpdate); +} + #define FIND_AND_CHECK_CLASS(CLASS_NAME) \ clazz = env->FindClass(CLASS_NAME); \ @@ -598,8 +739,9 @@ if (!VAR) { \ static JNINativeMethod methods[] = { {"positionUpdated", "(Landroid/location/Location;IZ)V", (void *)positionUpdated}, {"locationProvidersDisabled", "(I)V", (void *) locationProvidersDisabled}, - {"satelliteUpdated", "([Landroid/location/GpsSatellite;IZ)V", (void *)satelliteUpdated}, - {"locationProvidersChanged", "(I)V", (void *) locationProvidersChanged} + {"satelliteGpsUpdated", "([Landroid/location/GpsSatellite;IZ)V", (void *)satelliteGpsUpdated}, + {"locationProvidersChanged", "(I)V", (void *) locationProvidersChanged}, + {"satelliteGnssUpdated", "(Landroid/location/GnssStatus;IZ)V", (void *)satelliteGnssUpdated} }; static bool registerNatives(JNIEnv *env) @@ -650,6 +792,12 @@ Q_DECL_EXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void * /*reserved*/) return -1; } + if (!ConstellationMapper::init(env)) { + __android_log_print(ANDROID_LOG_ERROR, logTag, + "Failed to extract constellation type constants. " + "Satellite system will be undefined!"); + } + javaVM = vm; return JNI_VERSION_1_6; } |