summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAlex Blasche <alexander.blasche@digia.com>2013-10-14 16:44:54 +0200
committerThe Qt Project <gerrit-noreply@qt-project.org>2014-01-03 15:37:49 +0100
commit954b92e1207bfe5ab5a117e8393c191cdf0044d2 (patch)
treea5734da6c6412fbb29a5d6d5d115f7d8fa97660d /src
parent8dbb8c29877eaa12a8bab7bb14a3564dd608ecee (diff)
downloadqtlocation-954b92e1207bfe5ab5a117e8393c191cdf0044d2.tar.gz
Positioning backend for Android
Currently it only supports positioning data. Satellite information and potentially geo fencing will follow later on. Task-number: QTBUG-34102 [ChangeLog][QtPositioning][QGeoPositionInfoSource] Android backend added. Android devices can retrieve their current position. Network- and Satellite-based providers are supported. Change-Id: I94ec0d177aaef930f9a3f2a9f6af6fa3c904ec92 Reviewed-by: Eskil Abrahamsen Blomfeldt <eskil.abrahamsen-blomfeldt@digia.com> Reviewed-by: Alex Blasche <alexander.blasche@digia.com>
Diffstat (limited to 'src')
-rw-r--r--src/plugins/position/android/android.pro2
-rw-r--r--src/plugins/position/android/jar/AndroidManifest.xml6
-rw-r--r--src/plugins/position/android/jar/bundledjar.pro3
-rw-r--r--src/plugins/position/android/jar/distributedjar.pro3
-rw-r--r--src/plugins/position/android/jar/jar.pri14
-rw-r--r--src/plugins/position/android/jar/jar.pro2
-rw-r--r--src/plugins/position/android/jar/src/org/qtproject/qt5/android/positioning/QtPositioning.java425
-rw-r--r--src/plugins/position/android/src/jnipositioning.cpp400
-rw-r--r--src/plugins/position/android/src/jnipositioning.h60
-rw-r--r--src/plugins/position/android/src/plugin.json8
-rw-r--r--src/plugins/position/android/src/positionfactory_android.cpp61
-rw-r--r--src/plugins/position/android/src/positionfactory_android.h60
-rw-r--r--src/plugins/position/android/src/qgeopositioninfosource_android.cpp263
-rw-r--r--src/plugins/position/android/src/qgeopositioninfosource_android_p.h87
-rw-r--r--src/plugins/position/android/src/src.pro18
-rw-r--r--src/plugins/position/position.pro1
-rw-r--r--src/plugins/position/positionpoll/positionpollfactory.cpp6
-rw-r--r--src/plugins/position/positionpoll/positionpollfactory.h2
-rw-r--r--src/positioning/positioning.pro11
19 files changed, 1428 insertions, 4 deletions
diff --git a/src/plugins/position/android/android.pro b/src/plugins/position/android/android.pro
new file mode 100644
index 00000000..0dc6a3fc
--- /dev/null
+++ b/src/plugins/position/android/android.pro
@@ -0,0 +1,2 @@
+TEMPLATE = subdirs
+SUBDIRS += jar src
diff --git a/src/plugins/position/android/jar/AndroidManifest.xml b/src/plugins/position/android/jar/AndroidManifest.xml
new file mode 100644
index 00000000..2d066dbb
--- /dev/null
+++ b/src/plugins/position/android/jar/AndroidManifest.xml
@@ -0,0 +1,6 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="org.qtproject.qt5.android.positioning"
+ android:versionCode="1"
+ android:versionName="1.0" >
+ <supports-screens android:smallScreens="true" android:largeScreens="true" android:anyDensity="true" android:normalScreens="true"/>
+</manifest>
diff --git a/src/plugins/position/android/jar/bundledjar.pro b/src/plugins/position/android/jar/bundledjar.pro
new file mode 100644
index 00000000..e7bd106d
--- /dev/null
+++ b/src/plugins/position/android/jar/bundledjar.pro
@@ -0,0 +1,3 @@
+TARGET = QtPositioning-bundled
+CONFIG += bundled_jar_file
+include(jar.pri)
diff --git a/src/plugins/position/android/jar/distributedjar.pro b/src/plugins/position/android/jar/distributedjar.pro
new file mode 100644
index 00000000..4a5faaa4
--- /dev/null
+++ b/src/plugins/position/android/jar/distributedjar.pro
@@ -0,0 +1,3 @@
+TARGET = QtPositioning
+include(jar.pri)
+
diff --git a/src/plugins/position/android/jar/jar.pri b/src/plugins/position/android/jar/jar.pri
new file mode 100644
index 00000000..9fa548ff
--- /dev/null
+++ b/src/plugins/position/android/jar/jar.pri
@@ -0,0 +1,14 @@
+load(qt_build_paths)
+
+CONFIG += java
+DESTDIR = $$MODULE_BASE_OUTDIR/jar
+
+JAVACLASSPATH += $$PWD/src
+
+JAVASOURCES += \
+ $$PWD/src/org/qtproject/qt5/android/positioning/QtPositioning.java
+
+# install
+target.path = $$[QT_INSTALL_PREFIX]/jar
+INSTALLS += target
+
diff --git a/src/plugins/position/android/jar/jar.pro b/src/plugins/position/android/jar/jar.pro
new file mode 100644
index 00000000..8d19c1b7
--- /dev/null
+++ b/src/plugins/position/android/jar/jar.pro
@@ -0,0 +1,2 @@
+TEMPLATE = subdirs
+SUBDIRS += bundledjar.pro distributedjar.pro
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
new file mode 100644
index 00000000..ac510793
--- /dev/null
+++ b/src/plugins/position/android/jar/src/org/qtproject/qt5/android/positioning/QtPositioning.java
@@ -0,0 +1,425 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+
+package org.qtproject.qt5.android.positioning;
+
+import android.app.Activity;
+import android.content.Context;
+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.HashMap;
+import java.util.List;
+
+import android.util.Log;
+
+public class QtPositioning implements LocationListener
+{
+
+ private static final String TAG = "QtPositioning";
+ static LocationManager locationManager = null;
+ static Object m_syncObject = new Object();
+ static HashMap<Integer, QtPositioning> runningListeners = new HashMap<Integer, QtPositioning>();
+
+ /*
+ The positionInfo instance to which this
+ QtPositioning instance is attached to.
+ */
+ private int positionInfoSource = 0;
+
+ /*
+ The provider type requested by Qt:
+ 0 -> none
+ 1. bit -> GPS
+ 2. bit -> Network
+ */
+ private int expectedProviders = 0;
+
+ /* True, if updates were caused by requestUpdate() */
+ private boolean isSingleUpdate = false;
+ /* The length requested for regular intervals in msec. */
+ private int updateIntervalTime = 0;
+
+ /* The last received GPS update */
+ private Location lastGps = null;
+ /* The last received network update */
+ private Location lastNetwork = null;
+
+ private PositioningLooper looperThread;
+
+ static public void setActivity(Activity activity, Object activityDelegate)
+ {
+ try {
+ locationManager = (LocationManager) activity.getSystemService(Context.LOCATION_SERVICE);
+ } catch(Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ static private int[] providerList()
+ {
+ List<String> providers = locationManager.getAllProviders();
+ int retList[] = new int[providers.size()];
+ for (int i = 0; i < providers.size(); i++) {
+ if (providers.get(i).equals(LocationManager.GPS_PROVIDER)) {
+ //must be in sync with AndroidPositioning::PositionProvider::PROVIDER_GPS
+ retList[i] = 0;
+ } else if (providers.get(i).equals(LocationManager.NETWORK_PROVIDER)) {
+ //must be in sync with AndroidPositioning::PositionProvider::PROVIDER_NETWORK
+ retList[i] = 1;
+ } else if (providers.get(i).equals(LocationManager.PASSIVE_PROVIDER)) {
+ //must be in sync with AndroidPositioning::PositionProvider::PROVIDER_PASSIVE
+ retList[i] = 2;
+ } else {
+ retList[i] = -1;
+ }
+ }
+ return retList;
+ }
+
+ static public Location lastKnownPosition(boolean fromSatelliteOnly)
+ {
+ Location gps = null;
+ Location network = null;
+ try {
+ gps = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
+ if (!fromSatelliteOnly)
+ network = locationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
+ } catch(Exception e) {
+ e.printStackTrace();
+ gps = network = null;
+ }
+
+ if (gps != null && network != null) {
+ //we return the most recent location but slightly prefer GPS
+ //prefer GPS if it is max 4 hrs older than network
+ long delta = network.getTime() - gps.getTime();
+ if (delta < 4*60*60*1000) {
+ return gps;
+ } else {
+ return network;
+ }
+ } else if (gps != null ) {
+ return gps;
+ } else if (network != null) {
+ return network;
+ }
+
+ return null;
+ }
+
+ /* Returns true if at least on of the given providers is enabled. */
+ static private boolean expectedProvidersAvailable(int desiredProviders)
+ {
+ List<String> enabledProviders = locationManager.getProviders(true);
+ if ((desiredProviders & 1) > 0) { //gps desired
+ if (enabledProviders.contains(LocationManager.GPS_PROVIDER)) {
+ return true;
+ }
+ }
+ if ((desiredProviders & 2) > 0) { //network desired
+ if (enabledProviders.contains(LocationManager.NETWORK_PROVIDER)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ static public int startUpdates(int androidClassKey, int locationProvider, int updateInterval)
+ {
+ synchronized (m_syncObject) {
+ try {
+ boolean exceptionOccurred = false;
+ QtPositioning positioningListener = new QtPositioning();
+ positioningListener.setActiveLooper(true);
+ positioningListener.positionInfoSource = androidClassKey;
+ positioningListener.expectedProviders = locationProvider;
+
+ if (updateInterval == 0)
+ updateInterval = 1000; //don't update more often than once per second
+
+ positioningListener.updateIntervalTime = updateInterval;
+ if ((locationProvider & 1) > 0) {
+ Log.d(TAG, "Regular updates using GPS");
+ try {
+ locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER,
+ updateInterval, 0,
+ positioningListener,
+ positioningListener.looper());
+ } catch (SecurityException se) {
+ se.printStackTrace();
+ exceptionOccurred = true;
+ }
+ }
+
+ if ((locationProvider & 2) > 0) {
+ Log.d(TAG, "Regular updates using network");
+ try {
+ locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER,
+ updateInterval, 0,
+ positioningListener,
+ positioningListener.looper());
+ } catch (SecurityException se) {
+ se.printStackTrace();
+ exceptionOccurred = true;
+ }
+ }
+ if (exceptionOccurred) {
+ positioningListener.setActiveLooper(false);
+ locationManager.removeUpdates(positioningListener);
+ return 0; //AccessError
+ }
+
+ if (!expectedProvidersAvailable(locationProvider)) {
+ //all location providers unavailbe -> when they come back we resume automatically
+ return 1; //ClosedError
+ }
+
+ runningListeners.put(androidClassKey, positioningListener);
+ } catch(Exception e) {
+ e.printStackTrace();
+ return 2; //UnknownSourceError
+ }
+
+ return 3; //NoError
+ }
+ }
+
+ static public void stopUpdates(int androidClassKey)
+ {
+ synchronized (m_syncObject) {
+ try {
+ Log.d(TAG, "Stopping updates");
+ QtPositioning listener = runningListeners.remove(androidClassKey);
+ locationManager.removeUpdates(listener);
+ listener.setActiveLooper(false);
+ } catch(Exception e) {
+ e.printStackTrace();
+ return;
+ }
+ }
+ }
+
+ static public int requestUpdate(int androidClassKey, int locationProvider)
+ {
+ synchronized (m_syncObject) {
+ try {
+ boolean exceptionOccurred = false;
+ QtPositioning positioningListener = new QtPositioning();
+ positioningListener.setActiveLooper(true);
+ positioningListener.positionInfoSource = androidClassKey;
+ positioningListener.isSingleUpdate = true;
+ positioningListener.expectedProviders = locationProvider;
+
+ if ((locationProvider & 1) > 0) {
+ Log.d(TAG, "Single update using GPS");
+ try {
+ locationManager.requestSingleUpdate(LocationManager.GPS_PROVIDER,
+ positioningListener,
+ positioningListener.looper());
+ } catch (SecurityException se) {
+ se.printStackTrace();
+ exceptionOccurred = true;
+ }
+ }
+
+ if ((locationProvider & 2) > 0) {
+ Log.d(TAG, "Single update using network");
+ try {
+ locationManager.requestSingleUpdate(LocationManager.NETWORK_PROVIDER,
+ positioningListener,
+ positioningListener.looper());
+ } catch (SecurityException se) {
+ se.printStackTrace();
+ exceptionOccurred = true;
+ }
+ }
+ if (exceptionOccurred) {
+ positioningListener.setActiveLooper(false);
+ locationManager.removeUpdates(positioningListener);
+ return 0; //AccessError
+ }
+
+ if (!expectedProvidersAvailable(locationProvider)) {
+ //all location providers unavailable -> when they come back we resume automatically
+ //in the mean time return ClosedError
+ return 1; //ClosedError
+ }
+
+ runningListeners.put(androidClassKey, positioningListener);
+ } catch(Exception e) {
+ e.printStackTrace();
+ return 2; //UnknownSourceError
+ }
+
+ return 3; //NoError
+ }
+ }
+
+ public QtPositioning()
+ {
+ looperThread = new PositioningLooper();
+ }
+
+ public Looper looper()
+ {
+ return looperThread.looper();
+ }
+
+ private void setActiveLooper(boolean setActive)
+ {
+ try{
+ if (setActive) {
+ if (looperThread.isAlive())
+ return;
+
+ looperThread.start();
+ while (!looperThread.isReady());
+ Thread.sleep(1000);
+ } else {
+ looperThread.quitLooper();
+ }
+ } catch(Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ private class PositioningLooper extends Thread {
+ private boolean looperRunning;
+ private Looper posLooper;
+
+ private PositioningLooper()
+ {
+ looperRunning = false;
+ }
+
+ public void run()
+ {
+ Looper.prepare();
+ Handler handler = new Handler();
+ looperRunning = true;
+ posLooper = Looper.myLooper();
+ Looper.loop();
+ looperRunning = false;
+ }
+
+ public void quitLooper()
+ {
+ looper().quit();
+ }
+
+ public boolean isReady()
+ {
+ return looperRunning;
+ }
+
+ public Looper looper()
+ {
+ return posLooper;
+ }
+ }
+
+
+
+ public static native void positionUpdated(Location update, int androidClassKey, boolean isSingleUpdate);
+ public static native void locationProvidersDisabled(int androidClassKey);
+
+ @Override
+ public void onLocationChanged(Location location) {
+ //Log.d(TAG, "**** Position Update ****: " + location.toString() + " " + isSingleUpdate);
+ if (location == null)
+ return;
+
+ if (isSingleUpdate || expectedProviders < 3) {
+ positionUpdated(location, positionInfoSource, isSingleUpdate);
+ return;
+ }
+
+ /*
+ We can use GPS and Network, pick the better location provider.
+ Generally we prefer GPS data due to their higher accurancy but we
+ let Network data pass until GPS fix is available
+ */
+
+ if (location.getProvider().equals(LocationManager.GPS_PROVIDER)) {
+ lastGps = location;
+
+ // assumption: GPS always better -> pass it on
+ positionUpdated(location, positionInfoSource, 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);
+ return;
+ }
+
+ long delta = location.getTime() - lastGps.getTime();
+
+ // Ignore if network update is older than last GPS (delta < 0)
+ // Ignore if gps update still has time to provide next location (delta < updateInterval)
+ if (delta < updateIntervalTime)
+ return;
+
+ // Use network data -> GPS has timed out on updateInterval
+ positionUpdated(location, positionInfoSource, isSingleUpdate);
+ }
+ }
+
+ @Override
+ public void onStatusChanged(String provider, int status, Bundle extras) {}
+
+ @Override
+ public void onProviderEnabled(String provider) {
+ Log.d(TAG, "Enabled provider: " + provider);
+ }
+
+ @Override
+ public void onProviderDisabled(String provider) {
+ Log.d(TAG, "Disabled provider: " + provider);
+ if (!expectedProvidersAvailable(expectedProviders))
+ locationProvidersDisabled(positionInfoSource);
+ }
+}
diff --git a/src/plugins/position/android/src/jnipositioning.cpp b/src/plugins/position/android/src/jnipositioning.cpp
new file mode 100644
index 00000000..035fb760
--- /dev/null
+++ b/src/plugins/position/android/src/jnipositioning.cpp
@@ -0,0 +1,400 @@
+/****************************************************************************
+**
+** 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 <QDateTime>
+#include <QMap>
+#include <QtGlobal>
+#include <android/log.h>
+#include <jni.h>
+#include <QGeoPositionInfo>
+#include "qgeopositioninfosource_android_p.h"
+
+#include "jnipositioning.h"
+
+static JavaVM *javaVM = 0;
+jclass positioningClass;
+
+static jmethodID providerListMethodId;
+static jmethodID lastKnownPositionMethodId;
+static jmethodID startUpdatesMethodId;
+static jmethodID stopUpdatesMethodId;
+static jmethodID requestUpdateMethodId;
+
+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)
+
+ struct AttachedJNIEnv
+ {
+ AttachedJNIEnv()
+ {
+ attached = false;
+ if (javaVM->GetEnv((void**)&jniEnv, JNI_VERSION_1_6) < 0) {
+ if (javaVM->AttachCurrentThread(&jniEnv, NULL) < 0) {
+ __android_log_print(ANDROID_LOG_ERROR, logTag, "AttachCurrentThread failed");
+ jniEnv = 0;
+ return;
+ }
+ attached = true;
+ }
+ }
+
+ ~AttachedJNIEnv()
+ {
+ if (attached)
+ javaVM->DetachCurrentThread();
+ }
+ bool attached;
+ JNIEnv *jniEnv;
+ };
+
+ int registerPositionInfoSource(QGeoPositionInfoSourceAndroid *src)
+ {
+ static bool firstInit = true;
+ if (firstInit) {
+ qsrand( QDateTime::currentDateTime().toTime_t() );
+ firstInit = false;
+ }
+
+ int key;
+ do {
+ key = qrand();
+ } while (idToSource()->contains(key));
+
+ idToSource()->insert(key, src);
+ return key;
+ }
+
+ void unregisterPositionInfoSource(int key)
+ {
+ idToSource()->remove(key);
+ }
+
+ enum PositionProvider
+ {
+ PROVIDER_GPS = 0,
+ PROVIDER_NETWORK = 1,
+ PROVIDER_PASSIVE = 2
+ };
+
+
+ QGeoPositionInfoSource::PositioningMethods availableProviders()
+ {
+ QGeoPositionInfoSource::PositioningMethods ret =
+ static_cast<QGeoPositionInfoSource::PositioningMethods>(0);
+ AttachedJNIEnv env;
+ if (!env.jniEnv)
+ return ret;
+ jintArray jProviders = static_cast<jintArray>(env.jniEnv->CallStaticObjectMethod(
+ positioningClass, providerListMethodId));
+ jint *providers = env.jniEnv->GetIntArrayElements(jProviders, 0);
+ const uint size = env.jniEnv->GetArrayLength(jProviders);
+ for (uint i = 0; i < size; i++) {
+ switch (providers[i]) {
+ case PROVIDER_GPS:
+ ret |= QGeoPositionInfoSource::SatellitePositioningMethods;
+ break;
+ case PROVIDER_NETWORK:
+ ret |= QGeoPositionInfoSource::NonSatellitePositioningMethods;
+ break;
+ case PROVIDER_PASSIVE:
+ //we ignore as Qt doesn't have interface for it right now
+ break;
+ default:
+ __android_log_print(ANDROID_LOG_INFO, logTag, "Unknown positioningMethod");
+ }
+ }
+ env.jniEnv->ReleaseIntArrayElements(jProviders, providers, 0);
+
+ return ret;
+ }
+
+ QGeoPositionInfo positionInfoFromJavaLocation(JNIEnv * jniEnv, const jobject &location)
+ {
+ QGeoPositionInfo info;
+ jclass thisClass = jniEnv->GetObjectClass(location);
+ if (!thisClass)
+ return QGeoPositionInfo();
+
+ jmethodID mid = jniEnv->GetMethodID(thisClass, "getLatitude", "()D");
+ jdouble latitude = jniEnv->CallDoubleMethod(location, mid);
+ mid = jniEnv->GetMethodID(thisClass, "getLongitude", "()D");
+ jdouble longitude = jniEnv->CallDoubleMethod(location, mid);
+ QGeoCoordinate coordinate(latitude, longitude);
+
+ //altitude
+ mid = jniEnv->GetMethodID(thisClass, "hasAltitude", "()Z");
+ jboolean attributeExists = jniEnv->CallBooleanMethod(location, mid);
+ if (attributeExists) {
+ mid = jniEnv->GetMethodID(thisClass, "getAltitude", "()D");
+ jdouble value = jniEnv->CallDoubleMethod(location, mid);
+ coordinate.setAltitude(value);
+ }
+
+ info.setCoordinate(coordinate);
+
+ //time stamp
+ mid = jniEnv->GetMethodID(thisClass, "getTime", "()J");
+ jlong timestamp = jniEnv->CallLongMethod(location, mid);
+ info.setTimestamp(QDateTime::fromMSecsSinceEpoch(timestamp));
+
+ //accuracy
+ mid = jniEnv->GetMethodID(thisClass, "hasAccuracy", "()Z");
+ attributeExists = jniEnv->CallBooleanMethod(location, mid);
+ if (attributeExists) {
+ mid = jniEnv->GetMethodID(thisClass, "getAccuracy", "()F");
+ jfloat accuracy = jniEnv->CallFloatMethod(location, mid);
+ info.setAttribute(QGeoPositionInfo::HorizontalAccuracy, accuracy);
+ }
+
+ //ground speed
+ mid = jniEnv->GetMethodID(thisClass, "hasSpeed", "()Z");
+ attributeExists = jniEnv->CallBooleanMethod(location, mid);
+ if (attributeExists) {
+ mid = jniEnv->GetMethodID(thisClass, "getSpeed", "()F");
+ jfloat speed = jniEnv->CallFloatMethod(location, mid);
+ info.setAttribute(QGeoPositionInfo::GroundSpeed, speed);
+ }
+
+ //bearing
+ mid = jniEnv->GetMethodID(thisClass, "hasBearing", "()Z");
+ attributeExists = jniEnv->CallBooleanMethod(location, mid);
+ if (attributeExists) {
+ mid = jniEnv->GetMethodID(thisClass, "getBearing", "()F");
+ jfloat bearing = jniEnv->CallFloatMethod(location, mid);
+ info.setAttribute(QGeoPositionInfo::Direction, bearing);
+ }
+
+ return info;
+ }
+
+ QGeoPositionInfo lastKnownPosition(bool fromSatellitePositioningMethodsOnly)
+ {
+ AttachedJNIEnv env;
+ if (!env.jniEnv)
+ return QGeoPositionInfo();
+
+ jobject location = env.jniEnv->CallStaticObjectMethod(positioningClass,
+ lastKnownPositionMethodId,
+ fromSatellitePositioningMethodsOnly);
+ if (location == 0)
+ return QGeoPositionInfo();
+
+ return positionInfoFromJavaLocation(env.jniEnv, location);
+ }
+
+ inline int positioningMethodToInt(QGeoPositionInfoSource::PositioningMethods m)
+ {
+ int providerSelection = 0;
+ if (m & QGeoPositionInfoSource::SatellitePositioningMethods)
+ providerSelection |= 1;
+ if (m & QGeoPositionInfoSource::NonSatellitePositioningMethods)
+ providerSelection |= 2;
+
+ return providerSelection;
+ }
+
+ QGeoPositionInfoSource::Error startUpdates(int androidClassKey)
+ {
+ AttachedJNIEnv env;
+ if (!env.jniEnv)
+ return QGeoPositionInfoSource::UnknownSourceError;
+
+ QGeoPositionInfoSourceAndroid *source = AndroidPositioning::idToSource()->value(androidClassKey);
+
+ if (source) {
+ int errorCode = env.jniEnv->CallStaticIntMethod(positioningClass, startUpdatesMethodId,
+ androidClassKey,
+ positioningMethodToInt(source->preferredPositioningMethods()),
+ source->updateInterval());
+ switch (errorCode) {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ return static_cast<QGeoPositionInfoSource::Error>(errorCode);
+ default:
+ break;
+ }
+ }
+
+ return QGeoPositionInfoSource::UnknownSourceError;
+ }
+
+ //used for stopping regular and single updates
+ void stopUpdates(int androidClassKey)
+ {
+ AttachedJNIEnv env;
+ if (!env.jniEnv)
+ return;
+
+ env.jniEnv->CallStaticVoidMethod(positioningClass, stopUpdatesMethodId, androidClassKey);
+ }
+
+ QGeoPositionInfoSource::Error requestUpdate(int androidClassKey)
+ {
+ AttachedJNIEnv env;
+ if (!env.jniEnv)
+ return QGeoPositionInfoSource::UnknownSourceError;
+
+ QGeoPositionInfoSourceAndroid *source = AndroidPositioning::idToSource()->value(androidClassKey);
+
+ if (source) {
+ int errorCode = env.jniEnv->CallStaticIntMethod(positioningClass, requestUpdateMethodId,
+ androidClassKey,
+ positioningMethodToInt(source->preferredPositioningMethods()));
+ switch (errorCode) {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ return static_cast<QGeoPositionInfoSource::Error>(errorCode);
+ default:
+ break;
+ }
+ }
+ return QGeoPositionInfoSource::UnknownSourceError;
+ }
+}
+
+
+static void positionUpdated(JNIEnv *env, jobject /*thiz*/, jobject location, jint androidClassKey, jboolean isSingleUpdate)
+{
+ QGeoPositionInfo info = AndroidPositioning::positionInfoFromJavaLocation(env, location);
+
+ QGeoPositionInfoSourceAndroid *source = AndroidPositioning::idToSource()->value(androidClassKey);
+ if (!source) {
+ qFatal("positionUpdated: source == 0");
+ return;
+ }
+
+ //we need to invoke indirectly as the Looper thread is likely to be not the same thread
+ if (!isSingleUpdate)
+ QMetaObject::invokeMethod(source, "processPositionUpdate", Qt::AutoConnection,
+ Q_ARG(QGeoPositionInfo, info));
+ 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);
+ if (!source) {
+ qFatal("locationProvidersDisabled: source == 0");
+ return;
+ }
+
+ QMetaObject::invokeMethod(source, "locationProviderDisabled", Qt::AutoConnection);
+}
+
+
+#define FIND_AND_CHECK_CLASS(CLASS_NAME) \
+clazz = env->FindClass(CLASS_NAME); \
+if (!clazz) { \
+ __android_log_print(ANDROID_LOG_FATAL, logTag, classErrorMsg, CLASS_NAME); \
+ return JNI_FALSE; \
+}
+
+#define GET_AND_CHECK_STATIC_METHOD(VAR, CLASS, METHOD_NAME, METHOD_SIGNATURE) \
+VAR = env->GetStaticMethodID(CLASS, METHOD_NAME, METHOD_SIGNATURE); \
+if (!VAR) { \
+ __android_log_print(ANDROID_LOG_FATAL, logTag, methodErrorMsg, METHOD_NAME, METHOD_SIGNATURE); \
+ return JNI_FALSE; \
+}
+
+static JNINativeMethod methods[] = {
+ {"positionUpdated", "(Landroid/location/Location;IZ)V", (void *)positionUpdated},
+ {"locationProvidersDisabled", "(I)V", (void *) locationProvidersDisabled}
+};
+
+static bool registerNatives(JNIEnv *env)
+{
+ jclass clazz;
+ FIND_AND_CHECK_CLASS("org/qtproject/qt5/android/positioning/QtPositioning");
+ positioningClass = static_cast<jclass>(env->NewGlobalRef(clazz));
+
+ if (env->RegisterNatives(positioningClass, methods, sizeof(methods) / sizeof(methods[0])) < 0) {
+ __android_log_print(ANDROID_LOG_FATAL, logTag, "RegisterNatives failed");
+ return JNI_FALSE;
+ }
+
+ GET_AND_CHECK_STATIC_METHOD(providerListMethodId, positioningClass, "providerList", "()[I");
+ GET_AND_CHECK_STATIC_METHOD(lastKnownPositionMethodId, positioningClass, "lastKnownPosition", "(Z)Landroid/location/Location;");
+ 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");
+ return true;
+}
+
+Q_DECL_EXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void * /*reserved*/)
+{
+ typedef union {
+ JNIEnv *nativeEnvironment;
+ void *venv;
+ } UnionJNIEnvToVoid;
+
+ __android_log_print(ANDROID_LOG_INFO, logTag, "Positioning start");
+ UnionJNIEnvToVoid uenv;
+ uenv.venv = NULL;
+ javaVM = 0;
+
+ if (vm->GetEnv(&uenv.venv, JNI_VERSION_1_4) != JNI_OK) {
+ __android_log_print(ANDROID_LOG_FATAL, logTag, "GetEnv failed");
+ return -1;
+ }
+ JNIEnv *env = uenv.nativeEnvironment;
+ if (!registerNatives(env)) {
+ __android_log_print(ANDROID_LOG_FATAL, logTag, "registerNatives failed");
+ return -1;
+ }
+
+ javaVM = vm;
+ return JNI_VERSION_1_4;
+}
+
diff --git a/src/plugins/position/android/src/jnipositioning.h b/src/plugins/position/android/src/jnipositioning.h
new file mode 100644
index 00000000..de30107a
--- /dev/null
+++ b/src/plugins/position/android/src/jnipositioning.h
@@ -0,0 +1,60 @@
+/****************************************************************************
+**
+** 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 JNIPOSITIONING_H
+#define JNIPOSITIONING_H
+
+#include <QGeoPositionInfoSource>
+
+namespace AndroidPositioning
+{
+ int registerPositionInfoSource(QGeoPositionInfoSourceAndroid *src);
+ void unregisterPositionInfoSource(int key);
+
+ QGeoPositionInfoSource::PositioningMethods availableProviders();
+ QGeoPositionInfo lastKnownPosition(bool fromSatellitePositioningMethodsOnly);
+
+ QGeoPositionInfoSource::Error startUpdates(int androidClassKey);
+ void stopUpdates(int androidClassKey);
+ QGeoPositionInfoSource::Error requestUpdate(int androidClassKey);
+}
+
+#endif // JNIPOSITIONING_H
diff --git a/src/plugins/position/android/src/plugin.json b/src/plugins/position/android/src/plugin.json
new file mode 100644
index 00000000..35823b5e
--- /dev/null
+++ b/src/plugins/position/android/src/plugin.json
@@ -0,0 +1,8 @@
+{
+ "Keys": ["android"],
+ "Provider": "android",
+ "Position": true,
+ "Satellite": false,
+ "Monitor": false,
+ "Priority": 1000
+}
diff --git a/src/plugins/position/android/src/positionfactory_android.cpp b/src/plugins/position/android/src/positionfactory_android.cpp
new file mode 100644
index 00000000..57f2d57f
--- /dev/null
+++ b/src/plugins/position/android/src/positionfactory_android.cpp
@@ -0,0 +1,61 @@
+/****************************************************************************
+**
+** 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 "positionfactory_android.h"
+#include "qgeopositioninfosource_android_p.h"
+
+QGeoPositionInfoSource *QGeoPositionInfoSourceFactoryAndroid::positionInfoSource(QObject *parent)
+{
+ QGeoPositionInfoSourceAndroid *src = new QGeoPositionInfoSourceAndroid(parent);
+ return src;
+}
+
+QGeoSatelliteInfoSource *QGeoPositionInfoSourceFactoryAndroid::satelliteInfoSource(QObject *parent)
+{
+ Q_UNUSED(parent);
+ return 0;
+}
+
+QGeoAreaMonitorSource *QGeoPositionInfoSourceFactoryAndroid::areaMonitor(QObject *parent)
+{
+ Q_UNUSED(parent);
+ return 0;
+}
diff --git a/src/plugins/position/android/src/positionfactory_android.h b/src/plugins/position/android/src/positionfactory_android.h
new file mode 100644
index 00000000..4b7818d4
--- /dev/null
+++ b/src/plugins/position/android/src/positionfactory_android.h
@@ -0,0 +1,60 @@
+/****************************************************************************
+**
+** 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 POSITIONPOLLFACTORY_H
+#define POSITIONPOLLFACTORY_H
+
+#include <QObject>
+#include <QGeoPositionInfoSourceFactory>
+
+class QGeoPositionInfoSourceFactoryAndroid : public QObject, public QGeoPositionInfoSourceFactory
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID "org.qt-project.qt.position.sourcefactory/5.0"
+ FILE "plugin.json")
+ Q_INTERFACES(QGeoPositionInfoSourceFactory)
+public:
+ QGeoPositionInfoSource *positionInfoSource(QObject *parent);
+ QGeoSatelliteInfoSource *satelliteInfoSource(QObject *parent);
+ QGeoAreaMonitorSource *areaMonitor(QObject *parent);
+};
+
+#endif // POSITIONPOLLFACTORY_H
diff --git a/src/plugins/position/android/src/qgeopositioninfosource_android.cpp b/src/plugins/position/android/src/qgeopositioninfosource_android.cpp
new file mode 100644
index 00000000..d4a95aa3
--- /dev/null
+++ b/src/plugins/position/android/src/qgeopositioninfosource_android.cpp
@@ -0,0 +1,263 @@
+/****************************************************************************
+**
+** 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 "qgeopositioninfosource_android_p.h"
+#include "jnipositioning.h"
+//#include <QDebug>
+#include <QGeoPositionInfo>
+
+Q_DECLARE_METATYPE(QGeoPositionInfo)
+#define UPDATE_FROM_COLD_START 2*60*1000
+
+
+QGeoPositionInfoSourceAndroid::QGeoPositionInfoSourceAndroid(QObject *parent) :
+ QGeoPositionInfoSource(parent), updatesRunning(false), m_error(UnknownSourceError)
+{
+ qRegisterMetaType< QGeoPositionInfo >();
+ androidClassKeyForUpdate = AndroidPositioning::registerPositionInfoSource(this);
+ androidClassKeyForSingleRequest = AndroidPositioning::registerPositionInfoSource(this);
+
+ //qDebug() << "androidClassKey: " << androidClassKeyForUpdate << androidClassKeyForSingleRequest;
+ //by default use all methods
+ setPreferredPositioningMethods(AllPositioningMethods);
+
+ m_requestTimer.setSingleShot(true);
+ QObject::connect(&m_requestTimer, SIGNAL(timeout()), this, SLOT(requestTimeout()));
+}
+
+QGeoPositionInfoSourceAndroid::~QGeoPositionInfoSourceAndroid()
+{
+ stopUpdates();
+
+ if (m_requestTimer.isActive()) {
+ m_requestTimer.stop();
+ AndroidPositioning::stopUpdates(androidClassKeyForSingleRequest);
+ }
+
+ AndroidPositioning::unregisterPositionInfoSource(androidClassKeyForUpdate);
+ AndroidPositioning::unregisterPositionInfoSource(androidClassKeyForSingleRequest);
+}
+
+void QGeoPositionInfoSourceAndroid::setUpdateInterval(int msec)
+{
+ int previousInterval = updateInterval();
+ msec = (((msec > 0) && (msec < minimumUpdateInterval())) || msec < 0)? minimumUpdateInterval() : msec;
+
+ if (msec == previousInterval)
+ return;
+
+ QGeoPositionInfoSource::setUpdateInterval(msec);
+
+ if (updatesRunning)
+ reconfigureRunningSystem();
+}
+
+QGeoPositionInfo QGeoPositionInfoSourceAndroid::lastKnownPosition(bool fromSatellitePositioningMethodsOnly) const
+{
+ return AndroidPositioning::lastKnownPosition(fromSatellitePositioningMethodsOnly);
+}
+
+QGeoPositionInfoSource::PositioningMethods QGeoPositionInfoSourceAndroid::supportedPositioningMethods() const
+{
+ return AndroidPositioning::availableProviders();
+}
+
+void QGeoPositionInfoSourceAndroid::setPreferredPositioningMethods(QGeoPositionInfoSource::PositioningMethods methods)
+{
+ PositioningMethods previousPreferredPositioningMethods = preferredPositioningMethods();
+ QGeoPositionInfoSource::setPreferredPositioningMethods(methods);
+ if (previousPreferredPositioningMethods == preferredPositioningMethods())
+ return;
+
+ if (updatesRunning)
+ reconfigureRunningSystem();
+}
+
+int QGeoPositionInfoSourceAndroid::minimumUpdateInterval() const
+{
+ return 1000;
+}
+
+QGeoPositionInfoSource::Error QGeoPositionInfoSourceAndroid::error() const
+{
+ return m_error;
+}
+
+void QGeoPositionInfoSourceAndroid::startUpdates()
+{
+ if (updatesRunning)
+ return;
+
+ if (preferredPositioningMethods() == 0) {
+ m_error = UnknownSourceError;
+ emit QGeoPositionInfoSource::error(m_error);
+
+ return;
+ }
+
+ updatesRunning = true;
+ QGeoPositionInfoSource::Error error = AndroidPositioning::startUpdates(androidClassKeyForUpdate);
+ //if (error != QGeoPositionInfoSource::NoError) { //TODO
+ if (error != 3) {
+ updatesRunning = false;
+ m_error = error;
+ emit QGeoPositionInfoSource::error(m_error);
+ }
+}
+
+void QGeoPositionInfoSourceAndroid::stopUpdates()
+{
+ if (!updatesRunning)
+ return;
+
+ updatesRunning = false;
+ AndroidPositioning::stopUpdates(androidClassKeyForUpdate);
+}
+
+void QGeoPositionInfoSourceAndroid::requestUpdate(int timeout)
+{
+ if (m_requestTimer.isActive())
+ return;
+
+ if (timeout != 0 && timeout < minimumUpdateInterval()) {
+ emit updateTimeout();
+ return;
+ }
+
+ if (timeout == 0)
+ timeout = UPDATE_FROM_COLD_START;
+
+ m_requestTimer.start(timeout);
+
+ // if updates already running with interval equal to 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;
+
+ QGeoPositionInfoSource::Error error = AndroidPositioning::requestUpdate(androidClassKeyForSingleRequest);
+ //if (error != QGeoPositionInfoSource::NoError) { //TODO
+ if (error != 3) {
+ m_requestTimer.stop();
+ m_error = error;
+ emit QGeoPositionInfoSource::error(m_error);
+ }
+}
+
+void QGeoPositionInfoSourceAndroid::processPositionUpdate(const QGeoPositionInfo &pInfo)
+{
+ //single update request and served as part of regular update
+ if (m_requestTimer.isActive())
+ m_requestTimer.stop();
+
+ emit positionUpdated(pInfo);
+}
+
+// Might still be called multiple times (once for each provider)
+void QGeoPositionInfoSourceAndroid::processSinglePositionUpdate(const QGeoPositionInfo &pInfo)
+{
+ //timeout but we received a late update -> ignore
+ if (!m_requestTimer.isActive())
+ return;
+
+ queuedSingleUpdates.append(pInfo);
+}
+
+void QGeoPositionInfoSourceAndroid::locationProviderDisabled()
+{
+ m_error = QGeoPositionInfoSource::ClosedError;
+ emit QGeoPositionInfoSource::error(m_error);
+}
+
+void QGeoPositionInfoSourceAndroid::requestTimeout()
+{
+ AndroidPositioning::stopUpdates(androidClassKeyForSingleRequest);
+ //no queued update to process -> timeout
+ const int count = queuedSingleUpdates.count();
+
+ if (!count) {
+ emit updateTimeout();
+ return;
+ }
+
+ //pick best
+ QGeoPositionInfo best = queuedSingleUpdates[0];
+ for (int i = 1; i < count; i++) {
+ const QGeoPositionInfo info = queuedSingleUpdates[i];
+
+ //anything newer by 20s is always better
+ const int timeDelta = best.timestamp().secsTo(info.timestamp());
+ if (abs(timeDelta) > 20) {
+ if (timeDelta > 0)
+ best = info;
+ continue;
+ }
+
+ //compare accuracy
+ if (info.hasAttribute(QGeoPositionInfo::HorizontalAccuracy) &&
+ info.hasAttribute(QGeoPositionInfo::HorizontalAccuracy))
+ {
+ best = info.attribute(QGeoPositionInfo::HorizontalAccuracy) <
+ best.attribute(QGeoPositionInfo::HorizontalAccuracy) ? info : best;
+ continue;
+ }
+
+ //prefer info with accuracy information
+ if (info.hasAttribute(QGeoPositionInfo::HorizontalAccuracy))
+ best = info;
+ }
+
+ queuedSingleUpdates.clear();
+ emit positionUpdated(best);
+}
+
+/*
+ Updates the system assuming that updateInterval
+ and/or preferredPositioningMethod have changed.
+ */
+void QGeoPositionInfoSourceAndroid::reconfigureRunningSystem()
+{
+ if (!updatesRunning)
+ return;
+
+ stopUpdates();
+ startUpdates();
+}
diff --git a/src/plugins/position/android/src/qgeopositioninfosource_android_p.h b/src/plugins/position/android/src/qgeopositioninfosource_android_p.h
new file mode 100644
index 00000000..938b6564
--- /dev/null
+++ b/src/plugins/position/android/src/qgeopositioninfosource_android_p.h
@@ -0,0 +1,87 @@
+/****************************************************************************
+**
+** 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 QGEOPOSITIONINFOSOURCE_ANDROID_P_H
+#define QGEOPOSITIONINFOSOURCE_ANDROID_P_H
+
+#include <QGeoPositionInfoSource>
+#include <QTimer>
+
+class QGeoPositionInfoSourceAndroid : public QGeoPositionInfoSource
+{
+ Q_OBJECT
+public:
+ QGeoPositionInfoSourceAndroid(QObject *parent = 0);
+ ~QGeoPositionInfoSourceAndroid();
+
+ // From QGeoPositionInfoSource
+ void setUpdateInterval(int msec);
+ QGeoPositionInfo lastKnownPosition(bool fromSatellitePositioningMethodsOnly = false) const;
+ PositioningMethods supportedPositioningMethods() const;
+ void setPreferredPositioningMethods(PositioningMethods methods);
+ int minimumUpdateInterval() const;
+ Error error() const;
+
+public Q_SLOTS:
+ virtual void startUpdates();
+ virtual void stopUpdates();
+
+ virtual void requestUpdate(int timeout = 0);
+
+ void processPositionUpdate(const QGeoPositionInfo& pInfo);
+ void processSinglePositionUpdate(const QGeoPositionInfo& pInfo);
+
+ void locationProviderDisabled();
+private Q_SLOTS:
+ void requestTimeout();
+
+private:
+ void reconfigureRunningSystem();
+
+ bool updatesRunning;
+ int androidClassKeyForUpdate;
+ int androidClassKeyForSingleRequest;
+ QList<QGeoPositionInfo> queuedSingleUpdates;
+ Error m_error;
+ QTimer m_requestTimer;
+};
+
+#endif // QGEOPOSITIONINFOSOURCE_ANDROID_P_H
diff --git a/src/plugins/position/android/src/src.pro b/src/plugins/position/android/src/src.pro
new file mode 100644
index 00000000..52e57bd3
--- /dev/null
+++ b/src/plugins/position/android/src/src.pro
@@ -0,0 +1,18 @@
+TARGET = qtposition_android
+QT = core positioning
+
+PLUGIN_TYPE = position
+load(qt_plugin)
+
+
+HEADERS = \
+ positionfactory_android.h \
+ qgeopositioninfosource_android_p.h \
+ jnipositioning.h
+
+SOURCES = \
+ positionfactory_android.cpp \
+ qgeopositioninfosource_android.cpp \
+ jnipositioning.cpp
+
+OTHER_FILES = plugin.json
diff --git a/src/plugins/position/position.pro b/src/plugins/position/position.pro
index d6e7c6b6..207b949b 100644
--- a/src/plugins/position/position.pro
+++ b/src/plugins/position/position.pro
@@ -5,6 +5,7 @@ config_gypsy:SUBDIRS += gypsy
simulator:SUBDIRS += simulator
blackberry:SUBDIRS += blackberry
ios:SUBDIRS += corelocation
+android:SUBDIRS += android
SUBDIRS += \
positionpoll
diff --git a/src/plugins/position/positionpoll/positionpollfactory.cpp b/src/plugins/position/positionpoll/positionpollfactory.cpp
index 0f02bff1..2acafbc7 100644
--- a/src/plugins/position/positionpoll/positionpollfactory.cpp
+++ b/src/plugins/position/positionpoll/positionpollfactory.cpp
@@ -42,19 +42,19 @@
#include "positionpollfactory.h"
#include "qgeoareamonitor_polling.h"
-QGeoPositionInfoSource *PositionPollFactory::positionInfoSource(QObject *parent)
+QGeoPositionInfoSource *QGeoPositionInfoSourceFactoryAndroid::positionInfoSource(QObject *parent)
{
Q_UNUSED(parent);
return 0;
}
-QGeoSatelliteInfoSource *PositionPollFactory::satelliteInfoSource(QObject *parent)
+QGeoSatelliteInfoSource *QGeoPositionInfoSourceFactoryAndroid::satelliteInfoSource(QObject *parent)
{
Q_UNUSED(parent);
return 0;
}
-QGeoAreaMonitorSource *PositionPollFactory::areaMonitor(QObject *parent)
+QGeoAreaMonitorSource *QGeoPositionInfoSourceFactoryAndroid::areaMonitor(QObject *parent)
{
QGeoAreaMonitorPolling *ret = new QGeoAreaMonitorPolling(parent);
if (ret && ret->isValid())
diff --git a/src/plugins/position/positionpoll/positionpollfactory.h b/src/plugins/position/positionpoll/positionpollfactory.h
index 79ad85eb..4b7818d4 100644
--- a/src/plugins/position/positionpoll/positionpollfactory.h
+++ b/src/plugins/position/positionpoll/positionpollfactory.h
@@ -45,7 +45,7 @@
#include <QObject>
#include <QGeoPositionInfoSourceFactory>
-class PositionPollFactory : public QObject, public QGeoPositionInfoSourceFactory
+class QGeoPositionInfoSourceFactoryAndroid : public QObject, public QGeoPositionInfoSourceFactory
{
Q_OBJECT
Q_PLUGIN_METADATA(IID "org.qt-project.qt.position.sourcefactory/5.0"
diff --git a/src/positioning/positioning.pro b/src/positioning/positioning.pro
index bb4a6692..2e751bfb 100644
--- a/src/positioning/positioning.pro
+++ b/src/positioning/positioning.pro
@@ -4,6 +4,17 @@ QT = core-private
QMAKE_DOCS = $$PWD/doc/qtpositioning.qdocconf
OTHER_FILES += doc/src/*.qdoc # show .qdoc files in Qt Creator
+ANDROID_BUNDLED_JAR_DEPENDENCIES = \
+ jar/QtPositioning-bundled.jar:org.qtproject.qt5.android.positioning.QtPositioning
+ANDROID_JAR_DEPENDENCIES = \
+ jar/QtPositioning.jar:org.qtproject.qt5.android.positioning.QtPositioning
+ANDROID_PERMISSIONS = \
+ android.permission.ACCESS_FINE_LOCATION
+ANDROID_LIB_DEPENDENCIES = \
+ plugins/position/libqtposition_android.so
+MODULE_PLUGIN_TYPES = \
+ position
+
PUBLIC_HEADERS += \
qgeoaddress.h \
qgeoareamonitorinfo.h \