summaryrefslogtreecommitdiffstats
path: root/qt-mobile
diff options
context:
space:
mode:
authorGravatar Dirk Hohndel <dirk@hohndel.org>2015-11-18 18:30:55 -0800
committerGravatar Dirk Hohndel <dirk@hohndel.org>2015-11-18 18:34:49 -0800
commit456cc3955a3e343ea1d2303484d5c79b44635155 (patch)
tree4a48b0d244be602d1043c3e86b89d0b700f9afc2 /qt-mobile
parent0b804e5b215f94a924f0d516da25dc46a81e710d (diff)
downloadsubsurface-456cc3955a3e343ea1d2303484d5c79b44635155.tar.gz
Location service: move into subsurface-core
While this is primarily something targeted at a mobile device, with many of the 2 in 1 devices it is possible that the user might be running the desktop version of Subsurface on a mobile device. As a first step to make it possible to collect GPS fixes on such a device we need to make the infrastructure to do so available in the desktop application as well. This still needs to be hooked up in the desktop UI. Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
Diffstat (limited to 'qt-mobile')
-rw-r--r--qt-mobile/gpslocation.cpp334
-rw-r--r--qt-mobile/gpslocation.h43
-rw-r--r--qt-mobile/qmlmanager.h2
3 files changed, 1 insertions, 378 deletions
diff --git a/qt-mobile/gpslocation.cpp b/qt-mobile/gpslocation.cpp
deleted file mode 100644
index a07239b45..000000000
--- a/qt-mobile/gpslocation.cpp
+++ /dev/null
@@ -1,334 +0,0 @@
-#include "qt-mobile/gpslocation.h"
-#include "pref.h"
-#include "dive.h"
-#include "helpers.h"
-#include <time.h>
-#include <unistd.h>
-#include <QDebug>
-#include <QVariant>
-#include <QUrlQuery>
-#include <QApplication>
-#include <QTimer>
-
-#define GPS_FIX_ADD_URL "http://api.subsurface-divelog.org/api/dive/add/"
-#define GET_WEBSERVICE_UID_URL "https://cloud.subsurface-divelog.org/webuserid/"
-
-GpsLocation::GpsLocation(void (*showMsgCB)(const char *), QObject *parent)
-{
- showMessageCB = showMsgCB;
- // create a QSettings object that's separate from the main application settings
- geoSettings = new QSettings(QSettings::NativeFormat, QSettings::UserScope,
- QString("org.subsurfacedivelog"), QString("subsurfacelocation"), this);
- gpsSource = QGeoPositionInfoSource::createDefaultSource(parent);
- if (gpsSource != 0) {
- QString msg = QString("have position source %1").arg(gpsSource->sourceName());
- connect(gpsSource, SIGNAL(positionUpdated(QGeoPositionInfo)), this, SLOT(newPosition(QGeoPositionInfo)));
- connect(gpsSource, SIGNAL(updateTimeout()), this, SLOT(updateTimeout()));
- gpsSource->setUpdateInterval(5 * 60 * 1000); // 5 minutes so the device doesn't drain the battery
- } else {
- status("don't have GPS source");
- }
- userAgent = getUserAgent();
-}
-
-void GpsLocation::serviceEnable(bool toggle)
-{
- if (!gpsSource) {
- if (toggle)
- status("Can't start location service, no location source available");
- return;
- }
- if (toggle) {
- gpsSource->startUpdates();
- status("Starting Subsurface GPS service");
- } else {
- gpsSource->stopUpdates();
- status("Stopping Subsurface GPS service");
- }
-}
-
-void GpsLocation::newPosition(QGeoPositionInfo pos)
-{
- time_t lastTime;
- QGeoCoordinate lastCoord;
- QString msg("received new position %1");
- status(qPrintable(msg.arg(pos.coordinate().toString())));
- int nr = geoSettings->value("count", 0).toInt();
- if (nr) {
- lastCoord.setLatitude(geoSettings->value(QString("gpsFix%1_lat").arg(nr - 1)).toInt() / 1000000.0);
- lastCoord.setLongitude(geoSettings->value(QString("gpsFix%1_lon").arg(nr - 1)).toInt() / 1000000.0);
- lastTime = geoSettings->value(QString("gpsFix%1_time").arg(nr - 1)).toULongLong();
- }
- // if we have no record stored or if at least the configured minimum
- // time has passed or we moved at least the configured minimum distance
- if (!nr ||
- pos.timestamp().toTime_t() > lastTime + prefs.time_threshold ||
- lastCoord.distanceTo(pos.coordinate()) > prefs.distance_threshold) {
- geoSettings->setValue("count", nr + 1);
- geoSettings->setValue(QString("gpsFix%1_time").arg(nr), pos.timestamp().toTime_t());
- geoSettings->setValue(QString("gpsFix%1_lat").arg(nr), rint(pos.coordinate().latitude() * 1000000));
- geoSettings->setValue(QString("gpsFix%1_lon").arg(nr), rint(pos.coordinate().longitude() * 1000000));
- geoSettings->sync();
- }
-}
-
-void GpsLocation::updateTimeout()
-{
- status("request to get new position timed out");
-}
-
-void GpsLocation::status(QString msg)
-{
- qDebug() << msg;
- if (showMessageCB)
- (*showMessageCB)(qPrintable(msg));
-}
-
-QString GpsLocation::getUserid(QString user, QString passwd)
-{
- qDebug() << "called getUserid";
- QEventLoop loop;
- QTimer timer;
- timer.setSingleShot(true);
-
- QNetworkAccessManager *manager = new QNetworkAccessManager(qApp);
- QUrl url(GET_WEBSERVICE_UID_URL);
- QString data;
- data = user + " " + passwd;
- QNetworkRequest request;
- request.setUrl(url);
- request.setRawHeader("User-Agent", getUserAgent().toUtf8());
- request.setRawHeader("Accept", "text/html");
- request.setRawHeader("Content-type", "application/x-www-form-urlencoded");
- reply = manager->post(request, data.toUtf8());
- connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit);
- connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
- connect(reply, SIGNAL(error(QNetworkReply::NetworkError)),
- this, SLOT(getUseridError(QNetworkReply::NetworkError)));
- timer.start(10000);
- loop.exec();
- if (timer.isActive()) {
- timer.stop();
- if (reply->error() == QNetworkReply::NoError) {
- QString result = reply->readAll();
- status(QString("received ") + result);
- result.remove("WebserviceID:");
- reply->deleteLater();
- return result;
- }
- } else {
- status("Getting Web service ID timed out");
- }
- reply->deleteLater();
- return QString();
-}
-
-int GpsLocation::getGpsNum() const
-{
- return geoSettings->value("count", 0).toInt();
-}
-
-struct gpsTracker {
- degrees_t latitude;
- degrees_t longitude;
- time_t when;
-};
-
-static void copy_gps_location(struct gpsTracker *gps, struct dive *d)
-{
- struct dive_site *ds = get_dive_site_by_uuid(d->dive_site_uuid);
- ds->latitude = gps->latitude;
- ds->longitude = gps->longitude;
-}
-
-#define SAME_GROUP 6 * 3600 /* six hours */
-bool GpsLocation::applyLocations()
-{
- int i;
- bool changed = false;
- int last = 0;
- int cnt = geoSettings->value("count", 0).toInt();
- if (cnt == 0)
- return false;
-
- // create a table with the GPS information
- struct gpsTracker *gpsTable = (struct gpsTracker *)calloc(cnt, sizeof(struct gpsTracker));
- for (int i = 0; i < cnt; i++) {
- gpsTable[i].latitude.udeg = geoSettings->value(QString("gpsFix%1_lat").arg(i)).toInt();
- gpsTable[i].longitude.udeg = geoSettings->value(QString("gpsFix%1_lon").arg(i)).toInt();
- gpsTable[i].when = geoSettings->value(QString("gpsFix%1_time").arg(i)).toULongLong();
- }
-
- // now walk the dive table and see if we can fill in missing gps data
- struct dive *d;
- for_each_dive(i, d) {
- if (dive_has_gps_location(d))
- continue;
- for (int j = last; j < cnt; j++) {
- if (time_during_dive_with_offset(d, gpsTable[j].when, SAME_GROUP)) {
- if (verbose)
- qDebug() << "processing gpsFix @" << get_dive_date_string(gpsTable[j].when) <<
- "which is withing six hours of dive from" <<
- get_dive_date_string(d->when) << "until" <<
- get_dive_date_string(d->when + d->duration.seconds);
- /*
- * If position is fixed during dive. This is the good one.
- * Asign and mark position, and end gps_location loop
- */
- if (time_during_dive_with_offset(d, gpsTable[j].when, 0)) {
- if (verbose)
- qDebug() << "gpsFix is during the dive, pick that one";
- copy_gps_location(gpsTable + j, d);
- changed = true;
- last = j;
- break;
- } else {
- /*
- * If it is not, check if there are more position fixes in SAME_GROUP range
- */
- if (j + 1 < cnt && time_during_dive_with_offset(d, gpsTable[j+1].when, SAME_GROUP)) {
- if (verbose)
- qDebug() << "look at the next gps fix @" << get_dive_date_string(gpsTable[j+1].when);
- /* first let's test if this one is during the dive */
- if (time_during_dive_with_offset(d, gpsTable[j+1].when, 0)) {
- if (verbose)
- qDebug() << "which is during the dive, pick that one";
- copy_gps_location(gpsTable + j + 1, d);
- changed = true;
- last = j + 1;
- break;
- }
- /* we know the gps fixes are sorted; if they are both before the dive, ignore the first,
- * if theay are both after the dive, take the first,
- * if the first is before and the second is after, take the closer one */
- if (gpsTable[j+1].when < d->when) {
- if (verbose)
- qDebug() << "which is closer to the start of the dive, do continue with that";
- continue;
- } else if (gpsTable[j].when > d->when + d->duration.seconds) {
- if (verbose)
- qDebug() << "which is even later after the end of the dive, so pick the previous one";
- copy_gps_location(gpsTable + j, d);
- changed = true;
- last = j;
- break;
- } else {
- /* ok, gpsFix is before, nextgpsFix is after */
- if (d->when - gpsTable[j].when <= gpsTable[j+1].when - (d->when + d->duration.seconds)) {
- if (verbose)
- qDebug() << "pick the one before as it's closer to the start";
- copy_gps_location(gpsTable + j, d);
- changed = true;
- last = j;
- break;
- } else {
- if (verbose)
- qDebug() << "pick the one after as it's closer to the start";
- copy_gps_location(gpsTable + j + 1, d);
- changed = true;
- last = j + 1;
- break;
- }
- }
- /*
- * If no more positions in range, the actual is the one. Asign, mark and end loop.
- */
- } else {
- if (verbose)
- qDebug() << "which seems to be the best one for this dive, so pick it";
- copy_gps_location(gpsTable + j, d);
- changed = true;
- last = j;
- break;
- }
- }
- } else {
- /* If position is out of SAME_GROUP range and in the future, mark position for
- * next dive iteration and end the gps_location loop
- */
- if (gpsTable[j].when >= d->when + d->duration.seconds + SAME_GROUP) {
- last = j;
- break;
- }
- }
-
- }
- }
-}
-
-void GpsLocation::clearGpsData()
-{
- geoSettings->clear();
- geoSettings->sync();
-}
-
-void GpsLocation::postError(QNetworkReply::NetworkError error)
-{
- status(QString("error when sending a GPS fix: %1").arg(reply->errorString()));
-}
-
-void GpsLocation::getUseridError(QNetworkReply::NetworkError error)
-{
- status(QString("error when retrieving Subsurface webservice user id: %1").arg(reply->errorString()));
-}
-
-void GpsLocation::uploadToServer()
-{
- // we want to do this one at a time (the server prefers that)
- QEventLoop loop;
- QTimer timer;
- timer.setSingleShot(true);
-
- QNetworkAccessManager *manager = new QNetworkAccessManager(qApp);
- QUrl url(GPS_FIX_ADD_URL);
- int count = geoSettings->value("count", 0).toInt();
- for (int i = 0; i < count; i++) {
- QDateTime dt;
- QUrlQuery data;
- if (geoSettings->contains(QString("gpsFix%1_uploaded").arg(i)))
- continue;
- time_t when = geoSettings->value(QString("gpsFix%1_time").arg(i), 0).toULongLong();
- dt.setTime_t(when);
- qDebug() << dt.toString() << get_dive_date_string(when);
- data.addQueryItem("login", prefs.userid);
- data.addQueryItem("dive_date", dt.toString("yyyy-MM-dd"));
- data.addQueryItem("dive_time", dt.toString("hh:mm"));
- data.addQueryItem("dive_latitude", QString::number(geoSettings->value(QString("gpsFix%1_lat").arg(i)).toInt() / 1000000.0));
- data.addQueryItem("dive_longitude", QString::number(geoSettings->value(QString("gpsFix%1_lon").arg(i)).toInt() / 1000000.0));
- QString name(geoSettings->value(QString("gpsFix%1_name").arg(i)).toString());
- if (name.isEmpty())
- name = "Auto-created dive";
- data.addQueryItem("dive_name", name);
- status(data.toString(QUrl::FullyEncoded).toUtf8());
- QNetworkRequest request;
- request.setUrl(url);
- request.setRawHeader("User-Agent", getUserAgent().toUtf8());
- request.setRawHeader("Accept", "text/json");
- request.setRawHeader("Content-type", "application/x-www-form-urlencoded");
- reply = manager->post(request, data.toString(QUrl::FullyEncoded).toUtf8());
- connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit);
- connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
- // somehoe I cannot get this to work with the new connect syntax:
- // connect(reply, &QNetworkReply::error, this, &GpsLocation::postError);
- connect(reply, SIGNAL(error(QNetworkReply::NetworkError)),
- this, SLOT(postError(QNetworkReply::NetworkError)));
- timer.start(10000);
- loop.exec();
- if (timer.isActive()) {
- timer.stop();
- if (reply->error() != QNetworkReply::NoError) {
- QString response = reply->readAll();
- if (!response.contains("Duplicate entry")) {
- status(QString("Server response:") + reply->readAll());
- break;
- }
- }
- } else {
- status("Uploading to server timed out");
- break;
- }
- reply->deleteLater();
- status(QString("completed sending gps fix %1 - response: ").arg(i) + reply->readAll());
- geoSettings->setValue(QString("gpsFix%1_uploaded").arg(i), 1);
- }
-}
diff --git a/qt-mobile/gpslocation.h b/qt-mobile/gpslocation.h
deleted file mode 100644
index 55b47f07e..000000000
--- a/qt-mobile/gpslocation.h
+++ /dev/null
@@ -1,43 +0,0 @@
-#ifndef GPSLOCATION_H
-#define GPSLOCATION_H
-
-#include "units.h"
-#include <QObject>
-#include <QGeoCoordinate>
-#include <QGeoPositionInfoSource>
-#include <QGeoPositionInfo>
-#include <QSettings>
-#include <QNetworkReply>
-
-class GpsLocation : QObject
-{
- Q_OBJECT
-public:
- GpsLocation(void (*showMsgCB)(const char *msg), QObject *parent);
- bool applyLocations();
- int getGpsNum() const;
- QString getUserid(QString user, QString passwd);
-
-private:
- QGeoPositionInfo lastPos;
- QGeoPositionInfoSource *gpsSource;
- void status(QString msg);
- QSettings *geoSettings;
- QNetworkReply *reply;
- QString userAgent;
- void (*showMessageCB)(const char *msg);
-
-signals:
-
-public slots:
- void serviceEnable(bool toggle);
- void newPosition(QGeoPositionInfo pos);
- void updateTimeout();
- void uploadToServer();
- void postError(QNetworkReply::NetworkError error);
- void getUseridError(QNetworkReply::NetworkError error);
- void clearGpsData();
-
-};
-
-#endif // GPSLOCATION_H
diff --git a/qt-mobile/qmlmanager.h b/qt-mobile/qmlmanager.h
index 51b6b39d9..07a7b9f60 100644
--- a/qt-mobile/qmlmanager.h
+++ b/qt-mobile/qmlmanager.h
@@ -4,7 +4,7 @@
#include <QObject>
#include <QString>
-#include "qt-mobile/gpslocation.h"
+#include "gpslocation.h"
void qmlUiShowMessage(const char *errorString);