subsurface/qt-mobile/qmlmanager.cpp
Dirk Hohndel 99a5a743c4 QML UI: change the way we extract input data from dive edit
Instead of doing the silly "onEditingFinished" we get the strings from the QML
components at the time we commit the change. Much more logical, much more
straight forward, no issues with the TextArea not having an onEditingFinished
signal.

This still has a few open todos: the temperatures aren't parsed, the edit
screen is missing depth and duration, we can't edit the dive time (and it isn't
passed in on the commit). But it's progress.

Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2015-12-26 22:57:47 -08:00

524 lines
14 KiB
C++

#include "qmlmanager.h"
#include <QUrl>
#include <QSettings>
#include <QDebug>
#include <QNetworkAccessManager>
#include <QAuthenticator>
#include <QDesktopServices>
#include "qt-models/divelistmodel.h"
#include "divelist.h"
#include "pref.h"
#include "qthelper.h"
#include "qt-gui.h"
#include "git-access.h"
QMLManager *QMLManager::m_instance = NULL;
static void appendTextToLogStandalone(const char *text)
{
QMLManager *mgr = QMLManager::instance();
if (mgr)
mgr->appendTextToLog(QString(text));
}
extern "C" int gitProgressCB(int percent)
{
static int lastPercent = -10;
if (percent - lastPercent >= 10) {
lastPercent += 10;
QMLManager *mgr = QMLManager::instance();
if (mgr)
mgr->loadDiveProgress(percent);
}
// return 0 so that we don't end the download
return 0;
}
QMLManager::QMLManager() :
m_locationServiceEnabled(false),
reply(0),
mgr(0)
{
m_instance = this;
appendTextToLog(getUserAgent());
qDebug() << "Starting" << getUserAgent();
m_startPageText = tr("Searching for dive data");
// create location manager service
locationProvider = new GpsLocation(&appendTextToLogStandalone, this);
set_git_update_cb(&gitProgressCB);
}
void QMLManager::finishSetup()
{
// Initialize cloud credentials.
setCloudUserName(prefs.cloud_storage_email);
setCloudPassword(prefs.cloud_storage_password);
setSaveCloudPassword(prefs.save_password_local);
// if the cloud credentials are valid, we should get the GPS Webservice ID as well
if (!same_string(prefs.cloud_storage_email, "") &&
!same_string(prefs.cloud_storage_password, "")) {
appendTextToLog("have cloud credentials, trying to connect");
tryRetrieveDataFromBackend();
} else {
appendTextToLog("no cloud credentials, tell user no dives found");
setStartPageText(tr("No recorded dives found. You can download your dives to this device from the Subsurface cloud storage service, from your dive computer, or add them manually."));
}
setDistanceThreshold(prefs.distance_threshold);
setTimeThreshold(prefs.time_threshold / 60);
}
QMLManager::~QMLManager()
{
m_instance = NULL;
}
QMLManager *QMLManager::instance()
{
return m_instance;
}
void QMLManager::savePreferences()
{
QSettings s;
s.beginGroup("LocationService");
s.setValue("time_threshold", timeThreshold() * 60);
prefs.time_threshold = timeThreshold() * 60;
s.setValue("distance_threshold", distanceThreshold());
prefs.distance_threshold = distanceThreshold();
s.sync();
}
#define CLOUDURL QString(prefs.cloud_base_url)
#define CLOUDREDIRECTURL CLOUDURL + "/cgi-bin/redirect.pl"
void QMLManager::saveCloudCredentials()
{
QSettings s;
bool cloudCredentialsChanged = false;
s.beginGroup("CloudStorage");
s.setValue("email", cloudUserName());
s.setValue("save_password_local", saveCloudPassword());
if (saveCloudPassword())
s.setValue("password", cloudPassword());
s.sync();
if (!same_string(prefs.cloud_storage_email, qPrintable(cloudUserName()))) {
free(prefs.cloud_storage_email);
prefs.cloud_storage_email = strdup(qPrintable(cloudUserName()));
cloudCredentialsChanged = true;
}
if (saveCloudPassword() != prefs.save_password_local)
prefs.save_password_local = saveCloudPassword();
cloudCredentialsChanged |= !same_string(prefs.cloud_storage_password, qPrintable(cloudPassword()));
if (saveCloudPassword()) {
if (!same_string(prefs.cloud_storage_password, qPrintable(cloudPassword()))) {
free(prefs.cloud_storage_password);
prefs.cloud_storage_password = strdup(qPrintable(cloudPassword()));
}
}
if (cloudCredentialsChanged) {
free(prefs.userid);
prefs.userid = NULL;
tryRetrieveDataFromBackend();
}
}
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
if (!same_string(prefs.cloud_storage_email, "") &&
!same_string(prefs.cloud_storage_password, "")) {
appendTextToLog("Have credentials, let's see if they are valid");
if (!mgr)
mgr = new QNetworkAccessManager(this);
connect(mgr, &QNetworkAccessManager::authenticationRequired, this, &QMLManager::provideAuth, Qt::UniqueConnection);
connect(mgr, &QNetworkAccessManager::finished, this, execute, Qt::UniqueConnection);
QUrl url(CLOUDREDIRECTURL);
request = QNetworkRequest(url);
request.setRawHeader("User-Agent", getUserAgent().toUtf8());
request.setRawHeader("Accept", "text/html");
reply = mgr->get(request);
connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(handleError(QNetworkReply::NetworkError)));
connect(reply, &QNetworkReply::sslErrors, this, &QMLManager::handleSslErrors);
}
}
void QMLManager::tryRetrieveDataFromBackend()
{
checkCredentialsAndExecute(&QMLManager::retrieveUserid);
}
void QMLManager::loadDives()
{
checkCredentialsAndExecute(&QMLManager::loadDivesWithValidCredentials);
}
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");
setStartPageText(tr("No recorded dives found. You can download your dives to this device from the Subsurface cloud storage service, from your dive computer, or add them manually."));
reply->disconnect();
reply->abort();
reply->deleteLater();
return;
}
auth->setUser(prefs.cloud_storage_email);
auth->setPassword(prefs.cloud_storage_password);
}
void QMLManager::handleSslErrors(const QList<QSslError> &errors)
{
setStartPageText(tr("Cannot open cloud storage: Error creating https connection"));
Q_FOREACH(QSslError e, errors) {
qDebug() << e.errorString();
}
reply->abort();
reply->deleteLater();
}
void QMLManager::handleError(QNetworkReply::NetworkError nError)
{
QString errorString = reply->errorString();
qDebug() << "handleError" << nError << errorString;
setStartPageText(tr("Cannot open cloud storage: %1").arg(errorString));
reply->abort();
reply->deleteLater();
}
void QMLManager::retrieveUserid()
{
if (reply->attribute(QNetworkRequest::HttpStatusCodeAttribute) != 302) {
appendTextToLog(QString("Cloud storage connection not working correctly: ") + reply->readAll());
return;
}
QString userid(prefs.userid);
if (userid.isEmpty()) {
if (same_string(prefs.cloud_storage_email, "") || same_string(prefs.cloud_storage_password, "")) {
appendTextToLog("cloud user name or password are empty, can't retrieve web user id");
return;
}
appendTextToLog(QString("calling getUserid with user %1").arg(prefs.cloud_storage_email));
userid = locationProvider->getUserid(prefs.cloud_storage_email, prefs.cloud_storage_password);
}
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();
}
if (!loadFromCloud())
loadDivesWithValidCredentials();
}
void QMLManager::loadDiveProgress(int percent)
{
QString text(tr("Loading dive list from cloud storage."));
while(percent > 0) {
text.append(".");
percent -= 10;
}
setStartPageText(text);
}
void QMLManager::loadDivesWithValidCredentials()
{
if (reply->attribute(QNetworkRequest::HttpStatusCodeAttribute) != 302) {
appendTextToLog(QString("Cloud storage connection not working correctly: ") + reply->readAll());
setStartPageText(tr("Cannot connect to cloud storage"));
return;
}
appendTextToLog("Cloud credentials valid, loading dives...");
loadDiveProgress(0);
QString url;
if (getCloudURL(url)) {
QString errorString(get_error_string());
appendTextToLog(errorString);
setStartPageText(tr("Cloud storage error: %1").arg(errorString));
return;
}
clear_dive_file_data();
QByteArray fileNamePrt = QFile::encodeName(url);
int error = parse_file(fileNamePrt.data());
if (!error) {
report_error("filename is now %s", fileNamePrt.data());
const char *error_string = get_error_string();
appendTextToLog(error_string);
set_filename(fileNamePrt.data(), true);
} else {
report_error("failed to open file %s", fileNamePrt.data());
QString errorString(get_error_string());
appendTextToLog(errorString);
setStartPageText(tr("Cloud storage error: %1").arg(errorString));
return;
}
process_dives(false, false);
int i;
struct dive *d;
DiveListModel::instance()->clear();
for_each_dive(i, d) {
DiveListModel::instance()->addDive(d);
}
appendTextToLog(QString("%1 dives loaded").arg(i));
if (dive_table.nr == 0)
setStartPageText(tr("Cloud storage open successfully. No dives in dive list."));
setLoadFromCloud(true);
}
void QMLManager::commitChanges(QString diveId, QString location, QString gps, QString duration, QString depth,
QString airtemp, QString watertemp, QString suit, QString buddy, QString diveMaster, QString notes)
{
struct dive *d = get_dive_by_uniq_id(diveId.toInt());
qDebug() << diveId.toInt() << (d != 0 ? d->number : -1);
if (!d) {
qDebug() << "don't touch this... no dive";
return;
}
bool diveChanged = false;
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;
}
// now we need to handle the string representations of duration, depth, airtemp and watertemp
// and do something useful...
//
// FIXME
//
// TODO
if (!same_string(d->suit, qPrintable(suit))) {
diveChanged = true;
free(d->suit);
d->suit = strdup(qPrintable(suit));
}
if (!same_string(d->buddy, qPrintable(buddy))) {
diveChanged = true;
free(d->buddy);
d->buddy = strdup(qPrintable(buddy));
}
if (!same_string(d->divemaster, qPrintable(diveMaster))) {
diveChanged = true;
free(d->divemaster);
d->divemaster = strdup(qPrintable(diveMaster));
}
if (!same_string(d->notes, qPrintable(notes))) {
diveChanged = true;
free(d->notes);
d->notes = strdup(qPrintable(notes));
}
if (diveChanged) {
DiveListModel::instance()->updateDive(d);
mark_divelist_changed(true);
}
}
void QMLManager::saveChanges()
{
if (!loadFromCloud()) {
appendTextToLog("Don't save dives without loading from the cloud, first.");
return;
}
appendTextToLog("Saving dives.");
QString fileName;
if (getCloudURL(fileName)) {
appendTextToLog(get_error_string());
return;
}
if (save_dives(fileName.toUtf8().data())) {
appendTextToLog(get_error_string());
return;
}
appendTextToLog("Dive saved.");
set_filename(fileName.toUtf8().data(), true);
mark_divelist_changed(false);
}
QString QMLManager::addDive()
{
appendTextToLog("Adding new dive.");
return DiveListModel::instance()->startAddDive();
}
void QMLManager::applyGpsData()
{
locationProvider->applyLocations();
}
void QMLManager::sendGpsData()
{
locationProvider->uploadToServer();
}
void QMLManager::clearGpsData()
{
locationProvider->clearGpsData();
}
QString QMLManager::logText() const
{
QString logText = m_logText + QString("\nNumer of GPS fixes: %1").arg(locationProvider->getGpsNum());
return logText;
}
void QMLManager::setLogText(const QString &logText)
{
m_logText = logText;
emit logTextChanged();
}
void QMLManager::appendTextToLog(const QString &newText)
{
m_logText += "\n" + newText;
emit logTextChanged();
}
bool QMLManager::saveCloudPassword() const
{
return m_saveCloudPassword;
}
void QMLManager::setSaveCloudPassword(bool saveCloudPassword)
{
m_saveCloudPassword = saveCloudPassword;
}
bool QMLManager::locationServiceEnabled() const
{
return m_locationServiceEnabled;
}
void QMLManager::setLocationServiceEnabled(bool locationServiceEnabled)
{
m_locationServiceEnabled = locationServiceEnabled;
locationProvider->serviceEnable(m_locationServiceEnabled);
}
bool QMLManager::verboseEnabled() const
{
return m_verboseEnabled;
}
void QMLManager::setVerboseEnabled(bool verboseMode)
{
m_verboseEnabled = verboseMode;
verbose = verboseMode;
qDebug() << "verbose is" << verbose;
emit verboseEnabledChanged();
}
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)
{
m_cloudUserName = cloudUserName;
emit cloudUserNameChanged();
}
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();
}
bool QMLManager::loadFromCloud() const
{
return m_loadFromCloud;
}
void QMLManager::setLoadFromCloud(bool done)
{
m_loadFromCloud = done;
emit loadFromCloudChanged();
}
QString QMLManager::startPageText() const
{
return m_startPageText;
}
void QMLManager::setStartPageText(QString text)
{
m_startPageText = text;
emit startPageTextChanged();
}
void QMLManager::showMap(QString location)
{
if (!location.isEmpty()) {
QString link = QString("https://maps.googleapis.com/maps/api/staticmap?center=%1&zoom=15&size=600x400&maptype=satellite&markers=color:red|%2")
.arg(location).arg(location);
QDesktopServices::openUrl(link);
}
}
QString QMLManager::getNumber(QString diveId)
{
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;
}
QString QMLManager::getDate(QString diveId)
{
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;
}