summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGravatar Dirk Hohndel <dirk@hohndel.org>2016-01-07 21:43:22 -0800
committerGravatar Dirk Hohndel <dirk@hohndel.org>2016-01-07 21:43:22 -0800
commit1eda61e1158de19a47acdfc5525f5f0df002052f (patch)
treefd981938b70fe20c20b3ea2ffc90abd9db74de9a
parentce3a78efcac2c9b6bd215bc191560a64c54fd628 (diff)
parentb6ae6979e530fa32dbdb472a2e9698cb719945a8 (diff)
downloadsubsurface-1eda61e1158de19a47acdfc5525f5f0df002052f.tar.gz
Merge branch 'gpsList'
-rw-r--r--CMakeLists.txt1
-rw-r--r--qt-mobile/qml/GpsList.qml113
-rw-r--r--qt-mobile/qml/main.qml19
-rw-r--r--qt-mobile/qml/mobile-resources.qrc1
-rw-r--r--qt-mobile/qmlmanager.cpp12
-rw-r--r--qt-mobile/qmlmanager.h2
-rw-r--r--qt-models/gpslistmodel.cpp96
-rw-r--r--qt-models/gpslistmodel.h57
-rwxr-xr-xscripts/build.sh42
-rw-r--r--subsurface-core/gpslocation.cpp116
-rw-r--r--subsurface-core/gpslocation.h9
-rw-r--r--subsurface-mobile-helper.cpp8
12 files changed, 450 insertions, 26 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 3f4d90d2a..18101e087 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -226,6 +226,7 @@ if(${SUBSURFACE_TARGET_EXECUTABLE} MATCHES "MobileExecutable")
qt-mobile/qmlmanager.cpp
qt-mobile/qmlprofile.cpp
qt-models/divelistmodel.cpp
+ qt-models/gpslistmodel.cpp
subsurface-mobile-main.cpp
subsurface-mobile-helper.cpp
)
diff --git a/qt-mobile/qml/GpsList.qml b/qt-mobile/qml/GpsList.qml
new file mode 100644
index 000000000..f0afc56db
--- /dev/null
+++ b/qt-mobile/qml/GpsList.qml
@@ -0,0 +1,113 @@
+import QtQuick 2.3
+import QtQuick.Controls 1.2
+import QtQuick.Controls.Styles 1.2
+import QtQuick.Window 2.2
+import QtQuick.Dialogs 1.2
+import QtQuick.Layouts 1.1
+import QtQuick.Window 2.2
+import org.subsurfacedivelog.mobile 1.0
+import org.kde.plasma.mobilecomponents 0.2 as MobileComponents
+
+MobileComponents.Page {
+ id: gpsListWindow
+ width: parent.width - MobileComponents.Units.gridUnit
+ anchors.margins: MobileComponents.Units.gridUnit / 2
+ objectName: "gpsList"
+
+ contextualActions: [
+ Action {
+ id: closeLog
+ text: "Close GPS list"
+ iconName: "view-readermode"
+ onTriggered: {
+ stackView.pop()
+ contextDrawer.close()
+ }
+ }
+ ]
+
+ Component {
+ id: gpsDelegate
+ MobileComponents.ListItem {
+ id: gpsFix
+ enabled: true
+ width: parent.width
+ property int horizontalPadding: MobileComponents.Units.gridUnit / 2 - MobileComponents.Units.smallSpacing + 1
+ Item {
+ width: parent.width - MobileComponents.Units.gridUnit
+ height: childrenRect.height - MobileComponents.Units.smallSpacing
+ GridLayout {
+ columns: 4
+ id: timeAndName
+ anchors {
+ left: parent.left
+ leftMargin: horizontalPadding
+ right: parent.right
+ rightMargin: horizontalPadding
+ }
+ MobileComponents.Label {
+ text: 'Date: '
+ opacity: 0.6
+ font.pointSize: subsurfaceTheme.smallPointSize
+ }
+ MobileComponents.Label {
+ text: when
+ width: Math.max(parent.width / 5, paintedWidth) // helps vertical alignment throughout listview
+ font.pointSize: subsurfaceTheme.smallPointSize
+ }
+ MobileComponents.Label {
+ text: 'Name: '
+ opacity: 0.6
+ font.pointSize: subsurfaceTheme.smallPointSize
+ }
+ MobileComponents.Label {
+ text: name
+ width: Math.max(parent.width / 5, paintedWidth) // helps vertical alignment throughout listview
+ font.pointSize: subsurfaceTheme.smallPointSize
+ }
+ MobileComponents.Label {
+ text: 'Latitude: '
+ opacity: 0.6
+ font.pointSize: subsurfaceTheme.smallPointSize
+ }
+ MobileComponents.Label {
+ text: latitude
+ font.pointSize: subsurfaceTheme.smallPointSize
+ }
+ MobileComponents.Label {
+ text: 'Longitude: '
+ opacity: 0.6
+ font.pointSize: subsurfaceTheme.smallPointSize
+ }
+ MobileComponents.Label {
+ text: longitude
+ font.pointSize: subsurfaceTheme.smallPointSize
+ }
+ }
+
+ }
+ }
+ }
+
+ ScrollView {
+ anchors.fill: parent
+ ListView {
+ id: gpsListView
+ anchors.fill: parent
+ model: gpsModel
+ currentIndex: -1
+ delegate: gpsDelegate
+ boundsBehavior: Flickable.StopAtBounds
+ maximumFlickVelocity: parent.height * 5
+ cacheBuffer: parent.height *5
+ focus: true
+ clip: true
+ header: MobileComponents.Heading {
+ x: MobileComponents.Units.gridUnit / 2
+ height: paintedHeight + MobileComponents.Units.gridUnit / 2
+ verticalAlignment: Text.AlignBottom
+ text: "List of stored GPS fixes"
+ }
+ }
+ }
+}
diff --git a/qt-mobile/qml/main.qml b/qt-mobile/qml/main.qml
index c0d079106..db238bb22 100644
--- a/qt-mobile/qml/main.qml
+++ b/qt-mobile/qml/main.qml
@@ -98,6 +98,21 @@ MobileComponents.ApplicationWindow {
}
Action {
+ text: "Download GPS data"
+ onTriggered: {
+ manager.downloadGpsData();
+ }
+ }
+
+ Action {
+ text: "Show GPS fixes"
+ onTriggered: {
+ manager.populateGpsData();
+ stackView.push(gpsWindow)
+ }
+ }
+
+ Action {
text: "Clear GPS cache"
onTriggered: {
manager.clearGpsData();
@@ -217,6 +232,10 @@ MobileComponents.ApplicationWindow {
visible: false
}
+ GpsList {
+ id: gpsWindow
+ }
+
ThemeTest {
id: themetest
visible: false
diff --git a/qt-mobile/qml/mobile-resources.qrc b/qt-mobile/qml/mobile-resources.qrc
index cde0d42cf..bf38cf074 100644
--- a/qt-mobile/qml/mobile-resources.qrc
+++ b/qt-mobile/qml/mobile-resources.qrc
@@ -9,6 +9,7 @@
<file>DiveDetailsEdit.qml</file>
<file>DiveDetailsView.qml</file>
<file>DownloadFromDiveComputer.qml</file>
+ <file>GpsList.qml</file>
<file>Log.qml</file>
<file>TopBar.qml</file>
<file>ThemeTest.qml</file>
diff --git a/qt-mobile/qmlmanager.cpp b/qt-mobile/qmlmanager.cpp
index 00f8b5d48..4dd5ca4fb 100644
--- a/qt-mobile/qmlmanager.cpp
+++ b/qt-mobile/qmlmanager.cpp
@@ -501,9 +501,21 @@ void QMLManager::sendGpsData()
locationProvider->uploadToServer();
}
+void QMLManager::downloadGpsData()
+{
+ locationProvider->downloadFromServer();
+ locationProvider->updateModel();
+}
+
+void QMLManager::populateGpsData()
+{
+ locationProvider->updateModel();
+}
+
void QMLManager::clearGpsData()
{
locationProvider->clearGpsData();
+ locationProvider->updateModel();
}
QString QMLManager::logText() const
diff --git a/qt-mobile/qmlmanager.h b/qt-mobile/qmlmanager.h
index 24ca265e6..1953f0d76 100644
--- a/qt-mobile/qmlmanager.h
+++ b/qt-mobile/qmlmanager.h
@@ -77,6 +77,8 @@ public slots:
QString addDive();
void applyGpsData();
void sendGpsData();
+ void downloadGpsData();
+ void populateGpsData();
void clearGpsData();
void finishSetup();
void showMap(QString location);
diff --git a/qt-models/gpslistmodel.cpp b/qt-models/gpslistmodel.cpp
new file mode 100644
index 000000000..6bd1cfdf0
--- /dev/null
+++ b/qt-models/gpslistmodel.cpp
@@ -0,0 +1,96 @@
+#include "gpslistmodel.h"
+#include "helpers.h"
+
+GpsTracker::GpsTracker()
+{
+ m_latitude = 0;
+ m_longitude = 0;
+ m_when = 0;
+ m_name = QString();
+}
+
+GpsTracker::~GpsTracker()
+{
+}
+
+uint64_t GpsTracker::when() const
+{
+ return m_when;
+}
+
+int32_t GpsTracker::latitude() const
+{
+ return m_latitude;
+}
+
+int32_t GpsTracker::longitude() const
+{
+ return m_longitude;
+}
+
+QString GpsTracker::name() const
+{
+ return m_name;
+}
+
+GpsListModel *GpsListModel::m_instance = NULL;
+
+GpsListModel::GpsListModel(QObject *parent) : QAbstractListModel(parent)
+{
+ m_instance = this;
+}
+
+void GpsListModel::addGpsFix(gpsTracker *g)
+{
+ beginInsertColumns(QModelIndex(), rowCount(), rowCount());
+ m_gpsFixes.append(GpsTracker(g));
+ endInsertRows();
+}
+
+void GpsListModel::clear()
+{
+ if (m_gpsFixes.count()) {
+ beginRemoveRows(QModelIndex(), 0, m_gpsFixes.count() - 1);
+ m_gpsFixes.clear();
+ endRemoveRows();
+ }
+}
+
+int GpsListModel::rowCount(const QModelIndex &parent) const
+{
+ return m_gpsFixes.count();
+}
+
+QVariant GpsListModel::data(const QModelIndex &index, int role) const
+{
+ if (index.row() < 0 || index.row() > m_gpsFixes.count())
+ return QVariant();
+
+ const GpsTracker &gt = m_gpsFixes[index.row()];
+
+ if (role == GpsDateRole)
+ return get_short_dive_date_string(gt.when());
+ else if (role == GpsNameRole)
+ return QString(gt.name());
+ else if (role == GpsLatitudeRole)
+ return QString::number(gt.latitude() / 1000000.0, 'f', 6);
+ else if (role == GpsLongitudeRole)
+ return QString::number(gt.longitude() / 1000000.0, 'f', 6);
+ return QVariant();
+}
+
+QHash<int, QByteArray> GpsListModel::roleNames() const
+{
+ QHash<int, QByteArray> roles;
+ roles[GpsDateRole] = "when";
+ roles[GpsNameRole] = "name";
+ roles[GpsLatitudeRole] = "latitude";
+ roles[GpsLongitudeRole] = "longitude";
+ return roles;
+}
+
+GpsListModel *GpsListModel::instance()
+{
+ return m_instance;
+}
+
diff --git a/qt-models/gpslistmodel.h b/qt-models/gpslistmodel.h
new file mode 100644
index 000000000..35a5a03c4
--- /dev/null
+++ b/qt-models/gpslistmodel.h
@@ -0,0 +1,57 @@
+#ifndef GPSLISTMODEL_H
+#define GPSLISTMODEL_H
+
+#include "gpslocation.h"
+#include <QObject>
+#include <QAbstractListModel>
+
+class GpsTracker
+{
+private:
+ quint64 m_when;
+ qint32 m_latitude;
+ qint32 m_longitude;
+ QString m_name;
+
+public:
+ GpsTracker(struct gpsTracker *gt)
+ {
+ m_when = gt->when;
+ m_latitude = gt->latitude.udeg;
+ m_longitude = gt->longitude.udeg;
+ m_name = gt->name;
+ }
+ GpsTracker();
+ ~GpsTracker();
+ uint64_t when() const;
+ int32_t latitude() const;
+ int32_t longitude() const;
+ QString name() const;
+};
+
+class GpsListModel : public QAbstractListModel
+{
+ Q_OBJECT
+public:
+
+ enum GpsListRoles {
+ GpsDateRole = Qt::UserRole + 1,
+ GpsNameRole,
+ GpsLatitudeRole,
+ GpsLongitudeRole
+ };
+
+ static GpsListModel *instance();
+ GpsListModel(QObject *parent = 0);
+ void addGpsFix(struct gpsTracker *g);
+ void clear();
+ int rowCount(const QModelIndex &parent = QModelIndex()) const;
+ QHash<int, QByteArray> roleNames() const;
+ QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
+
+private:
+ QList<GpsTracker> m_gpsFixes;
+ static GpsListModel *m_instance;
+};
+
+#endif // GPSLISTMODEL_H
diff --git a/scripts/build.sh b/scripts/build.sh
index 9db5c983d..2c470ef94 100755
--- a/scripts/build.sh
+++ b/scripts/build.sh
@@ -141,30 +141,32 @@ if [ $PLATFORM = Darwin ] ; then
fi
fi
-# build grantlee
+if [ "$SUBSURFACE_EXECUTABLE" = "DesktopExecutable" ] ; then
+ # build grantlee
-cd $SRC
+ cd $SRC
-if [ ! -d grantlee ] ; then
- if [[ $1 = local ]] ; then
- git clone $SRC/../grantlee grantlee
- else
- git clone git://subsurface-divelog.org/grantlee
+ if [ ! -d grantlee ] ; then
+ if [[ $1 = local ]] ; then
+ git clone $SRC/../grantlee grantlee
+ else
+ git clone git://subsurface-divelog.org/grantlee
+ fi
fi
+ cd grantlee
+ if ! git checkout v5.0.0 ; then
+ echo "can't check out v5.0.0 of grantlee -- giving up"
+ exit 1
+ fi
+ mkdir -p build
+ cd build
+ cmake -DCMAKE_BUILD_TYPE=Release \
+ -DCMAKE_INSTALL_PREFIX=$INSTALL_ROOT \
+ -DBUILD__TESTS=NO \
+ $SRC/grantlee
+ make -j4
+ make install
fi
-cd grantlee
-if ! git checkout v5.0.0 ; then
- echo "can't check out v5.0.0 of grantlee -- giving up"
- exit 1
-fi
-mkdir -p build
-cd build
-cmake -DCMAKE_BUILD_TYPE=Release \
- -DCMAKE_INSTALL_PREFIX=$INSTALL_ROOT \
- -DBUILD__TESTS=NO \
- $SRC/grantlee
-make -j4
-make install
# pull the plasma-mobile components from upstream if building Subsurface-mobile
if [ "$SUBSURFACE_EXECUTABLE" = "MobileExecutable" ] ; then
diff --git a/subsurface-core/gpslocation.cpp b/subsurface-core/gpslocation.cpp
index 9019f59c7..cbcb6910b 100644
--- a/subsurface-core/gpslocation.cpp
+++ b/subsurface-core/gpslocation.cpp
@@ -1,4 +1,5 @@
#include "gpslocation.h"
+#include "gpslistmodel.h"
#include "pref.h"
#include "dive.h"
#include "helpers.h"
@@ -9,8 +10,12 @@
#include <QUrlQuery>
#include <QApplication>
#include <QTimer>
+#include <QJsonDocument>
+#include <QJsonObject>
+#include <QJsonArray>
#define GPS_FIX_ADD_URL "http://api.subsurface-divelog.org/api/dive/add/"
+#define GPS_FIX_DOWNLOAD_URL "http://api.subsurface-divelog.org/api/dive/get/"
#define GET_WEBSERVICE_UID_URL "https://cloud.subsurface-divelog.org/webuserid/"
GpsLocation *GpsLocation::m_Instance = NULL;
@@ -195,12 +200,6 @@ 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);
@@ -325,6 +324,32 @@ void GpsLocation::applyLocations()
mark_divelist_changed(true);
}
+void GpsLocation::updateModel()
+{
+ GpsListModel *gpsListModel = GpsListModel::instance();
+ if (!gpsListModel) {
+ qDebug() << "no gpsListModel";
+ return;
+ }
+ int cnt = geoSettings->value("count", 0).toInt();
+ if (cnt == 0) {
+ qDebug() << "no gps fixes";
+ gpsListModel->clear();
+ return;
+ }
+
+ // create a table with the GPS information
+ struct gpsTracker gt;
+ for (int i = 0; i < cnt; i++) {
+ gt.latitude.udeg = geoSettings->value(QString("gpsFix%1_lat").arg(i)).toInt();
+ gt.longitude.udeg = geoSettings->value(QString("gpsFix%1_lon").arg(i)).toInt();
+ gt.when = geoSettings->value(QString("gpsFix%1_time").arg(i)).toULongLong();
+ gt.name = geoSettings->value(QString("gpsFix%1_name").arg(i)).toString();
+ gpsListModel->addGpsFix(&gt);
+ }
+ qDebug() << "added" << cnt << "gps fixes to model";
+}
+
void GpsLocation::clearGpsData()
{
geoSettings->clear();
@@ -401,3 +426,82 @@ void GpsLocation::uploadToServer()
geoSettings->setValue(QString("gpsFix%1_uploaded").arg(i), 1);
}
}
+
+void GpsLocation::downloadFromServer()
+{
+ QEventLoop loop;
+ QTimer timer;
+ timer.setSingleShot(true);
+ QNetworkAccessManager *manager = new QNetworkAccessManager(qApp);
+ QUrl url(QString(GPS_FIX_DOWNLOAD_URL "?login=%1").arg(prefs.userid));
+ QNetworkRequest request;
+ request.setUrl(url);
+ request.setRawHeader("User-Agent", getUserAgent().toUtf8());
+ request.setRawHeader("Accept", "text/json");
+ request.setRawHeader("Content-type", "text/html");
+ qDebug() << "downloadFromServer accessing" << url;
+ reply = manager->get(request);
+ 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()) {
+ QString response = reply->readAll();
+ QJsonDocument json = QJsonDocument::fromJson(response.toLocal8Bit());
+ QJsonObject object = json.object();
+ if (object.value("download").toString() != "ok") {
+ qDebug() << "problems downloading GPS fixes";
+ return;
+ }
+ // create a table with the GPS information
+ QHash<int, struct gpsTracker> gpsFixes;
+ int existing = geoSettings->value("count", 0).toInt();
+ for (int i = 0; i < existing; i++) {
+ struct gpsTracker gt;
+ gt.latitude.udeg = geoSettings->value(QString("gpsFix%1_lat").arg(i)).toInt();
+ gt.longitude.udeg = geoSettings->value(QString("gpsFix%1_lon").arg(i)).toInt();
+ gt.when = geoSettings->value(QString("gpsFix%1_time").arg(i)).toULongLong();
+ gpsFixes.insert(gt.when, gt);
+ }
+ qDebug() << "already have" << gpsFixes.count() << "GPS fixes";
+ QJsonArray dives = object.value("dives").toArray();
+ qDebug() << dives.count() << "GPS fixes downloaded";
+ for (int i = 0; i < dives.count(); i++) {
+ QJsonObject fix = dives[i].toObject();
+ QString date = fix.value("date").toString();
+ QString time = fix.value("time").toString();
+ QString name = fix.value("name").toString();
+ QString latitude = fix.value("latitude").toString();
+ QString longitude = fix.value("longitude").toString();
+ QDateTime timestamp = QDateTime::fromString(date + " " + time, "yyyy-M-d hh:m:s");
+
+ struct gpsTracker gt;
+ gt.when = timestamp.toMSecsSinceEpoch() / 1000 + gettimezoneoffset(timestamp.toMSecsSinceEpoch() / 1000);
+ gt.latitude.udeg = latitude.toDouble() * 1000000;
+ gt.longitude.udeg = longitude.toDouble() * 1000000;
+ gt.name = name;
+ gpsFixes.insert(gt.when, gt);
+ }
+ QList<int> keys = gpsFixes.keys();
+ qSort(keys);
+ for (int i = 0; i < keys.count(); i++) {
+ struct gpsTracker gt = gpsFixes.value(keys[i]);
+ geoSettings->setValue(QString("gpsFix%1_time").arg(i), (uint64_t)gt.when);
+ geoSettings->setValue(QString("gpsFix%1_name").arg(i), gt.name);
+ geoSettings->setValue(QString("gpsFix%1_lat").arg(i), gt.latitude.udeg);
+ geoSettings->setValue(QString("gpsFix%1_lon").arg(i), gt.longitude.udeg);
+ }
+ geoSettings->setValue("count", keys.count());
+ } else {
+ qDebug() << "network error" << reply->error() << reply->errorString() << reply->readAll();
+ }
+ } else {
+ qDebug() << "download timed out";
+ status("Download from server timed out");
+ }
+ reply->deleteLater();
+}
diff --git a/subsurface-core/gpslocation.h b/subsurface-core/gpslocation.h
index 94706f611..3dbe48320 100644
--- a/subsurface-core/gpslocation.h
+++ b/subsurface-core/gpslocation.h
@@ -9,6 +9,13 @@
#include <QSettings>
#include <QNetworkReply>
+struct gpsTracker {
+ degrees_t latitude;
+ degrees_t longitude;
+ time_t when;
+ QString name;
+};
+
class GpsLocation : QObject
{
Q_OBJECT
@@ -39,8 +46,10 @@ public slots:
void newPosition(QGeoPositionInfo pos);
void updateTimeout();
void uploadToServer();
+ void downloadFromServer();
void postError(QNetworkReply::NetworkError error);
void getUseridError(QNetworkReply::NetworkError error);
+ void updateModel();
void clearGpsData();
};
diff --git a/subsurface-mobile-helper.cpp b/subsurface-mobile-helper.cpp
index 9d9875ead..4956a112d 100644
--- a/subsurface-mobile-helper.cpp
+++ b/subsurface-mobile-helper.cpp
@@ -17,6 +17,7 @@
#include <QSortFilterProxyModel>
#include "qt-mobile/qmlmanager.h"
#include "qt-models/divelistmodel.h"
+#include "qt-models/gpslistmodel.h"
#include "qt-mobile/qmlprofile.h"
QObject *qqWindowObject = NULL;
@@ -51,8 +52,15 @@ void run_ui()
sortModel->setDynamicSortFilter(true);
sortModel->setSortRole(DiveListModel::DiveDateRole);
sortModel->sort(0, Qt::DescendingOrder);
+ GpsListModel gpsListModel;
+ QSortFilterProxyModel *gpsSortModel = new QSortFilterProxyModel(0);
+ gpsSortModel->setSourceModel(&gpsListModel);
+ gpsSortModel->setDynamicSortFilter(true);
+ gpsSortModel->setSortRole(GpsListModel::GpsDateRole);
+ gpsSortModel->sort(0, Qt::DescendingOrder);
QQmlContext *ctxt = engine.rootContext();
ctxt->setContextProperty("diveModel", sortModel);
+ ctxt->setContextProperty("gpsModel", gpsSortModel);
engine.load(QUrl(QStringLiteral("qrc:///qml/main.qml")));
qqWindowObject = engine.rootObjects().value(0);
if (!qqWindowObject) {