summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGravatar Dirk Hohndel <dirk@hohndel.org>2016-04-30 12:40:52 -0700
committerGravatar Dirk Hohndel <dirk@hohndel.org>2016-04-30 12:40:52 -0700
commit241dd7cb81364521d78250f800e8508211575254 (patch)
tree1ea443fe4589f0f247db4304de38e857e18100f6
parent2e6588dc0ef8d7698cc5dbe831f524a6e394145d (diff)
parentba9288fab6d7aa8527f7a75a717cdc88a68facd9 (diff)
downloadsubsurface-241dd7cb81364521d78250f800e8508211575254.tar.gz
Merge branch 'offlineDefault'
-rw-r--r--android-mobile/AndroidManifest.xml1
-rw-r--r--core/file.c2
-rw-r--r--core/git-access.c19
-rw-r--r--core/git-access.h1
-rw-r--r--core/qt-init.cpp11
-rw-r--r--core/subsurface-qt/SettingsObjectWrapper.cpp7
-rw-r--r--core/subsurfacestartup.c12
-rw-r--r--core/subsurfacestartup.h2
-rw-r--r--mobile-widgets/qml/CloudCredentials.qml11
-rw-r--r--mobile-widgets/qml/DiveDetails.qml2
-rw-r--r--mobile-widgets/qml/DiveList.qml28
-rw-r--r--mobile-widgets/qml/StartPage.qml6
-rw-r--r--mobile-widgets/qml/icons/nocloud.svg71
-rw-r--r--mobile-widgets/qml/main.qml31
-rw-r--r--mobile-widgets/qml/mobile-resources.qrc2
-rw-r--r--mobile-widgets/qmlmanager.cpp162
-rw-r--r--mobile-widgets/qmlmanager.h7
-rw-r--r--subsurface-mobile-main.cpp29
18 files changed, 327 insertions, 77 deletions
diff --git a/android-mobile/AndroidManifest.xml b/android-mobile/AndroidManifest.xml
index 8fa0c5e83..87ef9d997 100644
--- a/android-mobile/AndroidManifest.xml
+++ b/android-mobile/AndroidManifest.xml
@@ -16,6 +16,7 @@
android:label="@string/app_name"
android:theme="@style/AppTheme"
android:launchMode="singleTop"
+ android:windowSoftInputMode="adjustResize"
android:screenOrientation="unspecified" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
diff --git a/core/file.c b/core/file.c
index 82eb684b0..4c0ae8fd0 100644
--- a/core/file.c
+++ b/core/file.c
@@ -485,7 +485,7 @@ int parse_file(const char *filename)
/* opening the cloud storage repository failed for some reason
* give up here and don't send errors about git repositories */
free(current_sha);
- return 0;
+ return -1;
}
/* if this is a git repository, do we already have this exact state loaded ?
* get the SHA and compare with what we currently have */
diff --git a/core/git-access.c b/core/git-access.c
index dba9cbec5..fe3a918ac 100644
--- a/core/git-access.c
+++ b/core/git-access.c
@@ -450,6 +450,11 @@ static int try_to_update(git_repository *repo, git_remote *origin, git_reference
return report_error("Unable to get local or remote SHA1");
}
if (git_merge_base(&base, repo, local_id, remote_id)) {
+ // TODO:
+ // if they have no merge base, they actually are different repos
+ // so instead merge this as merging a commit into a repo - git_merge() appears to do that
+ // but needs testing and cleanup afterwards
+ //
if (is_subsurface_cloud)
goto cloud_data_error;
else
@@ -940,3 +945,17 @@ struct git_repository *is_git_repository(const char *filename, const char **bran
*branchp = branch;
return repo;
}
+
+int git_create_local_repo(const char *filename)
+{
+ git_repository *repo;
+ char *path = strdup(filename);
+ char *branch = strchr(path, '[');
+ if (branch)
+ *branch = '\0';
+ int ret = git_repository_init(&repo, path, false);
+ free(path);
+ if (ret != 0)
+ (void)report_error("Create local repo failed with error code %d", ret);
+ return ret;
+}
diff --git a/core/git-access.h b/core/git-access.h
index f098f1e8d..b8b8181fe 100644
--- a/core/git-access.h
+++ b/core/git-access.h
@@ -27,6 +27,7 @@ extern void set_git_id(const struct git_oid *);
void set_git_update_cb(int(*)(bool, const char *));
int git_storage_update_progress(bool reset, const char *text);
char *get_local_dir(const char *remote, const char *branch);
+int git_create_local_repo(const char *filename);
extern int last_git_storage_update_val;
diff --git a/core/qt-init.cpp b/core/qt-init.cpp
index b52dfd970..5dc25f9fc 100644
--- a/core/qt-init.cpp
+++ b/core/qt-init.cpp
@@ -4,6 +4,8 @@
#include <QTextCodec>
#include "helpers.h"
+char *settings_suffix = NULL;
+
void init_qt_late()
{
QApplication *application = qApp;
@@ -19,7 +21,14 @@ void init_qt_late()
QCoreApplication::setOrganizationName("Subsurface");
QCoreApplication::setOrganizationDomain("subsurface.hohndel.org");
- QCoreApplication::setApplicationName("Subsurface");
+ // enable user specific settings (based on command line argument)
+ if (settings_suffix) {
+ if (verbose)
+ qDebug() << "using custom config for" << QString("Subsurface-%1").arg(settings_suffix);
+ QCoreApplication::setApplicationName(QString("Subsurface-%1").arg(settings_suffix));
+ } else {
+ QCoreApplication::setApplicationName("Subsurface");
+ }
// find plugins installed in the application directory (without this SVGs don't work on Windows)
QCoreApplication::addLibraryPath(QCoreApplication::applicationDirPath());
QLocale loc;
diff --git a/core/subsurface-qt/SettingsObjectWrapper.cpp b/core/subsurface-qt/SettingsObjectWrapper.cpp
index e43be1a9b..ed305ef28 100644
--- a/core/subsurface-qt/SettingsObjectWrapper.cpp
+++ b/core/subsurface-qt/SettingsObjectWrapper.cpp
@@ -1315,6 +1315,7 @@ int GeneralSettingsObjectWrapper::pscrRatio() const
void GeneralSettingsObjectWrapper::setDefaultFilename(const QString& value)
{
QSettings s;
+ s.beginGroup(group);
s.setValue("default_filename", value);
prefs.default_filename = copy_string(qPrintable(value));
emit defaultFilenameChanged(value);
@@ -1323,6 +1324,7 @@ void GeneralSettingsObjectWrapper::setDefaultFilename(const QString& value)
void GeneralSettingsObjectWrapper::setDefaultCylinder(const QString& value)
{
QSettings s;
+ s.beginGroup(group);
s.setValue("default_cylinder", value);
prefs.default_cylinder = copy_string(qPrintable(value));
emit defaultCylinderChanged(value);
@@ -1331,6 +1333,7 @@ void GeneralSettingsObjectWrapper::setDefaultCylinder(const QString& value)
void GeneralSettingsObjectWrapper::setDefaultFileBehavior(short value)
{
QSettings s;
+ s.beginGroup(group);
s.setValue("default_file_behavior", value);
prefs.default_file_behavior = value;
if (prefs.default_file_behavior == UNDEFINED_DEFAULT_FILE) {
@@ -1347,6 +1350,7 @@ void GeneralSettingsObjectWrapper::setDefaultFileBehavior(short value)
void GeneralSettingsObjectWrapper::setUseDefaultFile(bool value)
{
QSettings s;
+ s.beginGroup(group);
s.setValue("use_default_file", value);
prefs.use_default_file = value;
emit useDefaultFileChanged(value);
@@ -1355,6 +1359,7 @@ void GeneralSettingsObjectWrapper::setUseDefaultFile(bool value)
void GeneralSettingsObjectWrapper::setDefaultSetPoint(int value)
{
QSettings s;
+ s.beginGroup(group);
s.setValue("defaultsetpoint", value);
prefs.defaultsetpoint = value;
emit defaultSetPointChanged(value);
@@ -1363,6 +1368,7 @@ void GeneralSettingsObjectWrapper::setDefaultSetPoint(int value)
void GeneralSettingsObjectWrapper::setO2Consumption(int value)
{
QSettings s;
+ s.beginGroup(group);
s.setValue("o2consumption", value);
prefs.o2consumption = value;
emit o2ConsumptionChanged(value);
@@ -1371,6 +1377,7 @@ void GeneralSettingsObjectWrapper::setO2Consumption(int value)
void GeneralSettingsObjectWrapper::setPscrRatio(int value)
{
QSettings s;
+ s.beginGroup(group);
s.setValue("pscr_ratio", value);
prefs.pscr_ratio = value;
emit pscrRatioChanged(value);
diff --git a/core/subsurfacestartup.c b/core/subsurfacestartup.c
index 6e0dede1c..c2881a17f 100644
--- a/core/subsurfacestartup.c
+++ b/core/subsurfacestartup.c
@@ -10,6 +10,9 @@
struct preferences prefs, informational_prefs;
struct preferences default_prefs = {
.cloud_base_url = "https://cloud.subsurface-divelog.org/",
+#if defined(SUBSURFACE_MOBILE)
+ .git_local_only = true,
+#endif
.units = SI_UNITS,
.unit_system = METRIC,
.coordinates_traditional = true,
@@ -197,6 +200,11 @@ void parse_argument(const char *arg)
continue;
case '-':
/* long options with -- */
+ /* first test for --user=bla which allows the use of user specific settings */
+ if (strncmp(arg, "--user=", sizeof("--user=") - 1) == 0) {
+ settings_suffix = strdup(arg + sizeof("--user=") - 1);
+ return;
+ }
if (strcmp(arg, "--help") == 0) {
print_help();
exit(0);
@@ -254,8 +262,10 @@ void setup_system_prefs(void)
subsurface_OS_pref_setup();
default_prefs.divelist_font = strdup(system_divelist_default_font);
default_prefs.font_size = system_divelist_default_font_size;
- default_prefs.default_filename = system_default_filename();
+#if !defined(SUBSURFACE_MOBILE)
+ default_prefs.default_filename = system_default_filename();
+#endif
env = getenv("LC_MEASUREMENT");
if (!env)
env = getenv("LC_ALL");
diff --git a/core/subsurfacestartup.h b/core/subsurfacestartup.h
index 3ccc24aa4..53d44f28d 100644
--- a/core/subsurfacestartup.h
+++ b/core/subsurfacestartup.h
@@ -19,6 +19,8 @@ void free_prefs(void);
void copy_prefs(struct preferences *src, struct preferences *dest);
void print_files(void);
+extern char *settings_suffix;
+
#ifdef __cplusplus
}
#endif
diff --git a/mobile-widgets/qml/CloudCredentials.qml b/mobile-widgets/qml/CloudCredentials.qml
index d63227619..2bb42a6ed 100644
--- a/mobile-widgets/qml/CloudCredentials.qml
+++ b/mobile-widgets/qml/CloudCredentials.qml
@@ -23,6 +23,17 @@ Item {
id: outerLayout
width: loginWindow.width - loginWindow.leftPadding - loginWindow.rightPadding - 2 * Kirigami.Units.gridUnit
+ function goToNext() {
+ for (var i = 0; i < children.length; ++i)
+ if (children[i].focus) {
+ children[i].nextItemInFocusChain().forceActiveFocus()
+ break
+ }
+ }
+
+ Keys.onReturnPressed: goToNext()
+ Keys.onTabPressed: goToNext()
+
onVisibleChanged: {
if (visible && manager.accessingCloud < 0) {
manager.appendTextToLog("Credential scrn: show kbd was: " + (Qt.inputMethod.isVisible ? "visible" : "invisible"))
diff --git a/mobile-widgets/qml/DiveDetails.qml b/mobile-widgets/qml/DiveDetails.qml
index 3f42a1ed5..5735266f6 100644
--- a/mobile-widgets/qml/DiveDetails.qml
+++ b/mobile-widgets/qml/DiveDetails.qml
@@ -30,8 +30,6 @@ Kirigami.Page {
property alias gpsCheckbox: detailsEdit.gpsCheckbox
property int updateCurrentIdx: manager.updateSelectedDive
- property bool contentItem: true // HACK to work around Kirigami issue - remove once that's addressed upstream
-
title: diveDetailsListView.currentItem ? diveDetailsListView.currentItem.modelData.dive.location : "Dive details"
state: "view"
leftPadding: 0
diff --git a/mobile-widgets/qml/DiveList.qml b/mobile-widgets/qml/DiveList.qml
index 6ad3161e1..9ffb2ee88 100644
--- a/mobile-widgets/qml/DiveList.qml
+++ b/mobile-widgets/qml/DiveList.qml
@@ -211,18 +211,24 @@ Kirigami.ScrollablePage {
ScrollView {
id: startPageWrapper
anchors.fill: parent
- opacity: (diveListView.count > 0 && (credentialStatus == QMLManager.VALID || credentialStatus == QMLManager.VALID_EMAIL)) ? 0 : 1
+ opacity: credentialStatus === QMLManager.NOCLOUD || (diveListView.count > 0 && (credentialStatus === QMLManager.VALID || credentialStatus === QMLManager.VALID_EMAIL)) ? 0 : 1
visible: opacity > 0
Behavior on opacity { NumberAnimation { duration: Kirigami.Units.shortDuration } }
onVisibleChanged: {
+ print("startPageWrapper onVisibleChanged credentialStatus " + credentialStatus + " diveListView.count " + diveListView.count)
if (visible) {
page.actions.main = page.saveAction
+ page.actions.right = page.offlineAction
title = "Cloud credentials"
- } else if(manager.credentialStatus === QMLManager.VALID || manager.credentialStatus === QMLManager.VALID_EMAIL) {
+ } else if(manager.credentialStatus === QMLManager.VALID || manager.credentialStatus === QMLManager.VALID_EMAIL || manager.credentialStatus === QMLManager.NOCLOUD) {
page.actions.main = page.addDiveAction
+ page.actions.right = null
title = "Dive list"
+ if (diveListView.count === 0)
+ showPassiveNotification(qsTr("Please tap the '+' button to add a dive"), 3000)
} else {
page.actions.main = null
+ page.actions.right = null
title = "Dive list"
}
}
@@ -279,15 +285,25 @@ Kirigami.ScrollablePage {
}
}
+ property QtObject offlineAction: Action {
+ iconName: "qrc:/qml/nocloud.svg"
+ onTriggered: {
+ manager.syncToCloud = false
+ manager.credentialStatus = QMLManager.NOCLOUD
+ }
+ }
+
onBackRequested: {
- if (startPageWrapper.visible && diveListView.count > 0 && manager.credentialStatus != QMLManager.INVALID) {
+ if (startPageWrapper.visible && diveListView.count > 0 && manager.credentialStatus !== QMLManager.INVALID) {
manager.credentialStatus = oldStatus
event.accepted = true;
}
if (!startPageWrapper.visible) {
- manager.quit()
- // we shouldn't come back from there, but just in case
- event.accepted = true
+ if (Qt.platform.os != "ios") {
+ manager.quit()
+ // we shouldn't come back from there, but just in case
+ event.accepted = true
+ }
}
}
}
diff --git a/mobile-widgets/qml/StartPage.qml b/mobile-widgets/qml/StartPage.qml
index 41fe469c2..e9c785672 100644
--- a/mobile-widgets/qml/StartPage.qml
+++ b/mobile-widgets/qml/StartPage.qml
@@ -16,8 +16,10 @@ ColumnLayout {
Layout.fillWidth: true
Layout.margins: Kirigami.Units.gridUnit
Layout.topMargin: Kirigami.Units.gridUnit * 3
- text: "In order to use Subsurface-mobile you need to have a Subsurface cloud storage account " +
- "(which can be created with the Subsurface desktop application)."
+ text: "To use Subsurface-mobile with Subsurface cloud storage, please enter " +
+ "your cloud credentials.\n\n" +
+ "To use Subsurface-mobile only with local data on this device, tap " +
+ "on the no cloud icon below."
wrapMode: Text.WordWrap
}
Kirigami.Label {
diff --git a/mobile-widgets/qml/icons/nocloud.svg b/mobile-widgets/qml/icons/nocloud.svg
new file mode 100644
index 000000000..0661d32e6
--- /dev/null
+++ b/mobile-widgets/qml/icons/nocloud.svg
@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ viewBox="0 0 22 22"
+ id="svg2"
+ version="1.1"
+ inkscape:version="0.91 r13725"
+ sodipodi:docname="nocloud.svg">
+ <metadata
+ id="metadata10">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1022"
+ inkscape:window-height="740"
+ id="namedview8"
+ showgrid="false"
+ inkscape:zoom="10.727273"
+ inkscape:cx="-2.8898305"
+ inkscape:cy="11"
+ inkscape:window-x="0"
+ inkscape:window-y="0"
+ inkscape:window-maximized="0"
+ inkscape:current-layer="svg2" />
+ <defs
+ id="defs3051">
+ <style
+ type="text/css"
+ id="current-color-scheme">
+ .ColorScheme-Text {
+ color:#4d4d4d;
+ }
+ </style>
+ </defs>
+ <path
+ style="fill:currentColor;fill-opacity:1;stroke:none"
+ d="M 11 4 A 6 6 0 0 0 5 10 A 6 6 0 0 0 5.0039062 10.128906 A 4 4 0 0 0 2 14 A 4 4 0 0 0 6 18 L 15 18 A 5 5 0 0 0 20 13 A 5 5 0 0 0 16.757812 8.3242188 A 6 6 0 0 0 11 4 z M 11 5 A 5 5 0 0 1 15.919922 9.1113281 A 4.0000019 4.0000019 0 0 1 19 13 A 4.0000019 4.0000019 0 0 1 15 17 L 6 17 A 2.9999979 2.9999979 0 0 1 3 14 A 2.9999979 2.9999979 0 0 1 6 11 A 2.9999979 2.9999979 0 0 1 6.1074219 11.005859 A 5 5 0 0 1 6 10 A 5 5 0 0 1 11 5 z "
+ class="ColorScheme-Text"
+ id="path6" />
+ <path
+ style="opacity:1;fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:3.20000005;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="M 4.0550844,20.042373 18.877119,4.0084751"
+ id="path3338-1"
+ inkscape:connector-curvature="0" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#4d4d4d;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;opacity:1"
+ d="M 3.8220339,19.855932 18.644068,3.8220339"
+ id="path3338"
+ inkscape:connector-curvature="0" />
+</svg>
diff --git a/mobile-widgets/qml/main.qml b/mobile-widgets/qml/main.qml
index e1a140635..b4ab897e9 100644
--- a/mobile-widgets/qml/main.qml
+++ b/mobile-widgets/qml/main.qml
@@ -124,7 +124,6 @@ Kirigami.ApplicationWindow {
},
Kirigami.Action {
text: "Manage dives"
- enabled: manager.credentialStatus === QMLManager.VALID || manager.credentialStatus === QMLManager.VALID_EMAIL
/*
* disable for the beta to avoid confusion
Action {
@@ -137,6 +136,7 @@ Kirigami.ApplicationWindow {
*/
Kirigami.Action {
text: "Add dive manually"
+ enabled: manager.credentialStatus === QMLManager.VALID || manager.credentialStatus === QMLManager.VALID_EMAIL || manager.credentialStatus === QMLManager.NOCLOUD
onTriggered: {
returnTopPage() // otherwise odd things happen with the page stack
startAddDive()
@@ -144,24 +144,34 @@ Kirigami.ApplicationWindow {
}
Kirigami.Action {
text: "Manual sync with cloud"
+ enabled: manager.credentialStatus === QMLManager.VALID || manager.credentialStatus === QMLManager.VALID_EMAIL || manager.credentialStatus === QMLManager.NOCLOUD
onTriggered: {
- globalDrawer.close()
- detailsWindow.endEditMode()
- manager.saveChangesCloud(true);
- globalDrawer.close()
+ if (manager.credentialStatus === QMLManager.NOCLOUD) {
+ returnTopPage()
+ oldStatus = manager.credentialStatus
+ manager.startPageText = "Enter valid cloud storage credentials"
+ manager.credentialStatus = QMLManager.UNKNOWN
+ globalDrawer.close()
+ } else {
+ globalDrawer.close()
+ detailsWindow.endEditMode()
+ manager.saveChangesCloud(true);
+ globalDrawer.close()
+ }
}
}
Kirigami.Action {
- text: syncToCloud ? "Disable auto cloud sync" : "Enable auto cloud sync"
+ text: syncToCloud ? "Offline mode" : "Enable auto cloud sync"
+ enabled: manager.credentialStatus !== QMLManager.NOCLOUD
onTriggered: {
syncToCloud = !syncToCloud
if (!syncToCloud) {
var alertText = "Turning off automatic sync to cloud causes all data\n"
- alertText +=" to only be stored locally.\n"
+ alertText +="to only be stored locally.\n"
alertText += "This can be very useful in situations with\n"
- alertText += " limited or no network access.\n"
+ alertText += "limited or no network access.\n"
alertText += "Please chose 'Manual sync with cloud' if you have network\n"
- alertText += " connectivity and want to sync your data to cloud storage."
+ alertText += "connectivity and want to sync your data to cloud storage."
showPassiveNotification(alertText, 10000)
}
}
@@ -326,8 +336,7 @@ Kirigami.ApplicationWindow {
DiveDetails {
id: detailsWindow
visible: false
- width: parent.width
- height: parent.height
+ anchors.fill: parent
}
DownloadFromDiveComputer {
diff --git a/mobile-widgets/qml/mobile-resources.qrc b/mobile-widgets/qml/mobile-resources.qrc
index 6157e3bf6..ab556789b 100644
--- a/mobile-widgets/qml/mobile-resources.qrc
+++ b/mobile-widgets/qml/mobile-resources.qrc
@@ -21,6 +21,7 @@
<file alias="context-menu.png">icons/context-menu.png</file>
<file alias="menu-edit.png">icons/menu-edit.png</file>
<file alias="menu-back.png">icons/menu-back.png</file>
+ <file alias="nocloud.svg">icons/nocloud.svg</file>
</qresource>
<qresource prefix="/imports">
<file alias="org/kde/kirigami/qmldir">kirigami/qmldir</file>
@@ -48,6 +49,7 @@
<file alias="org/kde/kirigami/private/ActionButton.qml">kirigami/private/ActionButton.qml</file>
<file alias="org/kde/kirigami/private/BackButton.qml">kirigami/private/BackButton.qml</file>
<file alias="org/kde/kirigami/private/ContextIcon.qml">kirigami/private/ContextIcon.qml</file>
+ <file alias="org/kde/kirigami/private/DefaultListItemBackground.qml">kirigami/private/DefaultListItemBackground.qml</file>
<file alias="org/kde/kirigami/private/EdgeShadow.qml">kirigami/private/EdgeShadow.qml</file>
<file alias="org/kde/kirigami/private/MenuIcon.qml">kirigami/private/MenuIcon.qml</file>
<file alias="org/kde/kirigami/private/DefaultListItemBackground.qml">kirigami/private/DefaultListItemBackground.qml</file>
diff --git a/mobile-widgets/qmlmanager.cpp b/mobile-widgets/qmlmanager.cpp
index c2c60ce5c..82ea319bb 100644
--- a/mobile-widgets/qmlmanager.cpp
+++ b/mobile-widgets/qmlmanager.cpp
@@ -19,12 +19,16 @@
#include "core/qt-gui.h"
#include "core/git-access.h"
#include "core/cloudstorage.h"
+#include "core/subsurface-qt/SettingsObjectWrapper.h"
+#include "core/membuffer.h"
QMLManager *QMLManager::m_instance = NULL;
#define RED_FONT QLatin1Literal("<font color=\"red\">")
#define END_FONT QLatin1Literal("</font>")
+#define NOCLOUD_LOCALSTORAGE format_string("%s/cloudstorage/localrepo[master]", system_default_directory())
+
static void appendTextToLogStandalone(const char *text)
{
QMLManager *self = QMLManager::instance();
@@ -153,6 +157,11 @@ void QMLManager::openLocalThenRemote(QString url)
DiveListModel::instance()->addAllDives();
appendTextToLog(QStringLiteral("%1 dives loaded from cache").arg(dive_table.nr));
}
+ if (oldStatus() == NOCLOUD) {
+ // if we switch to credentials from NOCLOUD, we take things online temporarily
+ prefs.git_local_only = false;
+ appendTextToLog(QStringLiteral("taking things online to be able to switch to cloud account"));
+ }
set_filename(fileNamePrt.data(), true);
if (prefs.git_local_only) {
appendTextToLog(QStringLiteral("have cloud credentials, but user asked not to connect to network"));
@@ -163,6 +172,13 @@ void QMLManager::openLocalThenRemote(QString url)
}
}
+void QMLManager::mergeLocalRepo()
+{
+ char *filename = NOCLOUD_LOCALSTORAGE;
+ parse_file(filename);
+ process_dives(true, false);
+}
+
void QMLManager::finishSetup()
{
// Initialize cloud credentials.
@@ -178,9 +194,23 @@ void QMLManager::finishSetup()
// but we need to make sure we stay the only ones accessing git storage
alreadySaving = true;
openLocalThenRemote(url);
+ } else if (!same_string(existing_filename, "")) {
+ setCredentialStatus(NOCLOUD);
+ appendTextToLog(tr("working in no-cloud mode"));
+ int error = parse_file(existing_filename);
+ if (error) {
+ // we got an error loading the local file
+ appendTextToLog(QString("got error %2 when parsing file %1").arg(existing_filename, get_error_string()));
+ set_filename(NULL, "");
+ } else {
+ // successfully opened the local file, now add thigs to the dive list
+ consumeFinishedLoad(0);
+ setAccessingCloud(-1);
+ appendTextToLog(QString("working in no-cloud mode, finished loading %1 dives from %2").arg(dive_table.nr).arg(existing_filename));
+ }
} else {
setCredentialStatus(INCOMPLETE);
- appendTextToLog(QStringLiteral("no cloud credentials"));
+ appendTextToLog(tr("no cloud credentials"));
setStartPageText(RED_FONT + tr("Please enter valid cloud credentials.") + END_FONT);
}
setDistanceThreshold(prefs.distance_threshold);
@@ -231,6 +261,7 @@ void QMLManager::saveCloudCredentials()
// just go back to the dive list
setCredentialStatus(oldStatus());
}
+
if (!same_string(prefs.cloud_storage_password, qPrintable(cloudPassword()))) {
free(prefs.cloud_storage_password);
prefs.cloud_storage_password = strdup(qPrintable(cloudPassword()));
@@ -238,6 +269,9 @@ void QMLManager::saveCloudCredentials()
if (cloudUserName().isEmpty() || cloudPassword().isEmpty()) {
setStartPageText(RED_FONT + tr("Please enter valid cloud credentials.") + END_FONT);
} else if (cloudCredentialsChanged) {
+ // let's make sure there are no unsaved changes
+ saveChangesLocal();
+
free(prefs.userid);
prefs.userid = NULL;
syncLoadFromCloud();
@@ -252,6 +286,10 @@ void QMLManager::saveCloudCredentials()
// 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;
+ // since we changed credentials, we need to try to connect to the cloud, regardless
+ // of whether we're in offline mode or not, to make sure the repository is synced
+ currentGitLocalOnly = prefs.git_local_only;
+ prefs.git_local_only = false;
openLocalThenRemote(url);
}
}
@@ -268,7 +306,6 @@ void QMLManager::checkCredentialsAndExecute(execute_function_type execute)
CloudStorageAuthenticate *csa = new CloudStorageAuthenticate(this);
csa->backend(prefs.cloud_storage_email, prefs.cloud_storage_password);
connect(manager(), &QNetworkAccessManager::authenticationRequired, this, &QMLManager::provideAuth, Qt::UniqueConnection);
- connect(manager(), &QNetworkAccessManager::finished, this, execute, Qt::UniqueConnection);
QUrl url(CLOUDREDIRECTURL);
request = QNetworkRequest(url);
request.setRawHeader("User-Agent", getUserAgent().toUtf8());
@@ -276,6 +313,7 @@ void QMLManager::checkCredentialsAndExecute(execute_function_type execute)
reply = manager()->get(request);
connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(handleError(QNetworkReply::NetworkError)));
connect(reply, &QNetworkReply::sslErrors, this, &QMLManager::handleSslErrors);
+ connect(reply, &QNetworkReply::finished, this, execute, Qt::UniqueConnection);
}
}
@@ -325,10 +363,11 @@ void QMLManager::handleError(QNetworkReply::NetworkError nError)
void QMLManager::retrieveUserid()
{
if (reply->attribute(QNetworkRequest::HttpStatusCodeAttribute) != 302) {
- appendTextToLog(QStringLiteral("Cloud storage connection not working correctly: %1").arg(QString(reply->readAll())));
+ appendTextToLog(QStringLiteral("Cloud storage connection not working correctly: (%1) %2")
+ .arg(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt())
+ .arg(QString(reply->readAll())));
setStartPageText(RED_FONT + tr("Cannot connect to cloud storage") + END_FONT);
- setAccessingCloud(-1);
- alreadySaving = false;
+ revertToNoCloudIfNeeded();
return;
}
setCredentialStatus(VALID);
@@ -336,8 +375,7 @@ void QMLManager::retrieveUserid()
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");
- setAccessingCloud(-1);
- alreadySaving = false;
+ revertToNoCloudIfNeeded();
return;
}
appendTextToLog(QStringLiteral("calling getUserid with user %1").arg(prefs.cloud_storage_email));
@@ -366,12 +404,12 @@ void QMLManager::loadDiveProgress(int percent)
void QMLManager::loadDivesWithValidCredentials()
{
QString url;
+ timestamp_t currentDiveTimestamp = selectedDiveTimestamp();
if (getCloudURL(url)) {
QString errorString(get_error_string());
appendTextToLog(errorString);
setStartPageText(RED_FONT + tr("Cloud storage error: %1").arg(errorString) + END_FONT);
- setAccessingCloud(-1);
- alreadySaving = false;
+ revertToNoCloudIfNeeded();
return;
}
QByteArray fileNamePrt = QFile::encodeName(url);
@@ -381,13 +419,9 @@ void QMLManager::loadDivesWithValidCredentials()
if (check_git_sha(fileNamePrt.data(), &git, &branch) == 0) {
qDebug() << "local cache was current, no need to modify dive list";
appendTextToLog("Cloud sync shows local cache was current");
- setLoadFromCloud(true);
- setAccessingCloud(-1);
- alreadySaving = false;
- return;
+ goto successful_exit;
}
appendTextToLog("Cloud sync brought newer data, reloading the dive list");
- timestamp_t currentDiveTimestamp = selectedDiveTimestamp();
clear_dive_file_data();
if (git != dummy_git_repository) {
@@ -407,10 +441,71 @@ void QMLManager::loadDivesWithValidCredentials()
report_error("failed to open file %s", fileNamePrt.data());
QString errorString(get_error_string());
appendTextToLog(errorString);
- setStartPageText(RED_FONT + tr("Cloud storage error: %1").arg(errorString) + END_FONT);
- alreadySaving = false;
+ revertToNoCloudIfNeeded();
return;
}
+ consumeFinishedLoad(currentDiveTimestamp);
+
+successful_exit:
+ alreadySaving = false;
+ setLoadFromCloud(true);
+ // if we came from local storage mode, let's merge the local data into the local cache
+ // for the remote data - which then later gets merged with the remote data if necessary
+ if (oldStatus() == NOCLOUD) {
+ git_storage_update_progress(false, "import dives from nocloud local storage");
+ dive_table.preexisting = dive_table.nr;
+ mergeLocalRepo();
+ DiveListModel::instance()->clear();
+ DiveListModel::instance()->addAllDives();
+ appendTextToLog(QStringLiteral("%1 dives loaded after importing nocloud local storage").arg(dive_table.nr));
+ saveChangesLocal();
+ if (syncToCloud() == false) {
+ appendTextToLog(QStringLiteral("taking things back offline now that storage is synced"));
+ prefs.git_local_only = syncToCloud();
+ }
+ }
+ setAccessingCloud(-1);
+ // if we got here just for an initial connection to the cloud, reset to offline
+ if (currentGitLocalOnly) {
+ currentGitLocalOnly = false;
+ prefs.git_local_only = true;
+ }
+ return;
+}
+
+void QMLManager::revertToNoCloudIfNeeded()
+{
+ if (currentGitLocalOnly) {
+ // we tried to connect to the cloud for the first time and that failed
+ currentGitLocalOnly = false;
+ prefs.git_local_only = true;
+ }
+ if (oldStatus() == NOCLOUD) {
+ // we tried to switch to a cloud account and had previously used local data,
+ // but connecting to the cloud account (and subsequently merging the local
+ // and cloud data) failed - so let's delete the cloud credentials and go
+ // back to NOCLOUD mode in order to prevent us from losing the locally stored
+ // dives
+ if (syncToCloud() == false) {
+ appendTextToLog(QStringLiteral("taking things back offline since sync with cloud failed"));
+ prefs.git_local_only = syncToCloud();
+ }
+ free(prefs.cloud_storage_email);
+ prefs.cloud_storage_email = NULL;
+ free(prefs.cloud_storage_password);
+ prefs.cloud_storage_password = NULL;
+ setCloudUserName("");
+ setCloudPassword("");
+ setCredentialStatus(INCOMPLETE);
+ set_filename(NOCLOUD_LOCALSTORAGE, true);
+ setStartPageText(RED_FONT + tr("Failed to connect to cloud server, reverting to no cloud status") + END_FONT);
+ }
+ setAccessingCloud(-1);
+ alreadySaving = false;
+}
+
+void QMLManager::consumeFinishedLoad(timestamp_t currentDiveTimestamp)
+{
prefs.unit_system = informational_prefs.unit_system;
if (informational_prefs.unit_system == IMPERIAL)
informational_prefs.units = IMPERIAL_units;
@@ -420,12 +515,9 @@ void QMLManager::loadDivesWithValidCredentials()
DiveListModel::instance()->addAllDives();
if (currentDiveTimestamp)
setUpdateSelectedDive(dlSortModel->getIdxForId(get_dive_id_closest_to(currentDiveTimestamp)));
- else
- setUpdateSelectedDive(0);
appendTextToLog(QStringLiteral("%1 dives loaded").arg(dive_table.nr));
if (dive_table.nr == 0)
setStartPageText(tr("Cloud storage open successfully. No dives in dive list."));
- setLoadFromCloud(true);
alreadySaving = false;
}
@@ -782,11 +874,10 @@ void QMLManager::changesNeedSaving()
// 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)
+#if defined(Q_OS_ANDROID)
saveChangesLocal();
-#else
+#elif !defined(Q_OS_IOS)
saveChangesCloud(false);
#endif
}
@@ -794,7 +885,18 @@ void QMLManager::saveChangesLocal()
{
if (unsaved_changes()) {
git_storage_update_progress(true, "saving dives locally"); // reset the timers
- if (!loadFromCloud()) {
+ if (credentialStatus() == NOCLOUD) {
+ if (same_string(existing_filename, "")) {
+ char *filename = NOCLOUD_LOCALSTORAGE;
+ if (git_create_local_repo(filename))
+ appendTextToLog(get_error_string());
+ set_filename(filename, true);
+ GeneralSettingsObjectWrapper s(this);
+ s.setDefaultFilename(filename);
+ s.setDefaultFileBehavior(LOCAL_DEFAULT_FILE);
+ qDebug() << "setting default file to" << filename;
+ }
+ } else 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.");
@@ -809,6 +911,7 @@ void QMLManager::saveChangesLocal()
prefs.git_local_only = true;
if (save_dives(existing_filename)) {
appendTextToLog(get_error_string());
+ set_filename(NULL, true);
setAccessingCloud(-1);
prefs.git_local_only = glo;
alreadySaving = false;
@@ -825,14 +928,10 @@ void QMLManager::saveChangesLocal()
void QMLManager::saveChangesCloud(bool forceRemoteSync)
{
- if (!unsaved_changes()) {
+ if (!unsaved_changes() && !forceRemoteSync) {
appendTextToLog("asked to save changes but no unsaved changes");
return;
}
- if (!loadFromCloud()) {
- appendTextToLog("Don't save dives without loading from the cloud, first.");
- return;
- }
if (alreadySaving) {
appendTextToLog("save operation in progress already");
return;
@@ -844,6 +943,11 @@ void QMLManager::saveChangesCloud(bool forceRemoteSync)
if (prefs.git_local_only && !forceRemoteSync)
return;
+ if (!loadFromCloud()) {
+ appendTextToLog("Don't save dives without loading from the cloud, first.");
+ return;
+ }
+
bool glo = prefs.git_local_only;
git_storage_update_progress(false, "start save change to cloud");
prefs.git_local_only = false;
@@ -1119,6 +1223,8 @@ QMLManager::credentialStatus_t QMLManager::credentialStatus() const
void QMLManager::setCredentialStatus(const credentialStatus_t value)
{
if (m_credentialStatus != value) {
+ if (value == NOCLOUD)
+ appendTextToLog("Switching to no cloud mode");
m_credentialStatus = value;
emit credentialStatusChanged();
}
diff --git a/mobile-widgets/qmlmanager.h b/mobile-widgets/qmlmanager.h
index ece2c7b59..906e7b0b2 100644
--- a/mobile-widgets/qmlmanager.h
+++ b/mobile-widgets/qmlmanager.h
@@ -39,7 +39,8 @@ public:
UNKNOWN,
INVALID,
VALID_EMAIL,
- VALID
+ VALID,
+ NOCLOUD
};
static QMLManager *instance();
@@ -127,11 +128,14 @@ public slots:
void clearGpsData();
void finishSetup();
void openLocalThenRemote(QString url);
+ void mergeLocalRepo();
QString getNumber(const QString& diveId);
QString getDate(const QString& diveId);
QString getCurrentPosition();
QString getVersion() const;
void deleteGpsFix(quint64 when);
+ void revertToNoCloudIfNeeded();
+ void consumeFinishedLoad(timestamp_t currentDiveTimestamp);
void refreshDiveList();
void screenChanged(QScreen *screen);
qreal lastDevicePixelRatio();
@@ -170,6 +174,7 @@ private:
bool checkLocation(DiveObjectHelper *myDive, struct dive *d, QString location, QString gps);
bool checkDuration(DiveObjectHelper *myDive, struct dive *d, QString duration);
bool checkDepth(DiveObjectHelper *myDive, struct dive *d, QString depth);
+ bool currentGitLocalOnly;
signals:
void cloudUserNameChanged();
diff --git a/subsurface-mobile-main.cpp b/subsurface-mobile-main.cpp
index e5965090b..b7259c44a 100644
--- a/subsurface-mobile-main.cpp
+++ b/subsurface-mobile-main.cpp
@@ -22,12 +22,9 @@ QTranslator *qtTranslator, *ssrfTranslator;
int main(int argc, char **argv)
{
int i;
- bool no_filenames = true;
QLoggingCategory::setFilterRules(QStringLiteral("qt.bluetooth* = true"));
QApplication *application = new QApplication(argc, argv);
(void)application;
- QStringList files;
- QStringList importedFiles;
QStringList arguments = QCoreApplication::arguments();
bool dedicated_console = arguments.length() > 1 &&
@@ -40,18 +37,8 @@ int main(int argc, char **argv)
parse_argument(a.toLocal8Bit().data());
continue;
}
- if (imported) {
- importedFiles.push_back(a);
- } else {
- no_filenames = false;
- files.push_back(a);
- }
}
-#if !LIBGIT2_VER_MAJOR && LIBGIT2_VER_MINOR < 22
- git_threads_init();
-#else
git_libgit2_init();
-#endif
setup_system_prefs();
if (uiLanguage(0).contains("-US"))
default_prefs.units = IMPERIAL_units;
@@ -61,6 +48,11 @@ int main(int argc, char **argv)
taglist_init_global();
init_ui();
loadPreferences();
+ if (prefs.default_file_behavior == LOCAL_DEFAULT_FILE)
+ set_filename(prefs.default_filename, true);
+ else
+ set_filename(NULL, true);
+
// some hard coded settings
prefs.animation_speed = 0; // we render the profile to pixmap, no animations
@@ -69,17 +61,6 @@ int main(int argc, char **argv)
prefs.redceiling = 1;
init_proxy();
- if (no_filenames) {
- if (prefs.default_file_behavior == LOCAL_DEFAULT_FILE) {
- QString defaultFile(prefs.default_filename);
- if (!defaultFile.isEmpty())
- files.push_back(QString(prefs.default_filename));
- } else if (prefs.default_file_behavior == CLOUD_DEFAULT_FILE) {
- QString cloudURL;
- if (getCloudURL(cloudURL) == 0)
- files.push_back(cloudURL);
- }
- }
if (!quit)
run_ui();