2015-06-04 10:29:50 +00:00
|
|
|
#include "qmlmanager.h"
|
2015-06-04 10:36:36 +00:00
|
|
|
#include <QUrl>
|
2015-07-10 07:47:26 +00:00
|
|
|
#include <QSettings>
|
2015-07-13 00:39:13 +00:00
|
|
|
#include <QDebug>
|
2015-12-05 03:34:59 +00:00
|
|
|
#include <QNetworkAccessManager>
|
|
|
|
#include <QAuthenticator>
|
2015-12-26 21:22:50 +00:00
|
|
|
#include <QDesktopServices>
|
2016-01-08 06:30:58 +00:00
|
|
|
#include <QTextDocument>
|
2016-03-11 02:36:46 +00:00
|
|
|
#include <QRegularExpression>
|
2016-04-03 23:13:22 +00:00
|
|
|
#include <QApplication>
|
|
|
|
#include <QElapsedTimer>
|
2015-06-04 10:29:50 +00:00
|
|
|
|
2015-06-11 06:56:18 +00:00
|
|
|
#include "qt-models/divelistmodel.h"
|
2016-04-05 05:02:03 +00:00
|
|
|
#include "qt-models/gpslistmodel.h"
|
|
|
|
#include "core/divelist.h"
|
|
|
|
#include "core/device.h"
|
|
|
|
#include "core/pref.h"
|
|
|
|
#include "core/qthelper.h"
|
|
|
|
#include "core/qt-gui.h"
|
|
|
|
#include "core/git-access.h"
|
|
|
|
#include "core/cloudstorage.h"
|
2015-07-13 00:39:13 +00:00
|
|
|
|
2015-12-03 23:59:40 +00:00
|
|
|
QMLManager *QMLManager::m_instance = NULL;
|
|
|
|
|
2016-03-11 05:59:16 +00:00
|
|
|
#define RED_FONT QLatin1Literal("<font color=\"red\">")
|
|
|
|
#define END_FONT QLatin1Literal("</font>")
|
|
|
|
|
2015-12-03 23:59:40 +00:00
|
|
|
static void appendTextToLogStandalone(const char *text)
|
2015-07-13 00:39:13 +00:00
|
|
|
{
|
2016-01-26 14:45:03 +00:00
|
|
|
QMLManager *self = QMLManager::instance();
|
|
|
|
if (self)
|
|
|
|
self->appendTextToLog(QString(text));
|
2015-07-13 00:39:13 +00:00
|
|
|
}
|
2015-06-09 19:20:44 +00:00
|
|
|
|
2016-04-06 05:54:54 +00:00
|
|
|
extern "C" int gitProgressCB(bool reset, const char *text)
|
2015-12-15 07:00:19 +00:00
|
|
|
{
|
2016-04-03 23:13:22 +00:00
|
|
|
static QElapsedTimer timer;
|
2016-04-06 05:54:54 +00:00
|
|
|
static qint64 lastTime;
|
|
|
|
static QString lastText;
|
2016-04-06 04:16:25 +00:00
|
|
|
static QMLManager *self;
|
2016-04-06 05:54:54 +00:00
|
|
|
static int lastPercent;
|
2016-04-06 04:16:25 +00:00
|
|
|
|
|
|
|
if (!self)
|
|
|
|
self = QMLManager::instance();
|
2015-12-15 07:00:19 +00:00
|
|
|
|
2016-04-06 05:54:54 +00:00
|
|
|
if (!timer.isValid() || reset) {
|
2016-04-03 23:13:22 +00:00
|
|
|
timer.restart();
|
|
|
|
lastTime = 0;
|
2016-04-06 05:54:54 +00:00
|
|
|
lastPercent = 0;
|
|
|
|
lastText.clear();
|
2016-04-03 23:13:22 +00:00
|
|
|
}
|
|
|
|
if (self) {
|
|
|
|
qint64 elapsed = timer.elapsed();
|
2016-04-04 20:37:56 +00:00
|
|
|
// don't show the same status twice in 200ms
|
2016-04-06 05:54:54 +00:00
|
|
|
if (lastText == text && elapsed - lastTime < 200)
|
2016-04-04 20:37:56 +00:00
|
|
|
return 0;
|
2016-04-06 05:54:54 +00:00
|
|
|
self->loadDiveProgress(++lastPercent);
|
2016-04-04 17:57:44 +00:00
|
|
|
QString logText = QString::number(elapsed / 1000.0, 'f', 1) + " / " + QString::number((elapsed - lastTime) / 1000.0, 'f', 3) +
|
2016-04-06 05:54:54 +00:00
|
|
|
QString(" : git %1 (%2)").arg(lastPercent).arg(text);
|
2016-04-04 17:57:44 +00:00
|
|
|
self->appendTextToLog(logText);
|
|
|
|
qDebug() << logText;
|
2016-04-06 04:20:33 +00:00
|
|
|
if (elapsed - lastTime > 500)
|
|
|
|
qApp->processEvents();
|
2016-04-03 23:13:22 +00:00
|
|
|
lastTime = elapsed;
|
2015-12-15 07:00:19 +00:00
|
|
|
}
|
|
|
|
// return 0 so that we don't end the download
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-01-11 14:14:45 +00:00
|
|
|
QMLManager::QMLManager() : m_locationServiceEnabled(false),
|
2016-01-07 00:56:08 +00:00
|
|
|
m_verboseEnabled(false),
|
2016-02-29 14:53:26 +00:00
|
|
|
reply(0),
|
|
|
|
deletedDive(0),
|
2016-03-09 03:29:56 +00:00
|
|
|
deletedTrip(0),
|
|
|
|
m_credentialStatus(UNKNOWN),
|
QML UI: don't immediately save data after we make changes
Much as this felt like the prudent thing to do, it makes the UI painful
to use. In bad network conditions, with a large dive log, on a phone,
the save operation can take more than a minute - which is just completely
ludicrous.
So instead we mark the dive list changed when we make changes and wait
for the app to not be in the foreground. Once the OS tells us that we are
hidden (on the desktop that generally means we don't have focus, on a
mobile device it usually does mean that the app is not on the screen), we
check if there are data to be saved and do so.
There is of course a major problem with this logic. If the user switches
away from Subsurface-mobile but comes back fairly quickly (just reacting
to a notification or briefly checking something, changing a song,
something... then the app may still be non-responsive for quite a while.
So we need to do something about the time it takes us to save the git
tree locally, and then figure out if we can move at least some of the
network traffic to another thread.
And we need to make sure the user immediately notices that the app is not
crashed but is actually saving their data. But that's for another commit.
tl;dr: CAREFUL, don't kill Subsurface-mobile before it had time to save
your data or your changes may be gone. In typical use that shouldn't be
an issue, but it is something that we need to tell the user about.
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2016-04-04 00:37:17 +00:00
|
|
|
m_lastDevicePixelRatio(1.0),
|
|
|
|
alreadySaving(false)
|
2015-06-04 10:29:50 +00:00
|
|
|
{
|
2015-12-03 23:59:40 +00:00
|
|
|
m_instance = this;
|
QML UI: don't immediately save data after we make changes
Much as this felt like the prudent thing to do, it makes the UI painful
to use. In bad network conditions, with a large dive log, on a phone,
the save operation can take more than a minute - which is just completely
ludicrous.
So instead we mark the dive list changed when we make changes and wait
for the app to not be in the foreground. Once the OS tells us that we are
hidden (on the desktop that generally means we don't have focus, on a
mobile device it usually does mean that the app is not on the screen), we
check if there are data to be saved and do so.
There is of course a major problem with this logic. If the user switches
away from Subsurface-mobile but comes back fairly quickly (just reacting
to a notification or briefly checking something, changing a song,
something... then the app may still be non-responsive for quite a while.
So we need to do something about the time it takes us to save the git
tree locally, and then figure out if we can move at least some of the
network traffic to another thread.
And we need to make sure the user immediately notices that the app is not
crashed but is actually saving their data. But that's for another commit.
tl;dr: CAREFUL, don't kill Subsurface-mobile before it had time to save
your data or your changes may be gone. In typical use that shouldn't be
an issue, but it is something that we need to tell the user about.
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2016-04-04 00:37:17 +00:00
|
|
|
connect(qobject_cast<QApplication *>(QApplication::instance()), &QApplication::applicationStateChanged, this, &QMLManager::applicationStateChanged);
|
2015-12-20 16:02:05 +00:00
|
|
|
appendTextToLog(getUserAgent());
|
2016-01-26 15:02:42 +00:00
|
|
|
appendTextToLog(QStringLiteral("build with Qt Version %1, runtime from Qt Version %2").arg(QT_VERSION_STR).arg(qVersion()));
|
2015-12-20 16:02:05 +00:00
|
|
|
qDebug() << "Starting" << getUserAgent();
|
2016-01-26 15:02:42 +00:00
|
|
|
qDebug() << QStringLiteral("build with Qt Version %1, runtime from Qt Version %2").arg(QT_VERSION_STR).arg(qVersion());
|
2016-02-11 01:31:52 +00:00
|
|
|
setStartPageText(tr("Starting..."));
|
2016-04-04 00:00:49 +00:00
|
|
|
setAccessingCloud(-1);
|
2015-11-11 20:32:54 +00:00
|
|
|
// create location manager service
|
2015-12-03 23:59:40 +00:00
|
|
|
locationProvider = new GpsLocation(&appendTextToLogStandalone, this);
|
2015-12-15 07:00:19 +00:00
|
|
|
set_git_update_cb(&gitProgressCB);
|
2016-02-08 19:08:49 +00:00
|
|
|
|
|
|
|
// make sure we know if the current cloud repo has been successfully synced
|
|
|
|
syncLoadFromCloud();
|
2015-12-05 03:34:59 +00:00
|
|
|
}
|
2015-11-11 20:32:54 +00:00
|
|
|
|
QML UI: don't immediately save data after we make changes
Much as this felt like the prudent thing to do, it makes the UI painful
to use. In bad network conditions, with a large dive log, on a phone,
the save operation can take more than a minute - which is just completely
ludicrous.
So instead we mark the dive list changed when we make changes and wait
for the app to not be in the foreground. Once the OS tells us that we are
hidden (on the desktop that generally means we don't have focus, on a
mobile device it usually does mean that the app is not on the screen), we
check if there are data to be saved and do so.
There is of course a major problem with this logic. If the user switches
away from Subsurface-mobile but comes back fairly quickly (just reacting
to a notification or briefly checking something, changing a song,
something... then the app may still be non-responsive for quite a while.
So we need to do something about the time it takes us to save the git
tree locally, and then figure out if we can move at least some of the
network traffic to another thread.
And we need to make sure the user immediately notices that the app is not
crashed but is actually saving their data. But that's for another commit.
tl;dr: CAREFUL, don't kill Subsurface-mobile before it had time to save
your data or your changes may be gone. In typical use that shouldn't be
an issue, but it is something that we need to tell the user about.
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2016-04-04 00:37:17 +00:00
|
|
|
void QMLManager::applicationStateChanged(Qt::ApplicationState state)
|
|
|
|
{
|
|
|
|
if (!timer.isValid())
|
|
|
|
timer.start();
|
|
|
|
QString stateText;
|
|
|
|
switch (state) {
|
|
|
|
case Qt::ApplicationActive: stateText = "active"; break;
|
|
|
|
case Qt::ApplicationHidden: stateText = "hidden"; break;
|
|
|
|
case Qt::ApplicationSuspended: stateText = "suspended"; break;
|
|
|
|
case Qt::ApplicationInactive: stateText = "inactive"; break;
|
|
|
|
default: stateText = QString("none of the four: 0x") + QString::number(state, 16);
|
|
|
|
}
|
|
|
|
stateText.prepend(QString::number(timer.elapsed() / 1000.0,'f', 3) + ": AppState changed to ");
|
|
|
|
stateText.append(" with ");
|
|
|
|
stateText.append((alreadySaving ? QLatin1Literal("") : QLatin1Literal("no ")) + QLatin1Literal("save ongoing"));
|
2016-04-04 17:57:44 +00:00
|
|
|
stateText.append(" and ");
|
|
|
|
stateText.append((unsaved_changes() ? QLatin1Literal("") : QLatin1Literal("no ")) + QLatin1Literal("unsaved changes"));
|
QML UI: don't immediately save data after we make changes
Much as this felt like the prudent thing to do, it makes the UI painful
to use. In bad network conditions, with a large dive log, on a phone,
the save operation can take more than a minute - which is just completely
ludicrous.
So instead we mark the dive list changed when we make changes and wait
for the app to not be in the foreground. Once the OS tells us that we are
hidden (on the desktop that generally means we don't have focus, on a
mobile device it usually does mean that the app is not on the screen), we
check if there are data to be saved and do so.
There is of course a major problem with this logic. If the user switches
away from Subsurface-mobile but comes back fairly quickly (just reacting
to a notification or briefly checking something, changing a song,
something... then the app may still be non-responsive for quite a while.
So we need to do something about the time it takes us to save the git
tree locally, and then figure out if we can move at least some of the
network traffic to another thread.
And we need to make sure the user immediately notices that the app is not
crashed but is actually saving their data. But that's for another commit.
tl;dr: CAREFUL, don't kill Subsurface-mobile before it had time to save
your data or your changes may be gone. In typical use that shouldn't be
an issue, but it is something that we need to tell the user about.
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2016-04-04 00:37:17 +00:00
|
|
|
appendTextToLog(stateText);
|
|
|
|
qDebug() << stateText;
|
|
|
|
|
|
|
|
if (!alreadySaving && state == Qt::ApplicationInactive && unsaved_changes()) {
|
|
|
|
// FIXME
|
|
|
|
// make sure the user sees that we are saving data if they come back
|
|
|
|
// while this is running
|
2016-04-08 19:35:45 +00:00
|
|
|
saveChangesCloud(false);
|
QML UI: don't immediately save data after we make changes
Much as this felt like the prudent thing to do, it makes the UI painful
to use. In bad network conditions, with a large dive log, on a phone,
the save operation can take more than a minute - which is just completely
ludicrous.
So instead we mark the dive list changed when we make changes and wait
for the app to not be in the foreground. Once the OS tells us that we are
hidden (on the desktop that generally means we don't have focus, on a
mobile device it usually does mean that the app is not on the screen), we
check if there are data to be saved and do so.
There is of course a major problem with this logic. If the user switches
away from Subsurface-mobile but comes back fairly quickly (just reacting
to a notification or briefly checking something, changing a song,
something... then the app may still be non-responsive for quite a while.
So we need to do something about the time it takes us to save the git
tree locally, and then figure out if we can move at least some of the
network traffic to another thread.
And we need to make sure the user immediately notices that the app is not
crashed but is actually saving their data. But that's for another commit.
tl;dr: CAREFUL, don't kill Subsurface-mobile before it had time to save
your data or your changes may be gone. In typical use that shouldn't be
an issue, but it is something that we need to tell the user about.
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2016-04-04 00:37:17 +00:00
|
|
|
appendTextToLog(QString::number(timer.elapsed() / 1000.0,'f', 3) + ": done saving to git local / remote");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-09 15:53:22 +00:00
|
|
|
void QMLManager::openLocalThenRemote(QString url)
|
|
|
|
{
|
|
|
|
clear_dive_file_data();
|
|
|
|
QByteArray fileNamePrt = QFile::encodeName(url);
|
2016-04-04 01:33:40 +00:00
|
|
|
bool glo = prefs.git_local_only;
|
2016-02-09 15:53:22 +00:00
|
|
|
prefs.git_local_only = true;
|
|
|
|
int error = parse_file(fileNamePrt.data());
|
2016-04-04 01:00:00 +00:00
|
|
|
setAccessingCloud(-1);
|
2016-04-04 01:33:40 +00:00
|
|
|
prefs.git_local_only = glo;
|
2016-02-09 15:53:22 +00:00
|
|
|
if (error) {
|
2016-03-13 15:58:57 +00:00
|
|
|
appendTextToLog(QStringLiteral("loading dives from cache failed %1").arg(error));
|
2016-02-09 15:53:22 +00:00
|
|
|
} else {
|
2016-02-11 01:45:23 +00:00
|
|
|
// if we can load from the cache, we know that we have at least a valid email
|
|
|
|
if (credentialStatus() == UNKNOWN)
|
|
|
|
setCredentialStatus(VALID_EMAIL);
|
2016-02-09 15:53:22 +00:00
|
|
|
prefs.unit_system = informational_prefs.unit_system;
|
|
|
|
if (informational_prefs.unit_system == IMPERIAL)
|
|
|
|
informational_prefs.units = IMPERIAL_units;
|
|
|
|
prefs.units = informational_prefs.units;
|
|
|
|
process_dives(false, false);
|
|
|
|
DiveListModel::instance()->clear();
|
2016-04-06 04:17:37 +00:00
|
|
|
DiveListModel::instance()->addAllDives();
|
|
|
|
appendTextToLog(QStringLiteral("%1 dives loaded from cache").arg(dive_table.nr));
|
2016-02-09 15:53:22 +00:00
|
|
|
}
|
2016-04-06 18:44:58 +00:00
|
|
|
set_filename(fileNamePrt.data(), true);
|
2016-04-08 19:35:45 +00:00
|
|
|
if (prefs.git_local_only) {
|
|
|
|
appendTextToLog(QStringLiteral("have cloud credentials, but user asked not to connect to network"));
|
|
|
|
} else {
|
|
|
|
appendTextToLog(QStringLiteral("have cloud credentials, trying to connect"));
|
|
|
|
tryRetrieveDataFromBackend();
|
|
|
|
}
|
2016-02-09 15:53:22 +00:00
|
|
|
}
|
|
|
|
|
2015-12-05 03:34:59 +00:00
|
|
|
void QMLManager::finishSetup()
|
|
|
|
{
|
2015-11-11 20:32:54 +00:00
|
|
|
// Initialize cloud credentials.
|
2015-07-10 07:47:26 +00:00
|
|
|
setCloudUserName(prefs.cloud_storage_email);
|
|
|
|
setCloudPassword(prefs.cloud_storage_password);
|
2016-04-08 19:35:45 +00:00
|
|
|
setSyncToCloud(!prefs.git_local_only);
|
2015-11-18 21:14:19 +00:00
|
|
|
// if the cloud credentials are valid, we should get the GPS Webservice ID as well
|
2015-12-27 16:32:15 +00:00
|
|
|
QString url;
|
2016-02-11 01:31:52 +00:00
|
|
|
if (!cloudUserName().isEmpty() &&
|
|
|
|
!cloudPassword().isEmpty() &&
|
2015-12-27 16:32:15 +00:00
|
|
|
getCloudURL(url) == 0) {
|
2016-04-06 18:47:12 +00:00
|
|
|
// we know that we are the first ones to access git storage, so we don't need to test,
|
|
|
|
// but we need to make sure we stay the only ones accessing git storage
|
|
|
|
alreadySaving = true;
|
2016-02-09 15:53:22 +00:00
|
|
|
openLocalThenRemote(url);
|
2015-12-20 00:08:10 +00:00
|
|
|
} else {
|
2016-02-11 01:45:23 +00:00
|
|
|
setCredentialStatus(INCOMPLETE);
|
2016-03-13 15:58:57 +00:00
|
|
|
appendTextToLog(QStringLiteral("no cloud credentials"));
|
2016-03-11 05:59:16 +00:00
|
|
|
setStartPageText(RED_FONT + tr("Please enter valid cloud credentials.") + END_FONT);
|
2015-12-20 00:08:10 +00:00
|
|
|
}
|
2015-11-14 17:10:06 +00:00
|
|
|
setDistanceThreshold(prefs.distance_threshold);
|
|
|
|
setTimeThreshold(prefs.time_threshold / 60);
|
2015-06-04 10:29:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
QMLManager::~QMLManager()
|
|
|
|
{
|
2015-12-03 23:59:40 +00:00
|
|
|
m_instance = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
QMLManager *QMLManager::instance()
|
|
|
|
{
|
|
|
|
return m_instance;
|
2015-06-04 10:29:50 +00:00
|
|
|
}
|
|
|
|
|
2015-07-10 07:47:26 +00:00
|
|
|
void QMLManager::savePreferences()
|
|
|
|
{
|
|
|
|
QSettings s;
|
2015-11-14 17:10:06 +00:00
|
|
|
s.beginGroup("LocationService");
|
|
|
|
s.setValue("time_threshold", timeThreshold() * 60);
|
|
|
|
prefs.time_threshold = timeThreshold() * 60;
|
|
|
|
s.setValue("distance_threshold", distanceThreshold());
|
|
|
|
prefs.distance_threshold = distanceThreshold();
|
2015-12-05 03:34:59 +00:00
|
|
|
s.sync();
|
2015-12-03 22:30:30 +00:00
|
|
|
}
|
|
|
|
|
2015-12-05 03:34:59 +00:00
|
|
|
#define CLOUDURL QString(prefs.cloud_base_url)
|
|
|
|
#define CLOUDREDIRECTURL CLOUDURL + "/cgi-bin/redirect.pl"
|
|
|
|
|
2015-12-03 22:30:30 +00:00
|
|
|
void QMLManager::saveCloudCredentials()
|
|
|
|
{
|
|
|
|
QSettings s;
|
|
|
|
bool cloudCredentialsChanged = false;
|
2015-07-10 07:47:26 +00:00
|
|
|
s.beginGroup("CloudStorage");
|
|
|
|
s.setValue("email", cloudUserName());
|
2016-03-25 04:42:20 +00:00
|
|
|
s.setValue("password", cloudPassword());
|
2015-07-10 07:47:26 +00:00
|
|
|
s.sync();
|
2015-07-13 00:39:13 +00:00
|
|
|
if (!same_string(prefs.cloud_storage_email, qPrintable(cloudUserName()))) {
|
|
|
|
free(prefs.cloud_storage_email);
|
|
|
|
prefs.cloud_storage_email = strdup(qPrintable(cloudUserName()));
|
2015-11-18 21:14:19 +00:00
|
|
|
cloudCredentialsChanged = true;
|
2015-07-13 00:39:13 +00:00
|
|
|
}
|
2015-11-18 21:14:19 +00:00
|
|
|
|
|
|
|
cloudCredentialsChanged |= !same_string(prefs.cloud_storage_password, qPrintable(cloudPassword()));
|
|
|
|
|
2016-04-06 18:42:38 +00:00
|
|
|
if (!cloudCredentialsChanged) {
|
|
|
|
// just go back to the dive list
|
|
|
|
setCredentialStatus(oldStatus());
|
|
|
|
}
|
2016-03-25 04:42:20 +00:00
|
|
|
if (!same_string(prefs.cloud_storage_password, qPrintable(cloudPassword()))) {
|
|
|
|
free(prefs.cloud_storage_password);
|
|
|
|
prefs.cloud_storage_password = strdup(qPrintable(cloudPassword()));
|
2015-07-13 00:39:13 +00:00
|
|
|
}
|
2016-02-13 06:06:38 +00:00
|
|
|
if (cloudUserName().isEmpty() || cloudPassword().isEmpty()) {
|
2016-03-11 05:59:16 +00:00
|
|
|
setStartPageText(RED_FONT + tr("Please enter valid cloud credentials.") + END_FONT);
|
2016-02-13 06:06:38 +00:00
|
|
|
} else if (cloudCredentialsChanged) {
|
2015-12-05 03:34:59 +00:00
|
|
|
free(prefs.userid);
|
|
|
|
prefs.userid = NULL;
|
2016-02-08 19:14:11 +00:00
|
|
|
syncLoadFromCloud();
|
2016-02-09 15:53:22 +00:00
|
|
|
QString url;
|
|
|
|
getCloudURL(url);
|
2016-02-11 02:14:09 +00:00
|
|
|
manager()->clearAccessCache(); // remove any chached credentials
|
2016-02-11 05:24:10 +00:00
|
|
|
clear_git_id(); // invalidate our remembered GIT SHA
|
2016-02-12 06:46:35 +00:00
|
|
|
clear_dive_file_data();
|
|
|
|
DiveListModel::instance()->clear();
|
|
|
|
GpsListModel::instance()->clear();
|
2016-02-12 14:54:52 +00:00
|
|
|
setStartPageText(tr("Attempting to open cloud storage with new credentials"));
|
2016-04-06 18:47:12 +00:00
|
|
|
// we therefore know that no one else is already accessing THIS git repo;
|
|
|
|
// let's make sure we stay the only ones doing so
|
|
|
|
alreadySaving = true;
|
2016-02-09 15:53:22 +00:00
|
|
|
openLocalThenRemote(url);
|
2015-12-05 03:34:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void QMLManager::checkCredentialsAndExecute(execute_function_type execute)
|
|
|
|
{
|
|
|
|
// if the cloud credentials are present, we should try to get the GPS Webservice ID
|
|
|
|
// and (if we haven't done so) load the dive list
|
2015-11-18 21:14:19 +00:00
|
|
|
if (!same_string(prefs.cloud_storage_email, "") &&
|
|
|
|
!same_string(prefs.cloud_storage_password, "")) {
|
2016-04-04 01:00:00 +00:00
|
|
|
setAccessingCloud(0);
|
2016-01-02 08:04:59 +00:00
|
|
|
setStartPageText(tr("Testing cloud credentials"));
|
2015-12-05 03:34:59 +00:00
|
|
|
appendTextToLog("Have credentials, let's see if they are valid");
|
2016-04-08 19:28:05 +00:00
|
|
|
CloudStorageAuthenticate *csa = new CloudStorageAuthenticate(this);
|
|
|
|
csa->backend(prefs.cloud_storage_email, prefs.cloud_storage_password);
|
2016-01-26 14:45:03 +00:00
|
|
|
connect(manager(), &QNetworkAccessManager::authenticationRequired, this, &QMLManager::provideAuth, Qt::UniqueConnection);
|
|
|
|
connect(manager(), &QNetworkAccessManager::finished, this, execute, Qt::UniqueConnection);
|
2015-12-05 03:34:59 +00:00
|
|
|
QUrl url(CLOUDREDIRECTURL);
|
|
|
|
request = QNetworkRequest(url);
|
|
|
|
request.setRawHeader("User-Agent", getUserAgent().toUtf8());
|
|
|
|
request.setRawHeader("Accept", "text/html");
|
2016-01-26 14:45:03 +00:00
|
|
|
reply = manager()->get(request);
|
2015-12-05 03:34:59 +00:00
|
|
|
connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(handleError(QNetworkReply::NetworkError)));
|
|
|
|
connect(reply, &QNetworkReply::sslErrors, this, &QMLManager::handleSslErrors);
|
2015-11-18 21:14:19 +00:00
|
|
|
}
|
2015-12-05 03:34:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QMLManager::tryRetrieveDataFromBackend()
|
|
|
|
{
|
|
|
|
checkCredentialsAndExecute(&QMLManager::retrieveUserid);
|
2015-07-10 07:47:26 +00:00
|
|
|
}
|
2015-07-10 08:31:24 +00:00
|
|
|
|
2015-12-05 03:34:59 +00:00
|
|
|
void QMLManager::provideAuth(QNetworkReply *reply, QAuthenticator *auth)
|
|
|
|
{
|
|
|
|
if (auth->user() == QString(prefs.cloud_storage_email) &&
|
|
|
|
auth->password() == QString(prefs.cloud_storage_password)) {
|
|
|
|
// OK, credentials have been tried and didn't work, so they are invalid
|
|
|
|
appendTextToLog("Cloud credentials are invalid");
|
2016-03-11 05:59:16 +00:00
|
|
|
setStartPageText(RED_FONT + tr("Cloud credentials are invalid") + END_FONT);
|
2016-02-11 01:45:23 +00:00
|
|
|
setCredentialStatus(INVALID);
|
2015-12-05 03:34:59 +00:00
|
|
|
reply->disconnect();
|
|
|
|
reply->abort();
|
|
|
|
reply->deleteLater();
|
2015-11-30 18:15:04 +00:00
|
|
|
return;
|
|
|
|
}
|
2015-12-05 03:34:59 +00:00
|
|
|
auth->setUser(prefs.cloud_storage_email);
|
|
|
|
auth->setPassword(prefs.cloud_storage_password);
|
|
|
|
}
|
|
|
|
|
|
|
|
void QMLManager::handleSslErrors(const QList<QSslError> &errors)
|
|
|
|
{
|
2016-03-11 05:59:16 +00:00
|
|
|
setStartPageText(RED_FONT + tr("Cannot open cloud storage: Error creating https connection") + END_FONT);
|
2016-01-11 14:14:45 +00:00
|
|
|
Q_FOREACH (QSslError e, errors) {
|
2015-12-05 03:34:59 +00:00
|
|
|
qDebug() << e.errorString();
|
|
|
|
}
|
|
|
|
reply->abort();
|
|
|
|
reply->deleteLater();
|
2016-04-04 00:00:49 +00:00
|
|
|
setAccessingCloud(-1);
|
2015-12-05 03:34:59 +00:00
|
|
|
}
|
2015-11-30 18:15:04 +00:00
|
|
|
|
2015-12-05 03:34:59 +00:00
|
|
|
void QMLManager::handleError(QNetworkReply::NetworkError nError)
|
|
|
|
{
|
2015-12-15 07:00:19 +00:00
|
|
|
QString errorString = reply->errorString();
|
|
|
|
qDebug() << "handleError" << nError << errorString;
|
2016-03-11 05:59:16 +00:00
|
|
|
setStartPageText(RED_FONT + tr("Cannot open cloud storage: %1").arg(errorString) + END_FONT);
|
2015-12-05 03:34:59 +00:00
|
|
|
reply->abort();
|
|
|
|
reply->deleteLater();
|
2016-04-04 00:00:49 +00:00
|
|
|
setAccessingCloud(-1);
|
2015-12-05 03:34:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QMLManager::retrieveUserid()
|
|
|
|
{
|
|
|
|
if (reply->attribute(QNetworkRequest::HttpStatusCodeAttribute) != 302) {
|
2016-03-13 15:58:57 +00:00
|
|
|
appendTextToLog(QStringLiteral("Cloud storage connection not working correctly: %1").arg(QString(reply->readAll())));
|
2016-04-06 04:27:05 +00:00
|
|
|
setStartPageText(RED_FONT + tr("Cannot connect to cloud storage") + END_FONT);
|
2016-04-04 00:00:49 +00:00
|
|
|
setAccessingCloud(-1);
|
2016-04-06 18:47:12 +00:00
|
|
|
alreadySaving = false;
|
2015-12-05 03:34:59 +00:00
|
|
|
return;
|
|
|
|
}
|
2016-02-11 01:45:23 +00:00
|
|
|
setCredentialStatus(VALID);
|
2015-12-05 03:34:59 +00:00
|
|
|
QString userid(prefs.userid);
|
2015-12-20 00:20:20 +00:00
|
|
|
if (userid.isEmpty()) {
|
|
|
|
if (same_string(prefs.cloud_storage_email, "") || same_string(prefs.cloud_storage_password, "")) {
|
2016-03-13 15:58:57 +00:00
|
|
|
appendTextToLog("cloud user name or password are empty, can't retrieve web user id");
|
2016-04-04 00:00:49 +00:00
|
|
|
setAccessingCloud(-1);
|
2016-04-06 18:47:12 +00:00
|
|
|
alreadySaving = false;
|
2015-12-20 00:20:20 +00:00
|
|
|
return;
|
|
|
|
}
|
2016-01-26 15:02:42 +00:00
|
|
|
appendTextToLog(QStringLiteral("calling getUserid with user %1").arg(prefs.cloud_storage_email));
|
2015-12-05 03:34:59 +00:00
|
|
|
userid = locationProvider->getUserid(prefs.cloud_storage_email, prefs.cloud_storage_password);
|
2015-12-20 00:20:20 +00:00
|
|
|
}
|
2015-12-05 03:34:59 +00:00
|
|
|
if (!userid.isEmpty()) {
|
|
|
|
// overwrite the existing userid
|
|
|
|
free(prefs.userid);
|
|
|
|
prefs.userid = strdup(qPrintable(userid));
|
|
|
|
QSettings s;
|
|
|
|
s.setValue("subsurface_webservice_uid", prefs.userid);
|
|
|
|
s.sync();
|
|
|
|
}
|
2016-04-06 04:27:05 +00:00
|
|
|
setCredentialStatus(VALID);
|
|
|
|
setStartPageText("Cloud credentials valid, loading dives...");
|
2016-04-06 05:54:54 +00:00
|
|
|
git_storage_update_progress(true, "load dives with valid credentials");
|
2016-04-06 18:47:12 +00:00
|
|
|
// this only gets called with "alreadySaving" already locked
|
2016-02-08 19:12:43 +00:00
|
|
|
loadDivesWithValidCredentials();
|
2015-12-05 03:34:59 +00:00
|
|
|
}
|
|
|
|
|
2015-12-15 07:00:19 +00:00
|
|
|
void QMLManager::loadDiveProgress(int percent)
|
|
|
|
{
|
|
|
|
QString text(tr("Loading dive list from cloud storage."));
|
2016-04-04 00:00:49 +00:00
|
|
|
setAccessingCloud(percent);
|
2016-01-11 14:14:45 +00:00
|
|
|
while (percent > 0) {
|
2015-12-15 07:00:19 +00:00
|
|
|
text.append(".");
|
|
|
|
percent -= 10;
|
|
|
|
}
|
|
|
|
setStartPageText(text);
|
|
|
|
}
|
|
|
|
|
2015-12-05 03:34:59 +00:00
|
|
|
void QMLManager::loadDivesWithValidCredentials()
|
|
|
|
{
|
2015-07-10 08:31:24 +00:00
|
|
|
QString url;
|
|
|
|
if (getCloudURL(url)) {
|
2015-12-15 07:00:19 +00:00
|
|
|
QString errorString(get_error_string());
|
|
|
|
appendTextToLog(errorString);
|
2016-03-11 05:59:16 +00:00
|
|
|
setStartPageText(RED_FONT + tr("Cloud storage error: %1").arg(errorString) + END_FONT);
|
2016-04-04 00:00:49 +00:00
|
|
|
setAccessingCloud(-1);
|
2016-04-06 18:47:12 +00:00
|
|
|
alreadySaving = false;
|
2015-07-10 08:31:24 +00:00
|
|
|
return;
|
|
|
|
}
|
2016-01-11 14:14:45 +00:00
|
|
|
QByteArray fileNamePrt = QFile::encodeName(url);
|
2016-04-06 05:51:09 +00:00
|
|
|
git_repository *git;
|
|
|
|
const char *branch;
|
|
|
|
int error;
|
|
|
|
if (check_git_sha(fileNamePrt.data(), &git, &branch) == 0) {
|
2015-12-27 18:05:19 +00:00
|
|
|
qDebug() << "local cache was current, no need to modify dive list";
|
|
|
|
appendTextToLog("Cloud sync shows local cache was current");
|
2016-01-07 19:49:29 +00:00
|
|
|
setLoadFromCloud(true);
|
2016-04-04 00:00:49 +00:00
|
|
|
setAccessingCloud(-1);
|
2016-04-06 18:47:12 +00:00
|
|
|
alreadySaving = false;
|
2015-12-27 18:05:19 +00:00
|
|
|
return;
|
|
|
|
}
|
2016-04-06 04:27:05 +00:00
|
|
|
appendTextToLog("Cloud sync brought newer data, reloading the dive list");
|
2016-04-06 05:51:09 +00:00
|
|
|
clear_dive_file_data();
|
|
|
|
if (git != dummy_git_repository) {
|
|
|
|
appendTextToLog(QString("have repository and branch %1").arg(branch));
|
|
|
|
error = git_load_dives(git, branch);
|
|
|
|
} else {
|
|
|
|
appendTextToLog(QString("didn't receive valid git repo, try again"));
|
|
|
|
error = parse_file(fileNamePrt.data());
|
|
|
|
}
|
2016-04-04 00:00:49 +00:00
|
|
|
setAccessingCloud(-1);
|
2015-07-10 08:31:24 +00:00
|
|
|
if (!error) {
|
2015-07-13 00:39:13 +00:00
|
|
|
report_error("filename is now %s", fileNamePrt.data());
|
2015-12-01 17:37:47 +00:00
|
|
|
const char *error_string = get_error_string();
|
|
|
|
appendTextToLog(error_string);
|
2015-07-10 08:31:24 +00:00
|
|
|
set_filename(fileNamePrt.data(), true);
|
2015-07-13 00:39:13 +00:00
|
|
|
} else {
|
2015-12-01 17:37:47 +00:00
|
|
|
report_error("failed to open file %s", fileNamePrt.data());
|
2015-12-15 07:00:19 +00:00
|
|
|
QString errorString(get_error_string());
|
|
|
|
appendTextToLog(errorString);
|
2016-03-11 05:59:16 +00:00
|
|
|
setStartPageText(RED_FONT + tr("Cloud storage error: %1").arg(errorString) + END_FONT);
|
2016-04-06 18:47:12 +00:00
|
|
|
alreadySaving = false;
|
2015-12-03 01:50:47 +00:00
|
|
|
return;
|
2015-07-10 08:31:24 +00:00
|
|
|
}
|
2016-01-07 05:58:02 +00:00
|
|
|
prefs.unit_system = informational_prefs.unit_system;
|
2016-01-20 16:45:43 +00:00
|
|
|
if (informational_prefs.unit_system == IMPERIAL)
|
|
|
|
informational_prefs.units = IMPERIAL_units;
|
2016-01-07 05:58:02 +00:00
|
|
|
prefs.units = informational_prefs.units;
|
2016-04-06 04:17:37 +00:00
|
|
|
DiveListModel::instance()->clear();
|
2015-07-10 08:31:24 +00:00
|
|
|
process_dives(false, false);
|
2016-04-06 04:17:37 +00:00
|
|
|
DiveListModel::instance()->addAllDives();
|
|
|
|
appendTextToLog(QStringLiteral("%1 dives loaded").arg(dive_table.nr));
|
2015-12-15 07:00:19 +00:00
|
|
|
if (dive_table.nr == 0)
|
|
|
|
setStartPageText(tr("Cloud storage open successfully. No dives in dive list."));
|
2015-12-03 02:49:02 +00:00
|
|
|
setLoadFromCloud(true);
|
2016-04-06 18:47:12 +00:00
|
|
|
alreadySaving = false;
|
2015-07-17 15:28:01 +00:00
|
|
|
}
|
|
|
|
|
2016-01-11 03:34:21 +00:00
|
|
|
void QMLManager::refreshDiveList()
|
|
|
|
{
|
|
|
|
DiveListModel::instance()->clear();
|
2016-04-06 04:17:37 +00:00
|
|
|
DiveListModel::instance()->addAllDives();
|
2016-01-11 03:34:21 +00:00
|
|
|
}
|
|
|
|
|
2016-01-08 06:30:58 +00:00
|
|
|
// update the dive and return the notes field, stripped of the HTML junk
|
2016-02-28 14:43:54 +00:00
|
|
|
void QMLManager::commitChanges(QString diveId, QString date, QString location, QString gps, QString duration, QString depth,
|
|
|
|
QString airtemp, QString watertemp, QString suit, QString buddy, QString diveMaster, QString weight, QString notes,
|
|
|
|
QString startpressure, QString endpressure, QString gasmix)
|
2015-07-17 15:28:01 +00:00
|
|
|
{
|
2016-01-28 22:00:52 +00:00
|
|
|
#define DROP_EMPTY_PLACEHOLDER(_s) if ((_s) == QLatin1Literal("--")) (_s).clear()
|
|
|
|
|
|
|
|
DROP_EMPTY_PLACEHOLDER(location);
|
|
|
|
DROP_EMPTY_PLACEHOLDER(duration);
|
|
|
|
DROP_EMPTY_PLACEHOLDER(depth);
|
|
|
|
DROP_EMPTY_PLACEHOLDER(airtemp);
|
|
|
|
DROP_EMPTY_PLACEHOLDER(watertemp);
|
|
|
|
DROP_EMPTY_PLACEHOLDER(suit);
|
|
|
|
DROP_EMPTY_PLACEHOLDER(buddy);
|
|
|
|
DROP_EMPTY_PLACEHOLDER(diveMaster);
|
2016-02-06 06:54:47 +00:00
|
|
|
DROP_EMPTY_PLACEHOLDER(weight);
|
2016-02-13 17:34:31 +00:00
|
|
|
DROP_EMPTY_PLACEHOLDER(gasmix);
|
2016-02-09 16:20:17 +00:00
|
|
|
DROP_EMPTY_PLACEHOLDER(startpressure);
|
|
|
|
DROP_EMPTY_PLACEHOLDER(endpressure);
|
2016-01-28 22:00:52 +00:00
|
|
|
DROP_EMPTY_PLACEHOLDER(notes);
|
|
|
|
|
|
|
|
#undef DROP_EMPTY_PLACEHOLDER
|
|
|
|
|
2015-07-17 15:28:01 +00:00
|
|
|
struct dive *d = get_dive_by_uniq_id(diveId.toInt());
|
2016-01-08 06:30:58 +00:00
|
|
|
// notes comes back as rich text - let's convert this into plain text
|
|
|
|
QTextDocument doc;
|
|
|
|
doc.setHtml(notes);
|
|
|
|
notes = doc.toPlainText();
|
2015-12-27 05:24:29 +00:00
|
|
|
|
|
|
|
if (!d) {
|
|
|
|
qDebug() << "don't touch this... no dive";
|
2016-02-28 14:43:54 +00:00
|
|
|
return;
|
2015-12-27 05:24:29 +00:00
|
|
|
}
|
2015-07-17 15:28:01 +00:00
|
|
|
bool diveChanged = false;
|
2016-01-06 06:57:40 +00:00
|
|
|
bool needResort = false;
|
2015-07-17 15:28:01 +00:00
|
|
|
|
2016-04-03 22:31:59 +00:00
|
|
|
invalidate_dive_cache(d);
|
2016-01-06 06:53:32 +00:00
|
|
|
if (date != get_dive_date_string(d->when)) {
|
2016-01-27 20:11:37 +00:00
|
|
|
diveChanged = needResort = true;
|
2016-01-06 06:53:32 +00:00
|
|
|
QDateTime newDate;
|
|
|
|
// what a pain - Qt will not parse dates if the day of the week is incorrect
|
|
|
|
// so if the user changed the date but didn't update the day of the week (most likely behavior, actually),
|
|
|
|
// we need to make sure we don't try to parse that
|
2016-01-26 15:02:42 +00:00
|
|
|
QString format(QString(prefs.date_format) + QChar(' ') + prefs.time_format);
|
|
|
|
if (format.contains(QLatin1String("ddd")) || format.contains(QLatin1String("dddd"))) {
|
|
|
|
QString dateFormatToDrop = format.contains(QLatin1String("ddd")) ? QStringLiteral("ddd") : QStringLiteral("dddd");
|
2016-01-06 06:53:32 +00:00
|
|
|
QDateTime ts;
|
|
|
|
QLocale loc = getLocale();
|
|
|
|
ts.setMSecsSinceEpoch(d->when * 1000L);
|
|
|
|
QString drop = loc.toString(ts.toUTC(), dateFormatToDrop);
|
|
|
|
format.replace(dateFormatToDrop, "");
|
|
|
|
date.replace(drop, "");
|
|
|
|
}
|
|
|
|
newDate = QDateTime::fromString(date, format);
|
2016-03-11 02:36:46 +00:00
|
|
|
if (!newDate.isValid()) {
|
|
|
|
qDebug() << "unable to parse date" << date << "with the given format" << format;
|
|
|
|
QRegularExpression isoDate("\\d+-\\d+-\\d+[^\\d]+\\d+:\\d+");
|
|
|
|
if (date.contains(isoDate)) {
|
|
|
|
newDate = QDateTime::fromString(date, "yyyy-M-d h:m:s");
|
|
|
|
if (newDate.isValid())
|
|
|
|
goto parsed;
|
|
|
|
newDate = QDateTime::fromString(date, "yy-M-d h:m:s");
|
|
|
|
if (newDate.isValid())
|
|
|
|
goto parsed;
|
|
|
|
}
|
|
|
|
QRegularExpression isoDateNoSecs("\\d+-\\d+-\\d+[^\\d]+\\d+");
|
|
|
|
if (date.contains(isoDateNoSecs)) {
|
|
|
|
newDate = QDateTime::fromString(date, "yyyy-M-d h:m");
|
|
|
|
if (newDate.isValid())
|
|
|
|
goto parsed;
|
|
|
|
newDate = QDateTime::fromString(date, "yy-M-d h:m");
|
|
|
|
if (newDate.isValid())
|
|
|
|
goto parsed;
|
|
|
|
}
|
|
|
|
QRegularExpression usDate("\\d+/\\d+/\\d+[^\\d]+\\d+:\\d+:\\d+");
|
|
|
|
if (date.contains(usDate)) {
|
|
|
|
newDate = QDateTime::fromString(date, "M/d/yyyy h:m:s");
|
|
|
|
if (newDate.isValid())
|
|
|
|
goto parsed;
|
|
|
|
newDate = QDateTime::fromString(date, "M/d/yy h:m:s");
|
|
|
|
if (newDate.isValid())
|
|
|
|
goto parsed;
|
|
|
|
newDate = QDateTime::fromString(date.toLower(), "M/d/yyyy h:m:sap");
|
|
|
|
if (newDate.isValid())
|
|
|
|
goto parsed;
|
|
|
|
newDate = QDateTime::fromString(date.toLower(), "M/d/yy h:m:sap");
|
|
|
|
if (newDate.isValid())
|
|
|
|
goto parsed;
|
|
|
|
}
|
|
|
|
QRegularExpression usDateNoSecs("\\d+/\\d+/\\d+[^\\d]+\\d+:\\d+");
|
|
|
|
if (date.contains(usDateNoSecs)) {
|
|
|
|
newDate = QDateTime::fromString(date, "M/d/yyyy h:m");
|
|
|
|
if (newDate.isValid())
|
|
|
|
goto parsed;
|
|
|
|
newDate = QDateTime::fromString(date, "M/d/yy h:m");
|
|
|
|
if (newDate.isValid())
|
|
|
|
goto parsed;
|
|
|
|
newDate = QDateTime::fromString(date.toLower(), "M/d/yyyy h:map");
|
|
|
|
if (newDate.isValid())
|
|
|
|
goto parsed;
|
|
|
|
newDate = QDateTime::fromString(date.toLower(), "M/d/yy h:map");
|
|
|
|
if (newDate.isValid())
|
|
|
|
goto parsed;
|
|
|
|
}
|
|
|
|
QRegularExpression leDate("\\d+\\.\\d+\\.\\d+[^\\d]+\\d+:\\d+:\\d+");
|
|
|
|
if (date.contains(leDate)) {
|
|
|
|
newDate = QDateTime::fromString(date, "d.M.yyyy h:m:s");
|
|
|
|
if (newDate.isValid())
|
|
|
|
goto parsed;
|
|
|
|
newDate = QDateTime::fromString(date, "d.M.yy h:m:s");
|
|
|
|
if (newDate.isValid())
|
|
|
|
goto parsed;
|
|
|
|
}
|
|
|
|
QRegularExpression leDateNoSecs("\\d+\\.\\d+\\.\\d+[^\\d]+\\d+:\\d+");
|
|
|
|
if (date.contains(leDateNoSecs)) {
|
|
|
|
newDate = QDateTime::fromString(date, "d.M.yyyy h:m");
|
|
|
|
if (newDate.isValid())
|
|
|
|
goto parsed;
|
|
|
|
newDate = QDateTime::fromString(date, "d.M.yy h:m");
|
|
|
|
if (newDate.isValid())
|
|
|
|
goto parsed;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
parsed:
|
2016-01-27 20:07:10 +00:00
|
|
|
if (newDate.isValid()) {
|
|
|
|
// stupid Qt... two digit years are always 19xx - WTF???
|
|
|
|
// so if adding a hundred years gets you into something before a year from now...
|
|
|
|
// add a hundred years.
|
|
|
|
if (newDate.addYears(100) < QDateTime::currentDateTime().addYears(1))
|
|
|
|
newDate = newDate.addYears(100);
|
2016-01-11 06:07:58 +00:00
|
|
|
d->dc.when = d->when = newDate.toMSecsSinceEpoch() / 1000 + gettimezoneoffset(newDate.toMSecsSinceEpoch() / 1000);
|
2016-03-11 02:36:46 +00:00
|
|
|
} else {
|
|
|
|
qDebug() << "none of our parsing attempts worked for the date string";
|
2016-01-27 20:07:10 +00:00
|
|
|
}
|
2016-01-06 06:53:32 +00:00
|
|
|
}
|
2015-12-27 06:57:47 +00:00
|
|
|
struct dive_site *ds = get_dive_site_by_uuid(d->dive_site_uuid);
|
|
|
|
char *locationtext = NULL;
|
|
|
|
if (ds)
|
|
|
|
locationtext = ds->name;
|
|
|
|
if (!same_string(locationtext, qPrintable(location))) {
|
|
|
|
diveChanged = true;
|
|
|
|
// this is not ideal - and it's missing the gps information
|
|
|
|
// but for now let's just create a new dive site
|
|
|
|
ds = get_dive_site_by_uuid(create_dive_site(qPrintable(location), d->when));
|
|
|
|
d->dive_site_uuid = ds->uuid;
|
|
|
|
}
|
2016-01-27 14:29:14 +00:00
|
|
|
if (!gps.isEmpty()) {
|
|
|
|
QString gpsString = getCurrentPosition();
|
|
|
|
if (gpsString != QString("waiting for the next gps location")) {
|
|
|
|
qDebug() << "from commitChanges call to getCurrentPosition returns" << gpsString;
|
|
|
|
double lat, lon;
|
|
|
|
if (parseGpsText(qPrintable(gpsString), &lat, &lon)) {
|
|
|
|
struct dive_site *ds = get_dive_site_by_uuid(d->dive_site_uuid);
|
|
|
|
if (ds) {
|
|
|
|
ds->latitude.udeg = lat * 1000000;
|
|
|
|
ds->longitude.udeg = lon * 1000000;
|
|
|
|
} else {
|
|
|
|
degrees_t latData, lonData;
|
|
|
|
latData.udeg = lat;
|
|
|
|
lonData.udeg = lon;
|
|
|
|
d->dive_site_uuid = create_dive_site_with_gps("new site", latData, lonData, d->when);
|
|
|
|
}
|
|
|
|
qDebug() << "set up dive site with new GPS data";
|
2016-01-02 01:23:29 +00:00
|
|
|
}
|
2016-01-27 14:29:14 +00:00
|
|
|
} else {
|
|
|
|
qDebug() << "still don't have a position - will need to implement some sort of callback";
|
2016-01-02 01:23:29 +00:00
|
|
|
}
|
|
|
|
}
|
2016-01-01 08:23:15 +00:00
|
|
|
if (get_dive_duration_string(d->duration.seconds, tr("h:"), tr("min")) != duration) {
|
|
|
|
diveChanged = true;
|
|
|
|
int h = 0, m = 0, s = 0;
|
2016-02-06 20:07:42 +00:00
|
|
|
QRegExp r1(QStringLiteral("(\\d*)\\s*%1[\\s,:]*(\\d*)\\s*%2[\\s,:]*(\\d*)\\s*%3").arg(tr("h")).arg(tr("min")).arg(tr("sec")), Qt::CaseInsensitive);
|
|
|
|
QRegExp r2(QStringLiteral("(\\d*)\\s*%1[\\s,:]*(\\d*)\\s*%2").arg(tr("h")).arg(tr("min")), Qt::CaseInsensitive);
|
|
|
|
QRegExp r3(QStringLiteral("(\\d*)\\s*%1").arg(tr("min")), Qt::CaseInsensitive);
|
2016-01-26 15:02:42 +00:00
|
|
|
QRegExp r4(QStringLiteral("(\\d*):(\\d*):(\\d*)"));
|
|
|
|
QRegExp r5(QStringLiteral("(\\d*):(\\d*)"));
|
2016-02-09 21:00:56 +00:00
|
|
|
QRegExp r6(QStringLiteral("(\\d*)"));
|
2016-01-01 08:23:15 +00:00
|
|
|
if (r1.indexIn(duration) >= 0) {
|
|
|
|
h = r1.cap(1).toInt();
|
|
|
|
m = r1.cap(2).toInt();
|
|
|
|
s = r1.cap(3).toInt();
|
|
|
|
} else if (r2.indexIn(duration) >= 0) {
|
|
|
|
h = r2.cap(1).toInt();
|
|
|
|
m = r2.cap(2).toInt();
|
|
|
|
} else if (r3.indexIn(duration) >= 0) {
|
|
|
|
m = r3.cap(1).toInt();
|
|
|
|
} else if (r4.indexIn(duration) >= 0) {
|
|
|
|
h = r4.cap(1).toInt();
|
|
|
|
m = r4.cap(2).toInt();
|
|
|
|
s = r4.cap(3).toInt();
|
|
|
|
} else if (r5.indexIn(duration) >= 0) {
|
|
|
|
h = r5.cap(1).toInt();
|
|
|
|
m = r5.cap(2).toInt();
|
2016-02-09 21:00:56 +00:00
|
|
|
} else if (r6.indexIn(duration) >= 0) {
|
|
|
|
m = r6.cap(1).toInt();
|
2016-01-01 08:23:15 +00:00
|
|
|
}
|
2016-01-11 06:08:42 +00:00
|
|
|
d->dc.duration.seconds = d->duration.seconds = h * 3600 + m * 60 + s;
|
2016-03-02 14:28:00 +00:00
|
|
|
if (same_string(d->dc.model, "manually added dive")) {
|
|
|
|
free(d->dc.sample);
|
|
|
|
d->dc.sample = 0;
|
|
|
|
d->dc.samples = 0;
|
|
|
|
} else {
|
|
|
|
qDebug() << "changing the duration on a dive that wasn't manually added - Uh-oh";
|
|
|
|
}
|
|
|
|
|
2016-01-01 08:23:15 +00:00
|
|
|
}
|
2016-01-01 08:32:30 +00:00
|
|
|
if (get_depth_string(d->maxdepth.mm, true, true) != depth) {
|
2016-02-21 06:11:03 +00:00
|
|
|
int depthValue = parseLengthToMm(depth);
|
|
|
|
// the QML code should stop negative depth, but massively huge depth can make
|
|
|
|
// the profile extremely slow or even run out of memory and crash, so keep
|
|
|
|
// the depth <= 500m
|
|
|
|
if (0 <= depthValue && depthValue <= 500000) {
|
|
|
|
diveChanged = true;
|
|
|
|
d->maxdepth.mm = depthValue;
|
|
|
|
if (same_string(d->dc.model, "manually added dive")) {
|
|
|
|
d->dc.maxdepth.mm = d->maxdepth.mm;
|
|
|
|
free(d->dc.sample);
|
|
|
|
d->dc.sample = 0;
|
|
|
|
d->dc.samples = 0;
|
|
|
|
}
|
2016-02-15 15:51:23 +00:00
|
|
|
}
|
2016-01-01 08:32:30 +00:00
|
|
|
}
|
2016-01-20 21:56:28 +00:00
|
|
|
if (get_temperature_string(d->airtemp, true) != airtemp) {
|
2016-01-01 01:34:20 +00:00
|
|
|
diveChanged = true;
|
|
|
|
d->airtemp.mkelvin = parseTemperatureToMkelvin(airtemp);
|
|
|
|
}
|
2016-01-20 21:56:28 +00:00
|
|
|
if (get_temperature_string(d->watertemp, true) != watertemp) {
|
2016-01-01 01:34:20 +00:00
|
|
|
diveChanged = true;
|
|
|
|
d->watertemp.mkelvin = parseTemperatureToMkelvin(watertemp);
|
|
|
|
}
|
2016-02-06 06:54:47 +00:00
|
|
|
// not sure what we'd do if there was more than one weight system
|
|
|
|
// defined - for now just ignore that case
|
|
|
|
if (weightsystem_none((void *)&d->weightsystem[1])) {
|
|
|
|
if (get_weight_string(d->weightsystem[0].weight, true) != weight) {
|
|
|
|
diveChanged = true;
|
|
|
|
d->weightsystem[0].weight.grams = parseWeightToGrams(weight);
|
|
|
|
}
|
|
|
|
}
|
2016-02-28 14:43:54 +00:00
|
|
|
// start and end pressures for first cylinder only
|
2016-02-09 16:20:17 +00:00
|
|
|
if (get_pressure_string(d->cylinder[0].start, true) != startpressure || get_pressure_string(d->cylinder[0].end, true) != endpressure) {
|
|
|
|
diveChanged = true;
|
|
|
|
d->cylinder[0].start.mbar = parsePressureToMbar(startpressure);
|
|
|
|
d->cylinder[0].end.mbar = parsePressureToMbar(endpressure);
|
2016-03-11 21:17:57 +00:00
|
|
|
if (d->cylinder[0].end.mbar > d->cylinder[0].start.mbar)
|
|
|
|
d->cylinder[0].end.mbar = d->cylinder[0].start.mbar;
|
2016-02-09 16:20:17 +00:00
|
|
|
}
|
2016-02-13 17:34:31 +00:00
|
|
|
// gasmix for first cylinder
|
|
|
|
if (get_gas_string(d->cylinder[0].gasmix) != gasmix) {
|
2016-02-21 06:11:03 +00:00
|
|
|
int o2 = parseGasMixO2(gasmix);
|
|
|
|
int he = parseGasMixHE(gasmix);
|
|
|
|
// the QML code SHOULD only accept valid gas mixes, but just to make sure
|
2016-03-11 06:03:00 +00:00
|
|
|
if (o2 >= 0 && o2 <= 1000 &&
|
|
|
|
he >= 0 && he <= 1000 &&
|
|
|
|
o2 + he <= 1000) {
|
2016-02-21 06:11:03 +00:00
|
|
|
diveChanged = true;
|
|
|
|
d->cylinder[0].gasmix.o2.permille = o2;
|
|
|
|
d->cylinder[0].gasmix.he.permille = he;
|
|
|
|
}
|
2016-02-13 17:34:31 +00:00
|
|
|
}
|
2015-12-27 06:57:47 +00:00
|
|
|
if (!same_string(d->suit, qPrintable(suit))) {
|
2015-07-17 15:28:01 +00:00
|
|
|
diveChanged = true;
|
|
|
|
free(d->suit);
|
2015-12-27 06:57:47 +00:00
|
|
|
d->suit = strdup(qPrintable(suit));
|
2015-07-17 15:28:01 +00:00
|
|
|
}
|
2015-12-27 06:57:47 +00:00
|
|
|
if (!same_string(d->buddy, qPrintable(buddy))) {
|
2015-07-17 15:28:01 +00:00
|
|
|
diveChanged = true;
|
|
|
|
free(d->buddy);
|
2015-12-27 06:57:47 +00:00
|
|
|
d->buddy = strdup(qPrintable(buddy));
|
2015-07-17 15:28:01 +00:00
|
|
|
}
|
2015-12-27 06:57:47 +00:00
|
|
|
if (!same_string(d->divemaster, qPrintable(diveMaster))) {
|
2015-07-17 15:28:01 +00:00
|
|
|
diveChanged = true;
|
|
|
|
free(d->divemaster);
|
2015-12-27 06:57:47 +00:00
|
|
|
d->divemaster = strdup(qPrintable(diveMaster));
|
2015-07-17 15:28:01 +00:00
|
|
|
}
|
2015-12-27 06:57:47 +00:00
|
|
|
if (!same_string(d->notes, qPrintable(notes))) {
|
2015-07-17 15:28:01 +00:00
|
|
|
diveChanged = true;
|
|
|
|
free(d->notes);
|
2015-12-27 06:57:47 +00:00
|
|
|
d->notes = strdup(qPrintable(notes));
|
2015-07-17 15:28:01 +00:00
|
|
|
}
|
2016-03-02 12:52:44 +00:00
|
|
|
// now that we have it all figured out, let's see what we need
|
|
|
|
// to update
|
|
|
|
DiveListModel *dm = DiveListModel::instance();
|
|
|
|
int oldModelIdx = dm->getDiveIdx(d->id);
|
2016-01-27 20:11:37 +00:00
|
|
|
int oldIdx = get_idx_by_uniq_id(d->id);
|
|
|
|
if (needResort) {
|
|
|
|
// we know that the only thing that might happen in a resort is that
|
|
|
|
// this one dive moves to a different spot in the dive list
|
2016-01-06 06:57:40 +00:00
|
|
|
sort_table(&dive_table);
|
2016-01-27 20:11:37 +00:00
|
|
|
int newIdx = get_idx_by_uniq_id(d->id);
|
|
|
|
if (newIdx != oldIdx) {
|
|
|
|
DiveObjectHelper *newDive = new DiveObjectHelper(d);
|
2016-03-02 12:52:44 +00:00
|
|
|
DiveListModel::instance()->removeDive(oldModelIdx);
|
|
|
|
DiveListModel::instance()->insertDive(oldModelIdx - (newIdx - oldIdx), newDive);
|
2016-01-27 20:11:37 +00:00
|
|
|
diveChanged = false; // because we already modified things
|
|
|
|
}
|
|
|
|
}
|
2016-02-20 15:33:05 +00:00
|
|
|
if (diveChanged) {
|
|
|
|
if (d->maxdepth.mm == d->dc.maxdepth.mm &&
|
|
|
|
d->maxdepth.mm > 0 &&
|
|
|
|
same_string(d->dc.model, "manually added dive") &&
|
|
|
|
d->dc.samples == 0) {
|
|
|
|
// so we have depth > 0, a manually added dive and no samples
|
|
|
|
// let's create an actual profile so the desktop version can work it
|
|
|
|
// first clear out the mean depth (or the fake_dc() function tries
|
|
|
|
// to be too clever
|
|
|
|
d->meandepth.mm = d->dc.meandepth.mm = 0;
|
2016-02-20 17:36:14 +00:00
|
|
|
d->dc = *fake_dc(&d->dc, true);
|
2016-02-20 15:33:05 +00:00
|
|
|
}
|
2016-03-02 12:52:44 +00:00
|
|
|
DiveListModel::instance()->updateDive(oldModelIdx, d);
|
2016-02-20 15:33:05 +00:00
|
|
|
}
|
QML UI: don't immediately save data after we make changes
Much as this felt like the prudent thing to do, it makes the UI painful
to use. In bad network conditions, with a large dive log, on a phone,
the save operation can take more than a minute - which is just completely
ludicrous.
So instead we mark the dive list changed when we make changes and wait
for the app to not be in the foreground. Once the OS tells us that we are
hidden (on the desktop that generally means we don't have focus, on a
mobile device it usually does mean that the app is not on the screen), we
check if there are data to be saved and do so.
There is of course a major problem with this logic. If the user switches
away from Subsurface-mobile but comes back fairly quickly (just reacting
to a notification or briefly checking something, changing a song,
something... then the app may still be non-responsive for quite a while.
So we need to do something about the time it takes us to save the git
tree locally, and then figure out if we can move at least some of the
network traffic to another thread.
And we need to make sure the user immediately notices that the app is not
crashed but is actually saving their data. But that's for another commit.
tl;dr: CAREFUL, don't kill Subsurface-mobile before it had time to save
your data or your changes may be gone. In typical use that shouldn't be
an issue, but it is something that we need to tell the user about.
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2016-04-04 00:37:17 +00:00
|
|
|
if (diveChanged || needResort)
|
2016-04-11 02:22:16 +00:00
|
|
|
changesNeedSaving();
|
|
|
|
}
|
QML UI: don't immediately save data after we make changes
Much as this felt like the prudent thing to do, it makes the UI painful
to use. In bad network conditions, with a large dive log, on a phone,
the save operation can take more than a minute - which is just completely
ludicrous.
So instead we mark the dive list changed when we make changes and wait
for the app to not be in the foreground. Once the OS tells us that we are
hidden (on the desktop that generally means we don't have focus, on a
mobile device it usually does mean that the app is not on the screen), we
check if there are data to be saved and do so.
There is of course a major problem with this logic. If the user switches
away from Subsurface-mobile but comes back fairly quickly (just reacting
to a notification or briefly checking something, changing a song,
something... then the app may still be non-responsive for quite a while.
So we need to do something about the time it takes us to save the git
tree locally, and then figure out if we can move at least some of the
network traffic to another thread.
And we need to make sure the user immediately notices that the app is not
crashed but is actually saving their data. But that's for another commit.
tl;dr: CAREFUL, don't kill Subsurface-mobile before it had time to save
your data or your changes may be gone. In typical use that shouldn't be
an issue, but it is something that we need to tell the user about.
Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2016-04-04 00:37:17 +00:00
|
|
|
|
2016-04-11 02:22:16 +00:00
|
|
|
void QMLManager::changesNeedSaving()
|
|
|
|
{
|
|
|
|
// we no longer save right away on iOS because file access is so slow; on the other hand,
|
|
|
|
// on Android the save as the user switches away doesn't seem to work... drat.
|
|
|
|
// as a compromise for now we save just to local storage on Android right away (that appears
|
|
|
|
// to be reasonably fast), but don't save at all (and only remember that we need to save things
|
|
|
|
// on iOS
|
|
|
|
// on all other platforms we just save the changes and be done with it
|
|
|
|
#if defined(Q_OS_IOS)
|
|
|
|
mark_divelist_changed(true);
|
|
|
|
#elif defined(Q_OS_ANDROID)
|
|
|
|
saveChangesLocal();
|
|
|
|
#else
|
|
|
|
saveChanges();
|
|
|
|
#endif
|
2015-07-17 15:28:01 +00:00
|
|
|
}
|
2016-04-06 18:47:12 +00:00
|
|
|
void QMLManager::saveChangesLocal()
|
2015-07-17 15:28:01 +00:00
|
|
|
{
|
2016-04-06 04:36:56 +00:00
|
|
|
if (unsaved_changes()) {
|
2016-04-06 05:54:54 +00:00
|
|
|
git_storage_update_progress(true, "saving dives locally"); // reset the timers
|
2016-04-06 18:47:12 +00:00
|
|
|
if (!loadFromCloud()) {
|
|
|
|
// this seems silly, but you need a common ancestor in the repository in
|
|
|
|
// order to be able to merge che changes later
|
|
|
|
appendTextToLog("Don't save dives without loading from the cloud, first.");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (alreadySaving) {
|
|
|
|
appendTextToLog("save operation already in progress, can't save locally");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
alreadySaving = true;
|
|
|
|
bool glo = prefs.git_local_only;
|
2016-04-06 04:36:56 +00:00
|
|
|
prefs.git_local_only = true;
|
2016-04-06 18:47:12 +00:00
|
|
|
if (save_dives(existing_filename)) {
|
2016-04-06 04:36:56 +00:00
|
|
|
appendTextToLog(get_error_string());
|
|
|
|
setAccessingCloud(-1);
|
|
|
|
prefs.git_local_only = glo;
|
|
|
|
alreadySaving = false;
|
|
|
|
return;
|
|
|
|
}
|
2016-04-06 18:47:12 +00:00
|
|
|
prefs.git_local_only = glo;
|
|
|
|
mark_divelist_changed(false);
|
|
|
|
git_storage_update_progress(false, "done with local save");
|
|
|
|
alreadySaving = false;
|
2016-04-06 04:36:56 +00:00
|
|
|
} else {
|
2016-04-06 18:47:12 +00:00
|
|
|
appendTextToLog("local save requested with no unsaved changes");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-04-08 19:35:45 +00:00
|
|
|
void QMLManager::saveChangesCloud(bool forceRemoteSync)
|
2016-04-06 18:47:12 +00:00
|
|
|
{
|
2016-04-08 19:35:45 +00:00
|
|
|
if (prefs.git_local_only && !forceRemoteSync)
|
|
|
|
return;
|
|
|
|
|
2016-04-06 18:47:12 +00:00
|
|
|
git_storage_update_progress(true, "start save change to cloud");
|
|
|
|
if (!loadFromCloud()) {
|
|
|
|
appendTextToLog("Don't save dives without loading from the cloud, first.");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
bool glo = prefs.git_local_only;
|
|
|
|
// first we need to store any unsaved changes to the local repo
|
|
|
|
saveChangesLocal();
|
|
|
|
if (alreadySaving) {
|
|
|
|
appendTextToLog("save operation in progress already, can't sync with server");
|
|
|
|
return;
|
2015-07-17 15:28:01 +00:00
|
|
|
}
|
2016-04-06 04:36:56 +00:00
|
|
|
prefs.git_local_only = false;
|
2016-04-06 18:47:12 +00:00
|
|
|
alreadySaving = true;
|
2016-04-06 04:36:56 +00:00
|
|
|
loadDivesWithValidCredentials();
|
2016-04-06 18:47:12 +00:00
|
|
|
alreadySaving = false;
|
|
|
|
git_storage_update_progress(false, "finished syncing dive list to cloud server");
|
2016-04-04 00:00:49 +00:00
|
|
|
setAccessingCloud(-1);
|
2016-04-06 04:36:56 +00:00
|
|
|
prefs.git_local_only = glo;
|
2016-04-06 04:36:15 +00:00
|
|
|
alreadySaving = false;
|
2015-07-10 08:31:24 +00:00
|
|
|
}
|
2015-08-10 05:35:47 +00:00
|
|
|
|
2016-02-29 14:53:26 +00:00
|
|
|
void QMLManager::undoDelete(int id)
|
|
|
|
{
|
|
|
|
if (!deletedDive || deletedDive->id != id) {
|
|
|
|
qDebug() << "can't find the deleted dive";
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (deletedTrip)
|
|
|
|
insert_trip(&deletedTrip);
|
|
|
|
if (deletedDive->divetrip) {
|
|
|
|
struct dive_trip *trip = deletedDive->divetrip;
|
|
|
|
tripflag_t tripflag = deletedDive->tripflag; // this gets overwritten in add_dive_to_trip()
|
|
|
|
deletedDive->divetrip = NULL;
|
|
|
|
deletedDive->next = NULL;
|
|
|
|
deletedDive->pprev = NULL;
|
|
|
|
add_dive_to_trip(deletedDive, trip);
|
|
|
|
deletedDive->tripflag = tripflag;
|
|
|
|
}
|
|
|
|
record_dive(deletedDive);
|
2016-04-06 04:17:37 +00:00
|
|
|
QList<dive *>diveAsList;
|
|
|
|
diveAsList << deletedDive;
|
|
|
|
DiveListModel::instance()->addDive(diveAsList);
|
2016-04-11 02:22:16 +00:00
|
|
|
changesNeedSaving();
|
2016-02-29 14:53:26 +00:00
|
|
|
deletedDive = NULL;
|
|
|
|
deletedTrip = NULL;
|
|
|
|
}
|
|
|
|
|
2016-02-23 12:39:40 +00:00
|
|
|
void QMLManager::deleteDive(int id)
|
|
|
|
{
|
|
|
|
struct dive *d = get_dive_by_uniq_id(id);
|
|
|
|
if (!d) {
|
|
|
|
qDebug() << "oops, trying to delete non-existing dive";
|
|
|
|
return;
|
|
|
|
}
|
2016-02-29 14:53:26 +00:00
|
|
|
// clean up (or create) the storage for the deleted dive and trip (if applicable)
|
|
|
|
if (!deletedDive)
|
|
|
|
deletedDive = alloc_dive();
|
|
|
|
else
|
|
|
|
clear_dive(deletedDive);
|
|
|
|
copy_dive(d, deletedDive);
|
|
|
|
if (!deletedTrip) {
|
|
|
|
deletedTrip = (struct dive_trip *)calloc(1, sizeof(struct dive_trip));
|
|
|
|
} else {
|
|
|
|
free(deletedTrip->location);
|
|
|
|
free(deletedTrip->notes);
|
|
|
|
memset(deletedTrip, 0, sizeof(struct dive_trip));
|
|
|
|
}
|
|
|
|
// if this is the last dive in that trip, remember the trip as well
|
|
|
|
if (d->divetrip && d->divetrip->nrdives == 1) {
|
|
|
|
*deletedTrip = *d->divetrip;
|
|
|
|
deletedTrip->location = copy_string(d->divetrip->location);
|
|
|
|
deletedTrip->notes = copy_string(d->divetrip->notes);
|
|
|
|
deletedTrip->nrdives = 0;
|
|
|
|
deletedDive->divetrip = deletedTrip;
|
|
|
|
}
|
2016-02-23 12:39:40 +00:00
|
|
|
DiveListModel::instance()->removeDiveById(id);
|
|
|
|
delete_single_dive(get_idx_by_uniq_id(id));
|
2016-04-11 02:22:16 +00:00
|
|
|
changesNeedSaving();
|
2016-02-23 12:39:40 +00:00
|
|
|
}
|
|
|
|
|
2015-12-27 05:37:18 +00:00
|
|
|
QString QMLManager::addDive()
|
2015-08-10 05:35:47 +00:00
|
|
|
{
|
2015-08-19 07:17:52 +00:00
|
|
|
appendTextToLog("Adding new dive.");
|
2015-12-27 05:37:18 +00:00
|
|
|
return DiveListModel::instance()->startAddDive();
|
2015-08-10 05:35:47 +00:00
|
|
|
}
|
|
|
|
|
2016-01-29 14:25:13 +00:00
|
|
|
void QMLManager::addDiveAborted(int id)
|
|
|
|
{
|
|
|
|
DiveListModel::instance()->removeDiveById(id);
|
|
|
|
}
|
|
|
|
|
2016-01-02 01:23:29 +00:00
|
|
|
QString QMLManager::getCurrentPosition()
|
|
|
|
{
|
|
|
|
return locationProvider->currentPosition();
|
|
|
|
}
|
|
|
|
|
2015-11-13 04:23:00 +00:00
|
|
|
void QMLManager::applyGpsData()
|
|
|
|
{
|
2016-01-11 03:34:21 +00:00
|
|
|
if (locationProvider->applyLocations())
|
|
|
|
refreshDiveList();
|
2015-11-13 04:23:00 +00:00
|
|
|
}
|
|
|
|
|
2015-11-14 01:21:43 +00:00
|
|
|
void QMLManager::sendGpsData()
|
|
|
|
{
|
|
|
|
locationProvider->uploadToServer();
|
|
|
|
}
|
|
|
|
|
2016-01-08 05:37:36 +00:00
|
|
|
void QMLManager::downloadGpsData()
|
|
|
|
{
|
|
|
|
locationProvider->downloadFromServer();
|
2016-01-08 15:11:49 +00:00
|
|
|
populateGpsData();
|
2016-01-08 05:37:36 +00:00
|
|
|
}
|
|
|
|
|
2016-01-08 05:40:15 +00:00
|
|
|
void QMLManager::populateGpsData()
|
|
|
|
{
|
2016-01-08 15:11:49 +00:00
|
|
|
if (GpsListModel::instance())
|
|
|
|
GpsListModel::instance()->update();
|
2016-01-08 05:40:15 +00:00
|
|
|
}
|
|
|
|
|
2015-11-14 01:20:45 +00:00
|
|
|
void QMLManager::clearGpsData()
|
|
|
|
{
|
|
|
|
locationProvider->clearGpsData();
|
2016-01-08 15:11:49 +00:00
|
|
|
populateGpsData();
|
2015-11-14 01:20:45 +00:00
|
|
|
}
|
|
|
|
|
2016-01-09 07:18:41 +00:00
|
|
|
void QMLManager::deleteGpsFix(quint64 when)
|
|
|
|
{
|
|
|
|
locationProvider->deleteGpsFix(when);
|
|
|
|
populateGpsData();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-08-19 07:17:52 +00:00
|
|
|
QString QMLManager::logText() const
|
|
|
|
{
|
2015-11-13 17:17:13 +00:00
|
|
|
QString logText = m_logText + QString("\nNumer of GPS fixes: %1").arg(locationProvider->getGpsNum());
|
|
|
|
return logText;
|
2015-08-19 07:17:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QMLManager::setLogText(const QString &logText)
|
|
|
|
{
|
|
|
|
m_logText = logText;
|
2015-08-20 09:08:59 +00:00
|
|
|
emit logTextChanged();
|
2015-08-19 07:17:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QMLManager::appendTextToLog(const QString &newText)
|
|
|
|
{
|
|
|
|
m_logText += "\n" + newText;
|
2015-08-20 09:08:59 +00:00
|
|
|
emit logTextChanged();
|
2015-08-19 07:17:52 +00:00
|
|
|
}
|
|
|
|
|
2015-11-11 20:34:56 +00:00
|
|
|
bool QMLManager::locationServiceEnabled() const
|
|
|
|
{
|
|
|
|
return m_locationServiceEnabled;
|
|
|
|
}
|
|
|
|
|
|
|
|
void QMLManager::setLocationServiceEnabled(bool locationServiceEnabled)
|
|
|
|
{
|
|
|
|
m_locationServiceEnabled = locationServiceEnabled;
|
|
|
|
locationProvider->serviceEnable(m_locationServiceEnabled);
|
|
|
|
}
|
2015-07-10 08:31:24 +00:00
|
|
|
|
2015-12-20 02:41:10 +00:00
|
|
|
bool QMLManager::verboseEnabled() const
|
|
|
|
{
|
|
|
|
return m_verboseEnabled;
|
|
|
|
}
|
|
|
|
|
|
|
|
void QMLManager::setVerboseEnabled(bool verboseMode)
|
|
|
|
{
|
|
|
|
m_verboseEnabled = verboseMode;
|
|
|
|
verbose = verboseMode;
|
|
|
|
qDebug() << "verbose is" << verbose;
|
|
|
|
emit verboseEnabledChanged();
|
|
|
|
}
|
|
|
|
|
2015-07-10 07:47:26 +00:00
|
|
|
QString QMLManager::cloudPassword() const
|
|
|
|
{
|
|
|
|
return m_cloudPassword;
|
|
|
|
}
|
|
|
|
|
|
|
|
void QMLManager::setCloudPassword(const QString &cloudPassword)
|
|
|
|
{
|
|
|
|
m_cloudPassword = cloudPassword;
|
|
|
|
emit cloudPasswordChanged();
|
|
|
|
}
|
|
|
|
|
|
|
|
QString QMLManager::cloudUserName() const
|
|
|
|
{
|
|
|
|
return m_cloudUserName;
|
|
|
|
}
|
|
|
|
|
|
|
|
void QMLManager::setCloudUserName(const QString &cloudUserName)
|
|
|
|
{
|
2016-03-24 06:12:03 +00:00
|
|
|
m_cloudUserName = cloudUserName.toLower();
|
2015-07-10 07:47:26 +00:00
|
|
|
emit cloudUserNameChanged();
|
|
|
|
}
|
2015-11-14 01:14:22 +00:00
|
|
|
|
2015-11-14 17:10:06 +00:00
|
|
|
int QMLManager::distanceThreshold() const
|
|
|
|
{
|
|
|
|
return m_distanceThreshold;
|
|
|
|
}
|
|
|
|
|
|
|
|
void QMLManager::setDistanceThreshold(int distance)
|
|
|
|
{
|
|
|
|
m_distanceThreshold = distance;
|
|
|
|
emit distanceThresholdChanged();
|
|
|
|
}
|
|
|
|
|
|
|
|
int QMLManager::timeThreshold() const
|
|
|
|
{
|
|
|
|
return m_timeThreshold;
|
|
|
|
}
|
|
|
|
|
|
|
|
void QMLManager::setTimeThreshold(int time)
|
|
|
|
{
|
|
|
|
m_timeThreshold = time;
|
|
|
|
emit timeThresholdChanged();
|
|
|
|
}
|
2015-12-03 02:49:02 +00:00
|
|
|
|
|
|
|
bool QMLManager::loadFromCloud() const
|
|
|
|
{
|
|
|
|
return m_loadFromCloud;
|
|
|
|
}
|
|
|
|
|
2016-02-08 19:08:49 +00:00
|
|
|
void QMLManager::syncLoadFromCloud()
|
|
|
|
{
|
|
|
|
QSettings s;
|
|
|
|
QString cloudMarker = QLatin1Literal("loadFromCloud") + QString(prefs.cloud_storage_email);
|
|
|
|
m_loadFromCloud = s.contains(cloudMarker) && s.value(cloudMarker).toBool();
|
|
|
|
}
|
|
|
|
|
2015-12-03 02:49:02 +00:00
|
|
|
void QMLManager::setLoadFromCloud(bool done)
|
|
|
|
{
|
2016-01-11 05:38:44 +00:00
|
|
|
QSettings s;
|
2016-02-08 19:08:49 +00:00
|
|
|
QString cloudMarker = QLatin1Literal("loadFromCloud") + QString(prefs.cloud_storage_email);
|
|
|
|
s.setValue(cloudMarker, done);
|
2015-12-03 02:49:02 +00:00
|
|
|
m_loadFromCloud = done;
|
|
|
|
emit loadFromCloudChanged();
|
|
|
|
}
|
2015-12-15 07:00:19 +00:00
|
|
|
|
|
|
|
QString QMLManager::startPageText() const
|
|
|
|
{
|
|
|
|
return m_startPageText;
|
|
|
|
}
|
|
|
|
|
2016-01-26 15:02:42 +00:00
|
|
|
void QMLManager::setStartPageText(const QString& text)
|
2015-12-15 07:00:19 +00:00
|
|
|
{
|
|
|
|
m_startPageText = text;
|
|
|
|
emit startPageTextChanged();
|
|
|
|
}
|
2015-12-26 21:22:50 +00:00
|
|
|
|
2016-02-11 01:45:23 +00:00
|
|
|
QMLManager::credentialStatus_t QMLManager::credentialStatus() const
|
|
|
|
{
|
|
|
|
return m_credentialStatus;
|
|
|
|
}
|
|
|
|
|
|
|
|
void QMLManager::setCredentialStatus(const credentialStatus_t value)
|
|
|
|
{
|
|
|
|
if (m_credentialStatus != value) {
|
|
|
|
m_credentialStatus = value;
|
|
|
|
emit credentialStatusChanged();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-04-06 18:42:38 +00:00
|
|
|
QMLManager::credentialStatus_t QMLManager::oldStatus() const
|
|
|
|
{
|
|
|
|
return m_oldStatus;
|
|
|
|
}
|
|
|
|
|
|
|
|
void QMLManager::setOldStatus(const credentialStatus_t value)
|
|
|
|
{
|
|
|
|
if (m_oldStatus != value) {
|
|
|
|
m_oldStatus = value;
|
|
|
|
emit oldStatusChanged();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-03-02 12:41:36 +00:00
|
|
|
// where in the QML dive list is that dive?
|
|
|
|
int QMLManager::getIndex(const QString &diveId)
|
|
|
|
{
|
|
|
|
int dive_id = diveId.toInt();
|
|
|
|
int idx = DiveListModel::instance()->getDiveIdx(dive_id);
|
|
|
|
return idx;
|
|
|
|
}
|
|
|
|
|
2016-01-26 15:02:42 +00:00
|
|
|
QString QMLManager::getNumber(const QString& diveId)
|
2015-12-27 04:02:23 +00:00
|
|
|
{
|
|
|
|
int dive_id = diveId.toInt();
|
|
|
|
struct dive *d = get_dive_by_uniq_id(dive_id);
|
|
|
|
QString number;
|
|
|
|
if (d)
|
|
|
|
number = QString::number(d->number);
|
|
|
|
return number;
|
|
|
|
}
|
|
|
|
|
2016-01-26 15:02:42 +00:00
|
|
|
QString QMLManager::getDate(const QString& diveId)
|
2015-12-27 04:02:23 +00:00
|
|
|
{
|
|
|
|
int dive_id = diveId.toInt();
|
|
|
|
struct dive *d = get_dive_by_uniq_id(dive_id);
|
|
|
|
QString datestring;
|
|
|
|
if (d)
|
|
|
|
datestring = get_dive_date_string(d->when);
|
|
|
|
return datestring;
|
|
|
|
}
|
2016-02-10 20:53:58 +00:00
|
|
|
|
|
|
|
QString QMLManager::getVersion() const
|
|
|
|
{
|
2016-03-05 20:53:38 +00:00
|
|
|
QRegExp versionRe(".*:([()\\.,\\d]+).*");
|
2016-02-10 20:53:58 +00:00
|
|
|
if (!versionRe.exactMatch(getUserAgent()))
|
|
|
|
return QString();
|
|
|
|
|
|
|
|
return versionRe.cap(1);
|
|
|
|
}
|
2016-03-03 01:13:42 +00:00
|
|
|
|
2016-04-04 00:00:49 +00:00
|
|
|
int QMLManager::accessingCloud() const
|
2016-03-03 01:13:42 +00:00
|
|
|
{
|
|
|
|
return m_accessingCloud;
|
|
|
|
}
|
|
|
|
|
2016-04-04 00:00:49 +00:00
|
|
|
void QMLManager::setAccessingCloud(int status)
|
2016-03-03 01:13:42 +00:00
|
|
|
{
|
|
|
|
m_accessingCloud = status;
|
|
|
|
emit accessingCloudChanged();
|
|
|
|
}
|
2016-03-09 03:31:05 +00:00
|
|
|
|
2016-04-04 01:33:40 +00:00
|
|
|
bool QMLManager::syncToCloud() const
|
|
|
|
{
|
|
|
|
return m_syncToCloud;
|
|
|
|
}
|
|
|
|
|
|
|
|
void QMLManager::setSyncToCloud(bool status)
|
|
|
|
{
|
|
|
|
m_syncToCloud = status;
|
|
|
|
prefs.git_local_only = !status;
|
2016-04-04 01:48:20 +00:00
|
|
|
QSettings s;
|
|
|
|
s.beginGroup("CloudStorage");
|
|
|
|
s.setValue("git_local_only", prefs.git_local_only);
|
2016-04-04 01:33:40 +00:00
|
|
|
emit syncToCloudChanged();
|
|
|
|
}
|
|
|
|
|
2016-03-09 03:31:05 +00:00
|
|
|
qreal QMLManager::lastDevicePixelRatio()
|
|
|
|
{
|
|
|
|
return m_lastDevicePixelRatio;
|
|
|
|
}
|
|
|
|
|
|
|
|
void QMLManager::screenChanged(QScreen *screen)
|
|
|
|
{
|
|
|
|
m_lastDevicePixelRatio = screen->devicePixelRatio();
|
|
|
|
emit sendScreenChanged(screen);
|
|
|
|
}
|