diff options
author | Dirk Hohndel <dirk@hohndel.org> | 2016-01-07 21:43:22 -0800 |
---|---|---|
committer | Dirk Hohndel <dirk@hohndel.org> | 2016-01-07 21:43:22 -0800 |
commit | 1eda61e1158de19a47acdfc5525f5f0df002052f (patch) | |
tree | fd981938b70fe20c20b3ea2ffc90abd9db74de9a | |
parent | ce3a78efcac2c9b6bd215bc191560a64c54fd628 (diff) | |
parent | b6ae6979e530fa32dbdb472a2e9698cb719945a8 (diff) | |
download | subsurface-1eda61e1158de19a47acdfc5525f5f0df002052f.tar.gz |
Merge branch 'gpsList'
-rw-r--r-- | CMakeLists.txt | 1 | ||||
-rw-r--r-- | qt-mobile/qml/GpsList.qml | 113 | ||||
-rw-r--r-- | qt-mobile/qml/main.qml | 19 | ||||
-rw-r--r-- | qt-mobile/qml/mobile-resources.qrc | 1 | ||||
-rw-r--r-- | qt-mobile/qmlmanager.cpp | 12 | ||||
-rw-r--r-- | qt-mobile/qmlmanager.h | 2 | ||||
-rw-r--r-- | qt-models/gpslistmodel.cpp | 96 | ||||
-rw-r--r-- | qt-models/gpslistmodel.h | 57 | ||||
-rwxr-xr-x | scripts/build.sh | 42 | ||||
-rw-r--r-- | subsurface-core/gpslocation.cpp | 116 | ||||
-rw-r--r-- | subsurface-core/gpslocation.h | 9 | ||||
-rw-r--r-- | subsurface-mobile-helper.cpp | 8 |
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 > = 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(>); + } + 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) { |