diff options
author | Alex Blasche <alexander.blasche@digia.com> | 2013-11-21 11:05:57 +0100 |
---|---|---|
committer | The Qt Project <gerrit-noreply@qt-project.org> | 2014-01-03 15:46:19 +0100 |
commit | 53ad249bc24b27867a975a886902672f1c1e8333 (patch) | |
tree | 93ae1eb329858228f7319f265e3dadc3a56b267c /src/plugins/position/android | |
parent | 954b92e1207bfe5ab5a117e8393c191cdf0044d2 (diff) | |
download | qtlocation-53ad249bc24b27867a975a886902672f1c1e8333.tar.gz |
QGeoSatelliteInfoSource backend for Android
Android supports status enquiries about GPS/GLONASS satellites in range.
Task-number: QTBUG-34102
[ChangeLog][QtPositioning][QGeoSatelliteInfoSource] Android backend added.
Android devices can retrieve information about the currently accessible
GPS and GLONASS satellites.
Change-Id: Ia3627fdcf948586f9e6f04aa64bfc9548fc895e0
Reviewed-by: Eskil Abrahamsen Blomfeldt <eskil.abrahamsen-blomfeldt@digia.com>
Reviewed-by: Alex Blasche <alexander.blasche@digia.com>
Diffstat (limited to 'src/plugins/position/android')
8 files changed, 590 insertions, 32 deletions
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 ac510793..1c6a755c 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 @@ -43,13 +43,17 @@ package org.qtproject.qt5.android.positioning; import android.app.Activity; import android.content.Context; +import android.location.GpsSatellite; +import android.location.GpsStatus; import android.location.Location; import android.location.LocationListener; import android.location.LocationManager; import android.os.Bundle; import android.os.Handler; import android.os.Looper; +import java.util.ArrayList; import java.util.HashMap; +import java.util.Iterator; import java.util.List; import android.util.Log; @@ -66,7 +70,7 @@ public class QtPositioning implements LocationListener The positionInfo instance to which this QtPositioning instance is attached to. */ - private int positionInfoSource = 0; + private int nativeClassReference = 0; /* The provider type requested by Qt: @@ -85,6 +89,8 @@ public class QtPositioning implements LocationListener private Location lastGps = null; /* The last received network update */ private Location lastNetwork = null; + /* If true this class acts as satellite signal monitor rather than location monitor */ + private boolean isSatelliteUpdate = false; private PositioningLooper looperThread; @@ -174,8 +180,9 @@ public class QtPositioning implements LocationListener boolean exceptionOccurred = false; QtPositioning positioningListener = new QtPositioning(); positioningListener.setActiveLooper(true); - positioningListener.positionInfoSource = androidClassKey; + positioningListener.nativeClassReference = androidClassKey; positioningListener.expectedProviders = locationProvider; + positioningListener.isSatelliteUpdate = false; if (updateInterval == 0) updateInterval = 1000; //don't update more often than once per second @@ -249,9 +256,10 @@ public class QtPositioning implements LocationListener boolean exceptionOccurred = false; QtPositioning positioningListener = new QtPositioning(); positioningListener.setActiveLooper(true); - positioningListener.positionInfoSource = androidClassKey; + positioningListener.nativeClassReference = androidClassKey; positioningListener.isSingleUpdate = true; positioningListener.expectedProviders = locationProvider; + positioningListener.isSatelliteUpdate = false; if ((locationProvider & 1) > 0) { Log.d(TAG, "Single update using GPS"); @@ -298,6 +306,57 @@ public class QtPositioning implements LocationListener } } + static public int startSatelliteUpdates(int androidClassKey, int updateInterval, boolean isSingleRequest) + { + synchronized (m_syncObject) { + try { + boolean exceptionOccurred = false; + QtPositioning positioningListener = new QtPositioning(); + positioningListener.isSatelliteUpdate = true; + positioningListener.setActiveLooper(true); + positioningListener.nativeClassReference = androidClassKey; + positioningListener.expectedProviders = 1; //always satellite provider + positioningListener.isSingleUpdate = isSingleRequest; + + if (updateInterval == 0) + updateInterval = 1000; //don't update more often than once per second + + if (isSingleRequest) + Log.d(TAG, "Single update for Satellites " + updateInterval); + else + Log.d(TAG, "Regular updates for Satellites " + updateInterval); + try { + locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, + updateInterval, 0, + positioningListener, + positioningListener.looper()); + } catch (SecurityException se) { + se.printStackTrace(); + exceptionOccurred = true; + } + + if (exceptionOccurred) { + positioningListener.setActiveLooper(false); + locationManager.removeUpdates(positioningListener); + return 0; //QGeoSatelliteInfoSource::AccessError + } + + if (!expectedProvidersAvailable(positioningListener.expectedProviders)) { + //all location providers unavailable -> when they come back we resume automatically + //in the mean time return ClosedError + return 1; //QGeoSatelliteInfoSource::ClosedError + } + + runningListeners.put(androidClassKey, positioningListener); + } catch(Exception e) { + e.printStackTrace(); + return -1; //QGeoSatelliteInfoSource::UnknownSourceError + } + + return 2; //QGeoSatelliteInfoSource::NoError + } + } + public QtPositioning() { looperThread = new PositioningLooper(); @@ -315,6 +374,8 @@ public class QtPositioning implements LocationListener if (looperThread.isAlive()) return; + looperThread.isSatelliteListener(true); + looperThread.start(); while (!looperThread.isReady()); Thread.sleep(1000); @@ -326,9 +387,11 @@ public class QtPositioning implements LocationListener } } - private class PositioningLooper extends Thread { + private class PositioningLooper extends Thread implements GpsStatus.Listener{ private boolean looperRunning; private Looper posLooper; + private boolean isSatelliteLooper = false; + private LocationManager locManager = null; private PositioningLooper() { @@ -340,6 +403,15 @@ public class QtPositioning implements LocationListener Looper.prepare(); Handler handler = new Handler(); looperRunning = true; + + if (isSatelliteLooper) { + try { + locationManager.addGpsStatusListener(this); + } catch(Exception e) { + e.printStackTrace(); + } + } + posLooper = Looper.myLooper(); Looper.loop(); looperRunning = false; @@ -347,6 +419,8 @@ public class QtPositioning implements LocationListener public void quitLooper() { + if (isSatelliteLooper) + locationManager.removeGpsStatusListener(this); looper().quit(); } @@ -355,16 +429,48 @@ public class QtPositioning implements LocationListener return looperRunning; } + public void isSatelliteListener(boolean isListener) + { + isSatelliteLooper = isListener; + } + public Looper looper() { return posLooper; } + + @Override + public void onGpsStatusChanged(int event) { + switch (event) { + case GpsStatus.GPS_EVENT_FIRST_FIX: + break; + case GpsStatus.GPS_EVENT_SATELLITE_STATUS: + GpsStatus status = locationManager.getGpsStatus(null); + Iterable<GpsSatellite> iterable = status.getSatellites(); + Iterator<GpsSatellite> it = iterable.iterator(); + + ArrayList<GpsSatellite> list = new ArrayList<GpsSatellite>(); + while (it.hasNext()) { + GpsSatellite sat = (GpsSatellite) it.next(); + list.add(sat); + } + GpsSatellite[] sats = list.toArray(new GpsSatellite[list.size()]); + satelliteUpdated(sats, nativeClassReference, isSingleUpdate); + + break; + case GpsStatus.GPS_EVENT_STARTED: + break; + case GpsStatus.GPS_EVENT_STOPPED: + break; + } + } } public static native void positionUpdated(Location update, int androidClassKey, boolean isSingleUpdate); public static native void locationProvidersDisabled(int androidClassKey); + public static native void satelliteUpdated(GpsSatellite[] update, int androidClassKey, boolean isSingleUpdate); @Override public void onLocationChanged(Location location) { @@ -372,8 +478,11 @@ public class QtPositioning implements LocationListener if (location == null) return; + if (isSatelliteUpdate) //we are a QGeoSatelliteInfoSource -> ignore + return; + if (isSingleUpdate || expectedProviders < 3) { - positionUpdated(location, positionInfoSource, isSingleUpdate); + positionUpdated(location, nativeClassReference, isSingleUpdate); return; } @@ -387,12 +496,12 @@ public class QtPositioning implements LocationListener lastGps = location; // assumption: GPS always better -> pass it on - positionUpdated(location, positionInfoSource, isSingleUpdate); + positionUpdated(location, nativeClassReference, isSingleUpdate); } else if (location.getProvider().equals(LocationManager.NETWORK_PROVIDER)) { lastNetwork = location; if (lastGps == null) { //no GPS fix yet use network location - positionUpdated(location, positionInfoSource, isSingleUpdate); + positionUpdated(location, nativeClassReference, isSingleUpdate); return; } @@ -404,7 +513,7 @@ public class QtPositioning implements LocationListener return; // Use network data -> GPS has timed out on updateInterval - positionUpdated(location, positionInfoSource, isSingleUpdate); + positionUpdated(location, nativeClassReference, isSingleUpdate); } } @@ -420,6 +529,6 @@ public class QtPositioning implements LocationListener public void onProviderDisabled(String provider) { Log.d(TAG, "Disabled provider: " + provider); if (!expectedProvidersAvailable(expectedProviders)) - locationProvidersDisabled(positionInfoSource); - } + locationProvidersDisabled(nativeClassReference); + } } diff --git a/src/plugins/position/android/src/jnipositioning.cpp b/src/plugins/position/android/src/jnipositioning.cpp index 035fb760..67b9fe4a 100644 --- a/src/plugins/position/android/src/jnipositioning.cpp +++ b/src/plugins/position/android/src/jnipositioning.cpp @@ -40,12 +40,14 @@ ****************************************************************************/ #include <QDateTime> +#include <QDebug> #include <QMap> #include <QtGlobal> #include <android/log.h> #include <jni.h> #include <QGeoPositionInfo> #include "qgeopositioninfosource_android_p.h" +#include "qgeosatelliteinfosource_android_p.h" #include "jnipositioning.h" @@ -57,14 +59,19 @@ static jmethodID lastKnownPositionMethodId; static jmethodID startUpdatesMethodId; static jmethodID stopUpdatesMethodId; static jmethodID requestUpdateMethodId; +static jmethodID startSatelliteUpdatesMethodId; 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 AndroidPositioning { -typedef QMap<int, QGeoPositionInfoSourceAndroid * > PositionSourceMap; -Q_GLOBAL_STATIC(PositionSourceMap, idToSource) + typedef QMap<int, QGeoPositionInfoSourceAndroid * > PositionSourceMap; + typedef QMap<int, QGeoSatelliteInfoSourceAndroid * > SatelliteSourceMap; + + Q_GLOBAL_STATIC(PositionSourceMap, idToPosSource) + + Q_GLOBAL_STATIC(SatelliteSourceMap, idToSatSource) struct AttachedJNIEnv { @@ -90,7 +97,7 @@ Q_GLOBAL_STATIC(PositionSourceMap, idToSource) JNIEnv *jniEnv; }; - int registerPositionInfoSource(QGeoPositionInfoSourceAndroid *src) + int registerPositionInfoSource(QObject *obj) { static bool firstInit = true; if (firstInit) { @@ -98,18 +105,32 @@ Q_GLOBAL_STATIC(PositionSourceMap, idToSource) firstInit = false; } - int key; - do { - key = qrand(); - } while (idToSource()->contains(key)); + int key = -1; + if (obj->inherits("QGeoPositionInfoSource")) { + QGeoPositionInfoSourceAndroid *src = qobject_cast<QGeoPositionInfoSourceAndroid *>(obj); + Q_ASSERT(src); + do { + key = qrand(); + } while (idToPosSource()->contains(key)); + + idToPosSource()->insert(key, src); + } else if (obj->inherits("QGeoSatelliteInfoSource")) { + QGeoSatelliteInfoSourceAndroid *src = qobject_cast<QGeoSatelliteInfoSourceAndroid *>(obj); + Q_ASSERT(src); + do { + key = qrand(); + } while (idToSatSource()->contains(key)); + + idToSatSource()->insert(key, src); + } - idToSource()->insert(key, src); return key; } void unregisterPositionInfoSource(int key) { - idToSource()->remove(key); + idToPosSource()->remove(key); + idToSatSource()->remove(key); } enum PositionProvider @@ -210,6 +231,70 @@ Q_GLOBAL_STATIC(PositionSourceMap, idToSource) return info; } + QList<QGeoSatelliteInfo> satelliteInfoFromJavaLocation(JNIEnv *jniEnv, + jobjectArray satellites, + QList<QGeoSatelliteInfo>* usedInFix) + { + QList<QGeoSatelliteInfo> sats; + jsize length = jniEnv->GetArrayLength(satellites); + for (int i = 0; i<length; i++) { + jobject element = jniEnv->GetObjectArrayElement(satellites, i); + if (jniEnv->ExceptionOccurred()) { + qWarning() << "Cannot process all satellite data due to exception."; + break; + } + + jclass thisClass = jniEnv->GetObjectClass(element); + if (!thisClass) + continue; + + QGeoSatelliteInfo info; + + //signal strength + jmethodID mid = jniEnv->GetMethodID(thisClass, "getSnr", "()F"); + jfloat snr = jniEnv->CallFloatMethod(element, mid); + info.setSignalStrength((int)snr); + + //ignore any satellite with no signal whatsoever + if (qFuzzyIsNull(snr)) + continue; + + //prn + mid = jniEnv->GetMethodID(thisClass, "getPrn", "()I"); + jint prn = jniEnv->CallIntMethod(element, mid); + info.setSatelliteIdentifier(prn); + + if (prn >= 1 && prn <= 32) + info.setSatelliteSystem(QGeoSatelliteInfo::GPS); + else if (prn >= 65 && prn <= 96) + info.setSatelliteSystem(QGeoSatelliteInfo::GLONASS); + + //azimuth + mid = jniEnv->GetMethodID(thisClass, "getAzimuth", "()F"); + jfloat azimuth = jniEnv->CallFloatMethod(element, mid); + info.setAttribute(QGeoSatelliteInfo::Azimuth, azimuth); + + //elevation + mid = jniEnv->GetMethodID(thisClass, "getElevation", "()F"); + jfloat elevation = jniEnv->CallFloatMethod(element, mid); + info.setAttribute(QGeoSatelliteInfo::Elevation, elevation); + + //used in a fix + mid = jniEnv->GetMethodID(thisClass, "usedInFix", "()Z"); + jboolean inFix = jniEnv->CallBooleanMethod(element, mid); + + sats.append(info); + + if (inFix) + usedInFix->append(info); + + jniEnv->DeleteLocalRef(thisClass); + jniEnv->DeleteLocalRef(element); + } + + return sats; + } + QGeoPositionInfo lastKnownPosition(bool fromSatellitePositioningMethodsOnly) { AttachedJNIEnv env; @@ -242,7 +327,7 @@ Q_GLOBAL_STATIC(PositionSourceMap, idToSource) if (!env.jniEnv) return QGeoPositionInfoSource::UnknownSourceError; - QGeoPositionInfoSourceAndroid *source = AndroidPositioning::idToSource()->value(androidClassKey); + QGeoPositionInfoSourceAndroid *source = AndroidPositioning::idToPosSource()->value(androidClassKey); if (source) { int errorCode = env.jniEnv->CallStaticIntMethod(positioningClass, startUpdatesMethodId, @@ -279,7 +364,7 @@ Q_GLOBAL_STATIC(PositionSourceMap, idToSource) if (!env.jniEnv) return QGeoPositionInfoSource::UnknownSourceError; - QGeoPositionInfoSourceAndroid *source = AndroidPositioning::idToSource()->value(androidClassKey); + QGeoPositionInfoSourceAndroid *source = AndroidPositioning::idToPosSource()->value(androidClassKey); if (source) { int errorCode = env.jniEnv->CallStaticIntMethod(positioningClass, requestUpdateMethodId, @@ -297,6 +382,35 @@ Q_GLOBAL_STATIC(PositionSourceMap, idToSource) } return QGeoPositionInfoSource::UnknownSourceError; } + + QGeoSatelliteInfoSource::Error startSatelliteUpdates(int androidClassKey, bool isSingleRequest, int requestTimeout) + { + AttachedJNIEnv env; + if (!env.jniEnv) + return QGeoSatelliteInfoSource::UnknownSourceError; + + QGeoSatelliteInfoSourceAndroid *source = AndroidPositioning::idToSatSource()->value(androidClassKey); + + if (source) { + int interval = source->updateInterval(); + if (isSingleRequest) + interval = requestTimeout; + int errorCode = env.jniEnv->CallStaticIntMethod(positioningClass, startSatelliteUpdatesMethodId, + androidClassKey, + interval, isSingleRequest); + switch (errorCode) { + case -1: + case 0: + case 1: + case 2: + return static_cast<QGeoSatelliteInfoSource::Error>(errorCode); + default: + qWarning() << "startSatelliteUpdates: Unknown error code " << errorCode; + break; + } + } + return QGeoSatelliteInfoSource::UnknownSourceError; + } } @@ -304,7 +418,7 @@ static void positionUpdated(JNIEnv *env, jobject /*thiz*/, jobject location, jin { QGeoPositionInfo info = AndroidPositioning::positionInfoFromJavaLocation(env, location); - QGeoPositionInfoSourceAndroid *source = AndroidPositioning::idToSource()->value(androidClassKey); + QGeoPositionInfoSourceAndroid *source = AndroidPositioning::idToPosSource()->value(androidClassKey); if (!source) { qFatal("positionUpdated: source == 0"); return; @@ -317,14 +431,14 @@ static void positionUpdated(JNIEnv *env, jobject /*thiz*/, jobject location, jin else QMetaObject::invokeMethod(source, "processSinglePositionUpdate", Qt::AutoConnection, Q_ARG(QGeoPositionInfo, info)); - - } static void locationProvidersDisabled(JNIEnv *env, jobject /*thiz*/, jint androidClassKey) { Q_UNUSED(env); - QGeoPositionInfoSourceAndroid *source = AndroidPositioning::idToSource()->value(androidClassKey); + QObject *source = AndroidPositioning::idToPosSource()->value(androidClassKey); + if (!source) + source = AndroidPositioning::idToSatSource()->value(androidClassKey); if (!source) { qFatal("locationProvidersDisabled: source == 0"); return; @@ -333,6 +447,24 @@ static void locationProvidersDisabled(JNIEnv *env, jobject /*thiz*/, jint androi QMetaObject::invokeMethod(source, "locationProviderDisabled", Qt::AutoConnection); } +static void satelliteUpdated(JNIEnv *env, jobject /*thiz*/, jobjectArray satellites, jint androidClassKey, jboolean isSingleUpdate) +{ + QList<QGeoSatelliteInfo> inUse; + QList<QGeoSatelliteInfo> sats = AndroidPositioning::satelliteInfoFromJavaLocation(env, satellites, &inUse); + + QGeoSatelliteInfoSourceAndroid *source = AndroidPositioning::idToSatSource()->value(androidClassKey); + if (!source) { + qFatal("satelliteUpdated: source == 0"); + return; + } + + QMetaObject::invokeMethod(source, "processSatelliteUpdateInView", Qt::AutoConnection, + Q_ARG(QList<QGeoSatelliteInfo>, sats), Q_ARG(bool, isSingleUpdate)); + + QMetaObject::invokeMethod(source, "processSatelliteUpdateInUse", Qt::AutoConnection, + Q_ARG(QList<QGeoSatelliteInfo>, inUse), Q_ARG(bool, isSingleUpdate)); +} + #define FIND_AND_CHECK_CLASS(CLASS_NAME) \ clazz = env->FindClass(CLASS_NAME); \ @@ -350,7 +482,8 @@ if (!VAR) { \ static JNINativeMethod methods[] = { {"positionUpdated", "(Landroid/location/Location;IZ)V", (void *)positionUpdated}, - {"locationProvidersDisabled", "(I)V", (void *) locationProvidersDisabled} + {"locationProvidersDisabled", "(I)V", (void *) locationProvidersDisabled}, + {"satelliteUpdated", "([Landroid/location/GpsSatellite;IZ)V", (void *)satelliteUpdated} }; static bool registerNatives(JNIEnv *env) @@ -369,6 +502,8 @@ static bool registerNatives(JNIEnv *env) GET_AND_CHECK_STATIC_METHOD(startUpdatesMethodId, positioningClass, "startUpdates", "(III)I"); GET_AND_CHECK_STATIC_METHOD(stopUpdatesMethodId, positioningClass, "stopUpdates", "(I)V"); GET_AND_CHECK_STATIC_METHOD(requestUpdateMethodId, positioningClass, "requestUpdate", "(II)I"); + GET_AND_CHECK_STATIC_METHOD(startSatelliteUpdatesMethodId, positioningClass, "startSatelliteUpdates", "(IIZ)I"); + return true; } diff --git a/src/plugins/position/android/src/jnipositioning.h b/src/plugins/position/android/src/jnipositioning.h index de30107a..51341720 100644 --- a/src/plugins/position/android/src/jnipositioning.h +++ b/src/plugins/position/android/src/jnipositioning.h @@ -43,10 +43,11 @@ #define JNIPOSITIONING_H #include <QGeoPositionInfoSource> +#include <QGeoSatelliteInfoSource> namespace AndroidPositioning { - int registerPositionInfoSource(QGeoPositionInfoSourceAndroid *src); + int registerPositionInfoSource(QObject *obj); void unregisterPositionInfoSource(int key); QGeoPositionInfoSource::PositioningMethods availableProviders(); @@ -55,6 +56,10 @@ namespace AndroidPositioning QGeoPositionInfoSource::Error startUpdates(int androidClassKey); void stopUpdates(int androidClassKey); QGeoPositionInfoSource::Error requestUpdate(int androidClassKey); + + QGeoSatelliteInfoSource::Error startSatelliteUpdates(int androidClassKey, + bool isSingleRequest, + int updateRequestTimeout); } #endif // JNIPOSITIONING_H diff --git a/src/plugins/position/android/src/plugin.json b/src/plugins/position/android/src/plugin.json index 35823b5e..b84fafea 100644 --- a/src/plugins/position/android/src/plugin.json +++ b/src/plugins/position/android/src/plugin.json @@ -2,7 +2,7 @@ "Keys": ["android"], "Provider": "android", "Position": true, - "Satellite": false, + "Satellite": true, "Monitor": false, "Priority": 1000 } diff --git a/src/plugins/position/android/src/positionfactory_android.cpp b/src/plugins/position/android/src/positionfactory_android.cpp index 57f2d57f..047c313d 100644 --- a/src/plugins/position/android/src/positionfactory_android.cpp +++ b/src/plugins/position/android/src/positionfactory_android.cpp @@ -41,6 +41,7 @@ #include "positionfactory_android.h" #include "qgeopositioninfosource_android_p.h" +#include "qgeosatelliteinfosource_android_p.h" QGeoPositionInfoSource *QGeoPositionInfoSourceFactoryAndroid::positionInfoSource(QObject *parent) { @@ -50,8 +51,8 @@ QGeoPositionInfoSource *QGeoPositionInfoSourceFactoryAndroid::positionInfoSource QGeoSatelliteInfoSource *QGeoPositionInfoSourceFactoryAndroid::satelliteInfoSource(QObject *parent) { - Q_UNUSED(parent); - return 0; + QGeoSatelliteInfoSourceAndroid *src = new QGeoSatelliteInfoSourceAndroid(parent); + return src; } QGeoAreaMonitorSource *QGeoPositionInfoSourceFactoryAndroid::areaMonitor(QObject *parent) diff --git a/src/plugins/position/android/src/qgeosatelliteinfosource_android.cpp b/src/plugins/position/android/src/qgeosatelliteinfosource_android.cpp new file mode 100644 index 00000000..f9e627d0 --- /dev/null +++ b/src/plugins/position/android/src/qgeosatelliteinfosource_android.cpp @@ -0,0 +1,218 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QDebug> + +#include "qgeosatelliteinfosource_android_p.h" +#include "jnipositioning.h" + +Q_DECLARE_METATYPE(QGeoSatelliteInfo) +Q_DECLARE_METATYPE(QList<QGeoSatelliteInfo>) + +#define UPDATE_FROM_COLD_START 2*60*1000 + +QGeoSatelliteInfoSourceAndroid::QGeoSatelliteInfoSourceAndroid(QObject *parent) : + QGeoSatelliteInfoSource(parent), m_error(NoError), updatesRunning(false) +{ + qRegisterMetaType< QGeoSatelliteInfo >(); + qRegisterMetaType< QList<QGeoSatelliteInfo> >(); + androidClassKeyForUpdate = AndroidPositioning::registerPositionInfoSource(this); + androidClassKeyForSingleRequest = AndroidPositioning::registerPositionInfoSource(this); + + requestTimer.setSingleShot(true); + QObject::connect(&requestTimer, SIGNAL(timeout()), + this, SLOT(requestTimeout())); +} + +QGeoSatelliteInfoSourceAndroid::~QGeoSatelliteInfoSourceAndroid() +{ + stopUpdates(); + + if (requestTimer.isActive()) { + requestTimer.stop(); + AndroidPositioning::stopUpdates(androidClassKeyForSingleRequest); + } + + AndroidPositioning::unregisterPositionInfoSource(androidClassKeyForUpdate); + AndroidPositioning::unregisterPositionInfoSource(androidClassKeyForSingleRequest); +} + + +void QGeoSatelliteInfoSourceAndroid::setUpdateInterval(int msec) +{ + int previousInterval = updateInterval(); + msec = (((msec > 0) && (msec < minimumUpdateInterval())) || msec < 0)? minimumUpdateInterval() : msec; + + if (msec == previousInterval) + return; + + QGeoSatelliteInfoSource::setUpdateInterval(msec); + + if (updatesRunning) + reconfigureRunningSystem(); +} + +int QGeoSatelliteInfoSourceAndroid::minimumUpdateInterval() const +{ + return 1000; +} + +QGeoSatelliteInfoSource::Error QGeoSatelliteInfoSourceAndroid::error() const +{ + return m_error; +} + +void QGeoSatelliteInfoSourceAndroid::startUpdates() +{ + if (updatesRunning) + return; + + updatesRunning = true; + + QGeoSatelliteInfoSource::Error error = AndroidPositioning::startSatelliteUpdates( + androidClassKeyForUpdate, false, updateInterval()); + if (error != QGeoSatelliteInfoSource::NoError) { + updatesRunning = false; + m_error = error; + emit QGeoSatelliteInfoSource::error(m_error); + } +} + +void QGeoSatelliteInfoSourceAndroid::stopUpdates() +{ + if (!updatesRunning) + return; + + updatesRunning = false; + AndroidPositioning::stopUpdates(androidClassKeyForUpdate); +} + +void QGeoSatelliteInfoSourceAndroid::requestUpdate(int timeout) +{ + if (requestTimer.isActive()) + return; + + if (timeout != 0 && timeout < minimumUpdateInterval()) { + emit requestTimeout(); + return; + } + + if (timeout == 0) + timeout = UPDATE_FROM_COLD_START; + + requestTimer.start(timeout); + + // if updates already running with interval equal or less then timeout + // then we wait for next update coming through + // assume that a single update will not be quicker than regular updates anyway + if (updatesRunning && updateInterval() <= timeout) + return; + + QGeoSatelliteInfoSource::Error error = AndroidPositioning::startSatelliteUpdates( + androidClassKeyForSingleRequest, true, timeout); + if (error != QGeoSatelliteInfoSource::NoError) { + requestTimer.stop(); + m_error = error; + emit QGeoSatelliteInfoSource::error(m_error); + } +} + +void QGeoSatelliteInfoSourceAndroid::processSatelliteUpdateInView(const QList<QGeoSatelliteInfo> &satsInView, bool isSingleUpdate) +{ + if (!isSingleUpdate) { + //if requested while regular updates were running + if (requestTimer.isActive()) + requestTimer.stop(); + emit QGeoSatelliteInfoSource::satellitesInViewUpdated(satsInView); + return; + } + + m_satsInView = satsInView; +} + +void QGeoSatelliteInfoSourceAndroid::processSatelliteUpdateInUse(const QList<QGeoSatelliteInfo> &satsInUse, bool isSingleUpdate) +{ + if (!isSingleUpdate) { + //if requested while regular updates were running + if (requestTimer.isActive()) + requestTimer.stop(); + emit QGeoSatelliteInfoSource::satellitesInUseUpdated(satsInUse); + return; + } + + m_satsInUse = satsInUse; +} + +void QGeoSatelliteInfoSourceAndroid::requestTimeout() +{ + AndroidPositioning::stopUpdates(androidClassKeyForSingleRequest); + + const int count = m_satsInView.count(); + if (!count) { + emit requestTimeout(); + return; + } + + emit QGeoSatelliteInfoSource::satellitesInViewUpdated(m_satsInView); + emit QGeoSatelliteInfoSource::satellitesInUseUpdated(m_satsInUse); + + m_satsInUse.clear(); + m_satsInView.clear(); +} + +/* + Updates the system assuming that updateInterval + and/or preferredPositioningMethod have changed. + */ +void QGeoSatelliteInfoSourceAndroid::reconfigureRunningSystem() +{ + if (!updatesRunning) + return; + + stopUpdates(); + startUpdates(); +} + +void QGeoSatelliteInfoSourceAndroid::locationProviderDisabled() +{ + m_error = QGeoSatelliteInfoSource::ClosedError; + emit QGeoSatelliteInfoSource::error(m_error); +} diff --git a/src/plugins/position/android/src/qgeosatelliteinfosource_android_p.h b/src/plugins/position/android/src/qgeosatelliteinfosource_android_p.h new file mode 100644 index 00000000..75711a3e --- /dev/null +++ b/src/plugins/position/android/src/qgeosatelliteinfosource_android_p.h @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef QGEOSATELLITEINFOSOURCEANDROID_H +#define QGEOSATELLITEINFOSOURCEANDROID_H + +#include <QGeoSatelliteInfoSource> +#include <QTimer> + +class QGeoSatelliteInfoSourceAndroid : public QGeoSatelliteInfoSource +{ + Q_OBJECT +public: + explicit QGeoSatelliteInfoSourceAndroid(QObject *parent = 0); + ~QGeoSatelliteInfoSourceAndroid(); + + //From QGeoSatelliteInfoSource + void setUpdateInterval(int msec); + int minimumUpdateInterval() const; + + Error error() const; + +public Q_SLOTS: + void startUpdates(); + void stopUpdates(); + void requestUpdate(int timeout = 0); + + void processSatelliteUpdateInView(const QList<QGeoSatelliteInfo> &satsInView, bool isSingleUpdate); + void processSatelliteUpdateInUse(const QList<QGeoSatelliteInfo> &satsInUse, bool isSingleUpdate); + + void locationProviderDisabled(); +private Q_SLOTS: + void requestTimeout(); + +private: + void reconfigureRunningSystem(); + + Error m_error; + int androidClassKeyForUpdate; + int androidClassKeyForSingleRequest; + bool updatesRunning; + + QTimer requestTimer; + QList<QGeoSatelliteInfo> m_satsInUse; + QList<QGeoSatelliteInfo> m_satsInView; + +}; + +#endif // QGEOSATELLITEINFOSOURCEANDROID_H diff --git a/src/plugins/position/android/src/src.pro b/src/plugins/position/android/src/src.pro index 52e57bd3..a61b77b5 100644 --- a/src/plugins/position/android/src/src.pro +++ b/src/plugins/position/android/src/src.pro @@ -8,11 +8,13 @@ load(qt_plugin) HEADERS = \ positionfactory_android.h \ qgeopositioninfosource_android_p.h \ - jnipositioning.h + jnipositioning.h \ + qgeosatelliteinfosource_android_p.h SOURCES = \ positionfactory_android.cpp \ qgeopositioninfosource_android.cpp \ - jnipositioning.cpp + jnipositioning.cpp \ + qgeosatelliteinfosource_android.cpp OTHER_FILES = plugin.json |