From f3929af0e4f38915641033be33645e10609c5547 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Thu, 21 Apr 2016 11:39:37 -0700 Subject: [PATCH 01/38] QML UI: add new Kirigami file Signed-off-by: Dirk Hohndel --- mobile-widgets/qml/mobile-resources.qrc | 1 + 1 file changed, 1 insertion(+) diff --git a/mobile-widgets/qml/mobile-resources.qrc b/mobile-widgets/qml/mobile-resources.qrc index 0592c1119..a02896c59 100644 --- a/mobile-widgets/qml/mobile-resources.qrc +++ b/mobile-widgets/qml/mobile-resources.qrc @@ -46,6 +46,7 @@ kirigami/private/ActionButton.qml kirigami/private/BackButton.qml kirigami/private/ContextIcon.qml + kirigami/private/DefaultListItemBackground.qml kirigami/private/EdgeShadow.qml kirigami/private/MenuIcon.qml kirigami/private/PageStack.js From 68bb49e4ca26d818c5199de0da1002c82281d645 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Thu, 21 Apr 2016 11:39:04 -0700 Subject: [PATCH 02/38] Fix Android keyboard issue as recommended by Kirigami Signed-off-by: Dirk Hohndel --- android-mobile/AndroidManifest.xml | 1 + 1 file changed, 1 insertion(+) 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" > From 85560531da8205feca045305843b704f497a3d05 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Thu, 21 Apr 2016 12:14:37 -0700 Subject: [PATCH 03/38] QML UI: don't exit on iOS Apparently users interpret that as a crash. Signed-off-by: Dirk Hohndel --- mobile-widgets/qml/DiveList.qml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/mobile-widgets/qml/DiveList.qml b/mobile-widgets/qml/DiveList.qml index 73bb06a86..e5ce929d2 100644 --- a/mobile-widgets/qml/DiveList.qml +++ b/mobile-widgets/qml/DiveList.qml @@ -282,9 +282,11 @@ Kirigami.ScrollablePage { 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 + } } } } From 4e62bfa9a9cb9b715769189a292f655262157333 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Thu, 21 Apr 2016 14:00:37 -0700 Subject: [PATCH 04/38] QML UI: allow changing input fields with tab/return key This assumes a physical keyboard (e.g., iPad with BT keyboard). Signed-off-by: Dirk Hohndel --- mobile-widgets/qml/CloudCredentials.qml | 11 +++++++++++ 1 file changed, 11 insertions(+) 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")) From 6af77b08b2a37cdc61cded2a4b6c449485107180 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Thu, 21 Apr 2016 04:43:37 -0700 Subject: [PATCH 05/38] QML UI: rename menu entry for offline use That really is what it boils down to. Also removes a few stray spaces in the warning text. Signed-off-by: Dirk Hohndel --- mobile-widgets/qml/main.qml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mobile-widgets/qml/main.qml b/mobile-widgets/qml/main.qml index 06e691eee..372de1f27 100644 --- a/mobile-widgets/qml/main.qml +++ b/mobile-widgets/qml/main.qml @@ -150,16 +150,16 @@ Kirigami.ApplicationWindow { } } Kirigami.Action { - text: syncToCloud ? "Disable auto cloud sync" : "Enable auto cloud sync" + text: syncToCloud ? "Offline mode" : "Enable auto cloud sync" 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) } } From a5af2478d47e70097404ef93f780338483826ad6 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Thu, 21 Apr 2016 04:46:22 -0700 Subject: [PATCH 06/38] QML UI: reword the intro text to mention offline mode Signed-off-by: Dirk Hohndel --- mobile-widgets/qml/StartPage.qml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/mobile-widgets/qml/StartPage.qml b/mobile-widgets/qml/StartPage.qml index 41fe469c2..980e0921c 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, select " + + "Offline mode in the Manage dives entry in the main menu." wrapMode: Text.WordWrap } Kirigami.Label { From be30724ba0eeaba214c099921579f2023d2a9a7a Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Thu, 21 Apr 2016 04:52:47 -0700 Subject: [PATCH 07/38] QML UI: allow switching to offline mode without valid credentials If the user wants to work offline, they don't need validated credentials. Signed-off-by: Dirk Hohndel --- mobile-widgets/qml/main.qml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mobile-widgets/qml/main.qml b/mobile-widgets/qml/main.qml index 372de1f27..858e9d003 100644 --- a/mobile-widgets/qml/main.qml +++ b/mobile-widgets/qml/main.qml @@ -122,7 +122,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 { @@ -135,6 +134,7 @@ Kirigami.ApplicationWindow { */ Kirigami.Action { text: "Add dive manually" + enabled: manager.credentialStatus === QMLManager.VALID || manager.credentialStatus === QMLManager.VALID_EMAIL onTriggered: { returnTopPage() // otherwise odd things happen with the page stack startAddDive() @@ -142,6 +142,7 @@ Kirigami.ApplicationWindow { } Kirigami.Action { text: "Manual sync with cloud" + enabled: manager.credentialStatus === QMLManager.VALID || manager.credentialStatus === QMLManager.VALID_EMAIL onTriggered: { globalDrawer.close() detailsWindow.endEditMode() From 34628f8e6d2156d7eae9bfbb64d5b43ca58e1a34 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Thu, 21 Apr 2016 05:21:56 -0700 Subject: [PATCH 08/38] QML UI: add option to disable cloud to start screen Signed-off-by: Dirk Hohndel --- mobile-widgets/qml/DiveList.qml | 8 +++ mobile-widgets/qml/icons/nocloud.svg | 71 +++++++++++++++++++++++++ mobile-widgets/qml/mobile-resources.qrc | 1 + 3 files changed, 80 insertions(+) create mode 100644 mobile-widgets/qml/icons/nocloud.svg diff --git a/mobile-widgets/qml/DiveList.qml b/mobile-widgets/qml/DiveList.qml index e5ce929d2..355e0ba34 100644 --- a/mobile-widgets/qml/DiveList.qml +++ b/mobile-widgets/qml/DiveList.qml @@ -217,6 +217,7 @@ Kirigami.ScrollablePage { onVisibleChanged: { 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) { page.actions.main = page.addDiveAction @@ -276,6 +277,13 @@ Kirigami.ScrollablePage { } } + property QtObject offlineAction: Action { + iconName: "qrc:/qml/nocloud.svg" + onTriggered: { + manager.syncToCloud = false + } + } + onBackRequested: { if (startPageWrapper.visible && diveListView.count > 0 && manager.credentialStatus != QMLManager.INVALID) { manager.credentialStatus = oldStatus 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 @@ + + + + + + image/svg+xml + + + + + + + + + + + + diff --git a/mobile-widgets/qml/mobile-resources.qrc b/mobile-widgets/qml/mobile-resources.qrc index a02896c59..4fe82a66a 100644 --- a/mobile-widgets/qml/mobile-resources.qrc +++ b/mobile-widgets/qml/mobile-resources.qrc @@ -21,6 +21,7 @@ icons/context-menu.png icons/menu-edit.png icons/menu-back.png + icons/nocloud.svg kirigami/qmldir From 6b43b3015dcfff3c0c535e9fee69e2011feeda17 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Fri, 22 Apr 2016 05:05:57 -0700 Subject: [PATCH 09/38] QML UI: turn off 'no cloud' button when not in use This needs to be actively cleared. Signed-off-by: Dirk Hohndel --- mobile-widgets/qml/DiveList.qml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mobile-widgets/qml/DiveList.qml b/mobile-widgets/qml/DiveList.qml index 355e0ba34..b9a75abb4 100644 --- a/mobile-widgets/qml/DiveList.qml +++ b/mobile-widgets/qml/DiveList.qml @@ -221,9 +221,11 @@ Kirigami.ScrollablePage { title = "Cloud credentials" } else if(manager.credentialStatus === QMLManager.VALID || manager.credentialStatus === QMLManager.VALID_EMAIL) { page.actions.main = page.addDiveAction + page.actions.right = null title = "Dive list" } else { page.actions.main = null + page.actions.right = null title = "Dive list" } } From 27fccbbe4ea21ef4723ded4e01d5ea0df67fb61f Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Fri, 22 Apr 2016 05:19:34 -0700 Subject: [PATCH 10/38] QML UI: when chosing 'no cloud', switch to dive list Currently we don't remember that we picked 'no cloud' across restarts. Signed-off-by: Dirk Hohndel --- mobile-widgets/qml/DiveList.qml | 7 +++++-- mobile-widgets/qmlmanager.h | 3 ++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/mobile-widgets/qml/DiveList.qml b/mobile-widgets/qml/DiveList.qml index b9a75abb4..98a7a260f 100644 --- a/mobile-widgets/qml/DiveList.qml +++ b/mobile-widgets/qml/DiveList.qml @@ -211,7 +211,7 @@ 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: { @@ -219,10 +219,12 @@ Kirigami.ScrollablePage { 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 @@ -283,6 +285,7 @@ Kirigami.ScrollablePage { iconName: "qrc:/qml/nocloud.svg" onTriggered: { manager.syncToCloud = false + manager.credentialStatus = QMLManager.NOCLOUD } } diff --git a/mobile-widgets/qmlmanager.h b/mobile-widgets/qmlmanager.h index ece2c7b59..860e3c3ad 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(); From 8946edfd922c6e3abde9d52f25e4818207a0f508 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Fri, 22 Apr 2016 05:21:26 -0700 Subject: [PATCH 11/38] QML UI: adapt "Manage dives" to no cloud status If we are in no cloud mode - allow adding dives via the menu - enabling / disabling automatic sync makes no sense. - if the user wants to manually sync the cloud, they need to first enter credentials. Signed-off-by: Dirk Hohndel --- mobile-widgets/qml/main.qml | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/mobile-widgets/qml/main.qml b/mobile-widgets/qml/main.qml index 858e9d003..ad3f68e37 100644 --- a/mobile-widgets/qml/main.qml +++ b/mobile-widgets/qml/main.qml @@ -134,7 +134,7 @@ Kirigami.ApplicationWindow { */ Kirigami.Action { text: "Add dive manually" - enabled: manager.credentialStatus === QMLManager.VALID || manager.credentialStatus === QMLManager.VALID_EMAIL + 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() @@ -142,16 +142,25 @@ Kirigami.ApplicationWindow { } Kirigami.Action { text: "Manual sync with cloud" - enabled: manager.credentialStatus === QMLManager.VALID || manager.credentialStatus === QMLManager.VALID_EMAIL + 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 ? "Offline mode" : "Enable auto cloud sync" + enabled: manager.credentialStatus !== QMLManager.NOCLOUD onTriggered: { syncToCloud = !syncToCloud if (!syncToCloud) { From a1f0263c64f7c78f621e4334f7cb0b2838178118 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Fri, 22 Apr 2016 05:30:43 -0700 Subject: [PATCH 12/38] Remove useless command line handling code from mobile app The files collected were never opened, this all is just the result of a copy and paste job that clearly was never fully thought through. Also remove support for ancient libgit2 Signed-off-by: Dirk Hohndel --- subsurface-mobile-main.cpp | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/subsurface-mobile-main.cpp b/subsurface-mobile-main.cpp index e5965090b..fc570a96b 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; @@ -69,17 +56,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(); From d6b36c47cb2810921ae638a9237310052d9920ef Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Fri, 22 Apr 2016 06:51:47 -0700 Subject: [PATCH 13/38] Fix SettingsObjectWrapper to actually use the group set At least for the GeneralSettings group. Signed-off-by: Dirk Hohndel --- core/subsurface-qt/SettingsObjectWrapper.cpp | 7 +++++++ 1 file changed, 7 insertions(+) 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); From 9ba090c31fd87bdec71c4e14cae3d71c4bb07eac Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Fri, 22 Apr 2016 07:00:29 -0700 Subject: [PATCH 14/38] git storage: init local repository So far we didn't do that at all, we either relied on the user manually creating a local repo, or we cloned a remote repo. Signed-off-by: Dirk Hohndel --- core/git-access.c | 14 ++++++++++++++ core/git-access.h | 1 + 2 files changed, 15 insertions(+) diff --git a/core/git-access.c b/core/git-access.c index 2b7fa0ca2..0e012b082 100644 --- a/core/git-access.c +++ b/core/git-access.c @@ -429,6 +429,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 @@ -919,3 +924,12 @@ 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; + int ret = git_repository_init(&repo, filename, false); + 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; From 7c156f3ba1c1b2129453836f9c99de97621acb2b Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Fri, 22 Apr 2016 07:08:23 -0700 Subject: [PATCH 15/38] QML UI: in no cloud mode, save to a default local repo And set that up as the default file to load at start. Signed-off-by: Dirk Hohndel --- mobile-widgets/qmlmanager.cpp | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/mobile-widgets/qmlmanager.cpp b/mobile-widgets/qmlmanager.cpp index c2c60ce5c..9aae67f33 100644 --- a/mobile-widgets/qmlmanager.cpp +++ b/mobile-widgets/qmlmanager.cpp @@ -19,6 +19,7 @@ #include "core/qt-gui.h" #include "core/git-access.h" #include "core/cloudstorage.h" +#include "core/subsurface-qt/SettingsObjectWrapper.h" QMLManager *QMLManager::m_instance = NULL; @@ -794,7 +795,20 @@ 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, "")) { + QString filename(system_default_directory()); + filename += "/cloudstorage/localrepo"; + if (git_create_local_repo(qPrintable(filename))) + appendTextToLog(get_error_string()); + filename += "[master]"; + set_filename(qPrintable(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 +823,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; From 4b1edceca0342a403ee549987e156b03ce8767e0 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Fri, 22 Apr 2016 07:10:20 -0700 Subject: [PATCH 16/38] Add helper function for the consumption of loaded dive data This allows us to call that part of the process from multiple places in the future. Signed-off-by: Dirk Hohndel --- mobile-widgets/qmlmanager.cpp | 7 ++++++- mobile-widgets/qmlmanager.h | 1 + 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/mobile-widgets/qmlmanager.cpp b/mobile-widgets/qmlmanager.cpp index 9aae67f33..7780a3ff6 100644 --- a/mobile-widgets/qmlmanager.cpp +++ b/mobile-widgets/qmlmanager.cpp @@ -412,6 +412,12 @@ void QMLManager::loadDivesWithValidCredentials() alreadySaving = false; return; } + consumeFinishedLoad(currentDiveTimestamp); + setLoadFromCloud(true); +} + +void QMLManager::consumeFinishedLoad(timestamp_t currentDiveTimestamp) +{ prefs.unit_system = informational_prefs.unit_system; if (informational_prefs.unit_system == IMPERIAL) informational_prefs.units = IMPERIAL_units; @@ -426,7 +432,6 @@ void QMLManager::loadDivesWithValidCredentials() 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; } diff --git a/mobile-widgets/qmlmanager.h b/mobile-widgets/qmlmanager.h index 860e3c3ad..2bd2ee240 100644 --- a/mobile-widgets/qmlmanager.h +++ b/mobile-widgets/qmlmanager.h @@ -133,6 +133,7 @@ public slots: QString getCurrentPosition(); QString getVersion() const; void deleteGpsFix(quint64 when); + void consumeFinishedLoad(timestamp_t currentDiveTimestamp); void refreshDiveList(); void screenChanged(QScreen *screen); qreal lastDevicePixelRatio(); From ec136defa8eb87c816ba5f3ea3cbfd9e61150ee6 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Fri, 22 Apr 2016 06:57:08 -0700 Subject: [PATCH 17/38] QML UI: set the default local repo to load This will allow the user to open a repo that was stored in no cloud mode. Signed-off-by: Dirk Hohndel --- subsurface-mobile-main.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/subsurface-mobile-main.cpp b/subsurface-mobile-main.cpp index fc570a96b..e18d40794 100644 --- a/subsurface-mobile-main.cpp +++ b/subsurface-mobile-main.cpp @@ -48,6 +48,9 @@ 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); + // some hard coded settings prefs.animation_speed = 0; // we render the profile to pixmap, no animations From bdf8bc676b9becd6b3f856eae0a60e2f165fda37 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Fri, 22 Apr 2016 07:13:14 -0700 Subject: [PATCH 18/38] QML UI: with a local default file setup, open that at start And set no cloud mode. Signed-off-by: Dirk Hohndel --- mobile-widgets/qmlmanager.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/mobile-widgets/qmlmanager.cpp b/mobile-widgets/qmlmanager.cpp index 7780a3ff6..6a5cd9281 100644 --- a/mobile-widgets/qmlmanager.cpp +++ b/mobile-widgets/qmlmanager.cpp @@ -179,9 +179,15 @@ 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")); + parse_file(existing_filename); + consumeFinishedLoad(0); + qDebug() << "working in no-cloud mode, finished loading" << dive_table.nr << "dives"; } 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); From 49e2113ae8ab175638554e6aaaab3bb61181a9a3 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Wed, 27 Apr 2016 05:41:38 -0700 Subject: [PATCH 19/38] Remove hack that's no longer needed Signed-off-by: Dirk Hohndel --- mobile-widgets/qml/DiveDetails.qml | 2 -- 1 file changed, 2 deletions(-) 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 From 15431f418e724c0f729dd2ffa140a0b7efda23e9 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Wed, 27 Apr 2016 05:44:18 -0700 Subject: [PATCH 20/38] QML UI: use anchor to fill parent Signed-off-by: Dirk Hohndel --- mobile-widgets/qml/main.qml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/mobile-widgets/qml/main.qml b/mobile-widgets/qml/main.qml index ad3f68e37..1927d6224 100644 --- a/mobile-widgets/qml/main.qml +++ b/mobile-widgets/qml/main.qml @@ -334,8 +334,7 @@ Kirigami.ApplicationWindow { DiveDetails { id: detailsWindow visible: false - width: parent.width - height: parent.height + anchors.fill: parent } DownloadFromDiveComputer { From 3946f83ff2ab1e3d51e62654dc23df01bd1222c6 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Wed, 27 Apr 2016 05:51:14 -0700 Subject: [PATCH 21/38] only set index if we have specific target Signed-off-by: Dirk Hohndel --- mobile-widgets/qmlmanager.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/mobile-widgets/qmlmanager.cpp b/mobile-widgets/qmlmanager.cpp index 6a5cd9281..301dd22dc 100644 --- a/mobile-widgets/qmlmanager.cpp +++ b/mobile-widgets/qmlmanager.cpp @@ -433,8 +433,6 @@ void QMLManager::consumeFinishedLoad(timestamp_t currentDiveTimestamp) 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.")); From 93d21ceac74f7c6fdb5f025e9b65db420fdd5ce8 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Wed, 27 Apr 2016 06:01:07 -0700 Subject: [PATCH 22/38] correctly load default filename Signed-off-by: Dirk Hohndel --- subsurface-mobile-main.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/subsurface-mobile-main.cpp b/subsurface-mobile-main.cpp index e18d40794..a354aa429 100644 --- a/subsurface-mobile-main.cpp +++ b/subsurface-mobile-main.cpp @@ -40,6 +40,8 @@ int main(int argc, char **argv) } git_libgit2_init(); setup_system_prefs(); + free((void*)prefs.default_filename); + prefs.default_filename = NULL; if (uiLanguage(0).contains("-US")) default_prefs.units = IMPERIAL_units; prefs = default_prefs; @@ -50,6 +52,8 @@ int main(int argc, char **argv) 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 From 9ca0f7334b241fca133e7c2e22242c77d3071833 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Wed, 27 Apr 2016 06:14:13 -0700 Subject: [PATCH 23/38] QML UI: handle failure to read local dive data Signed-off-by: Dirk Hohndel --- mobile-widgets/qmlmanager.cpp | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/mobile-widgets/qmlmanager.cpp b/mobile-widgets/qmlmanager.cpp index 301dd22dc..2149d5c86 100644 --- a/mobile-widgets/qmlmanager.cpp +++ b/mobile-widgets/qmlmanager.cpp @@ -182,9 +182,17 @@ void QMLManager::finishSetup() } else if (!same_string(existing_filename, "")) { setCredentialStatus(NOCLOUD); appendTextToLog(tr("working in no-cloud mode")); - parse_file(existing_filename); - consumeFinishedLoad(0); - qDebug() << "working in no-cloud mode, finished loading" << dive_table.nr << "dives"; + 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(tr("no cloud credentials")); From f536da0ee32a39806efdbcbf9ed2edcaaa230f26 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Fri, 29 Apr 2016 05:04:47 -0700 Subject: [PATCH 24/38] QML UI: make sure nothing is lost when changing cloud credentials Signed-off-by: Dirk Hohndel --- mobile-widgets/qmlmanager.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mobile-widgets/qmlmanager.cpp b/mobile-widgets/qmlmanager.cpp index 2149d5c86..9e2670242 100644 --- a/mobile-widgets/qmlmanager.cpp +++ b/mobile-widgets/qmlmanager.cpp @@ -253,6 +253,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(); From 3f6366026798ebd31cd08af0619049aacfd96e2b Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Fri, 29 Apr 2016 06:17:02 -0700 Subject: [PATCH 25/38] Allow switching the user from the command line This is a feature that many people have asked for. This implementation is somewhat simplistic because we simply use a different name for the program settings - but interestingly enough this appears to be enough to capture a lot of the core functionality that people are looking for in multi-user support. Signed-off-by: Dirk Hohndel --- core/qt-init.cpp | 11 ++++++++++- core/subsurfacestartup.c | 9 ++++++++- core/subsurfacestartup.h | 2 ++ 3 files changed, 20 insertions(+), 2 deletions(-) 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 #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/subsurfacestartup.c b/core/subsurfacestartup.c index 6e0dede1c..bb257960a 100644 --- a/core/subsurfacestartup.c +++ b/core/subsurfacestartup.c @@ -197,6 +197,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 +259,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 From 247358be37d060fb070b92dd6768aefae40fa024 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Fri, 29 Apr 2016 06:22:48 -0700 Subject: [PATCH 26/38] Clean up comparison syntax in qml For consistency. Signed-off-by: Dirk Hohndel --- mobile-widgets/qml/DiveList.qml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/mobile-widgets/qml/DiveList.qml b/mobile-widgets/qml/DiveList.qml index 98a7a260f..436bf3d8d 100644 --- a/mobile-widgets/qml/DiveList.qml +++ b/mobile-widgets/qml/DiveList.qml @@ -211,10 +211,11 @@ Kirigami.ScrollablePage { ScrollView { id: startPageWrapper anchors.fill: parent - opacity: credentialStatus === QMLManager.NOCLOUD || (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 @@ -290,7 +291,7 @@ Kirigami.ScrollablePage { } 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; } From c6205b5819c7e7e3853b93208fb1444a02d46b71 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Fri, 29 Apr 2016 06:24:21 -0700 Subject: [PATCH 27/38] QML UI: fix order of tests when saving changes Only check for previous cloud connection when actually trying to save to cloud Signed-off-by: Dirk Hohndel --- mobile-widgets/qmlmanager.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/mobile-widgets/qmlmanager.cpp b/mobile-widgets/qmlmanager.cpp index 9e2670242..427b184f3 100644 --- a/mobile-widgets/qmlmanager.cpp +++ b/mobile-widgets/qmlmanager.cpp @@ -246,6 +246,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())); @@ -864,10 +865,6 @@ void QMLManager::saveChangesCloud(bool 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; @@ -879,6 +876,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; From 4ad73fb80756eb7509efd19962994ebfeb2a219b Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Fri, 29 Apr 2016 06:28:09 -0700 Subject: [PATCH 28/38] QML UI: when switching from no cloud to cloud usage, don't drop local data This is rather simplistic, it just imports the local data into the remote repository and therefore loses the git history of the local data - but I wasn't able to make the git merge without shared base commit work, so I went this much easier to implement route instead. One thing we need to be careful about is that contacting the remote server could fail. If we don't manage to merge the dives from cloud server and local storage, we need to revery to no cloud status in order not to lose the local data. Signed-off-by: Dirk Hohndel --- mobile-widgets/qmlmanager.cpp | 79 +++++++++++++++++++++++++++++------ mobile-widgets/qmlmanager.h | 2 + 2 files changed, 68 insertions(+), 13 deletions(-) diff --git a/mobile-widgets/qmlmanager.cpp b/mobile-widgets/qmlmanager.cpp index 427b184f3..416130d98 100644 --- a/mobile-widgets/qmlmanager.cpp +++ b/mobile-widgets/qmlmanager.cpp @@ -20,6 +20,7 @@ #include "core/git-access.h" #include "core/cloudstorage.h" #include "core/subsurface-qt/SettingsObjectWrapper.h" +#include "core/membuffer.h" QMLManager *QMLManager::m_instance = NULL; @@ -154,6 +155,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")); @@ -164,6 +170,13 @@ void QMLManager::openLocalThenRemote(QString url) } } +void QMLManager::mergeLocalRepo() +{ + char *filename = format_string("%s/cloudstorage/localrepo[master]", system_default_directory()); + parse_file(filename); + process_dives(true, false); +} + void QMLManager::finishSetup() { // Initialize cloud credentials. @@ -346,8 +359,7 @@ void QMLManager::retrieveUserid() if (reply->attribute(QNetworkRequest::HttpStatusCodeAttribute) != 302) { appendTextToLog(QStringLiteral("Cloud storage connection not working correctly: %1").arg(QString(reply->readAll()))); setStartPageText(RED_FONT + tr("Cannot connect to cloud storage") + END_FONT); - setAccessingCloud(-1); - alreadySaving = false; + revertToNoCloudIfNeeded(); return; } setCredentialStatus(VALID); @@ -355,8 +367,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)); @@ -385,12 +396,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); @@ -400,13 +411,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) { @@ -426,12 +433,56 @@ 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); + return; +} + +void QMLManager::revertToNoCloudIfNeeded() +{ + 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); + 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) @@ -1156,6 +1207,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 2bd2ee240..965a68d40 100644 --- a/mobile-widgets/qmlmanager.h +++ b/mobile-widgets/qmlmanager.h @@ -128,11 +128,13 @@ 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); From 4a2a0a426e5249a532bb15879c42325068b13194 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Sat, 30 Apr 2016 06:21:55 -0700 Subject: [PATCH 29/38] Fix logic for when to mark dive list as changed Since saving checks if there are changes, we have to always mark the dive list as changed before asking for the changes to be saved. Signed-off-by: Dirk Hohndel --- mobile-widgets/qmlmanager.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/mobile-widgets/qmlmanager.cpp b/mobile-widgets/qmlmanager.cpp index 416130d98..8a09dc212 100644 --- a/mobile-widgets/qmlmanager.cpp +++ b/mobile-widgets/qmlmanager.cpp @@ -855,11 +855,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 } From 78d420ac322507596f010fc639924f052c14ed82 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Sat, 30 Apr 2016 06:44:15 -0700 Subject: [PATCH 30/38] QML UI: make text match the implementation Signed-off-by: Dirk Hohndel --- mobile-widgets/qml/StartPage.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mobile-widgets/qml/StartPage.qml b/mobile-widgets/qml/StartPage.qml index 980e0921c..e9c785672 100644 --- a/mobile-widgets/qml/StartPage.qml +++ b/mobile-widgets/qml/StartPage.qml @@ -18,8 +18,8 @@ ColumnLayout { Layout.topMargin: Kirigami.Units.gridUnit * 3 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, select " + - "Offline mode in the Manage dives entry in the main menu." + "To use Subsurface-mobile only with local data on this device, tap " + + "on the no cloud icon below." wrapMode: Text.WordWrap } Kirigami.Label { From 05a51f7984e62a8eaf02a8b8dc7269325fb26d9b Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Sat, 30 Apr 2016 06:51:26 -0700 Subject: [PATCH 31/38] Only create the path to no cloud local storage once This required a small change to the helper function, but this seemed totally worth it. Signed-off-by: Dirk Hohndel --- core/git-access.c | 7 ++++++- mobile-widgets/qmlmanager.cpp | 13 +++++++------ 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/core/git-access.c b/core/git-access.c index 0e012b082..fe9a27452 100644 --- a/core/git-access.c +++ b/core/git-access.c @@ -928,7 +928,12 @@ struct git_repository *is_git_repository(const char *filename, const char **bran int git_create_local_repo(const char *filename) { git_repository *repo; - int ret = git_repository_init(&repo, filename, false); + 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/mobile-widgets/qmlmanager.cpp b/mobile-widgets/qmlmanager.cpp index 8a09dc212..9b3471a7a 100644 --- a/mobile-widgets/qmlmanager.cpp +++ b/mobile-widgets/qmlmanager.cpp @@ -27,6 +27,8 @@ QMLManager *QMLManager::m_instance = NULL; #define RED_FONT QLatin1Literal("") #define END_FONT QLatin1Literal("") +#define NOCLOUD_LOCALSTORAGE format_string("%s/cloudstorage/localrepo[master]", system_default_directory()) + static void appendTextToLogStandalone(const char *text) { QMLManager *self = QMLManager::instance(); @@ -172,7 +174,7 @@ void QMLManager::openLocalThenRemote(QString url) void QMLManager::mergeLocalRepo() { - char *filename = format_string("%s/cloudstorage/localrepo[master]", system_default_directory()); + char *filename = NOCLOUD_LOCALSTORAGE; parse_file(filename); process_dives(true, false); } @@ -479,6 +481,7 @@ void QMLManager::revertToNoCloudIfNeeded() 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); @@ -868,12 +871,10 @@ void QMLManager::saveChangesLocal() git_storage_update_progress(true, "saving dives locally"); // reset the timers if (credentialStatus() == NOCLOUD) { if (same_string(existing_filename, "")) { - QString filename(system_default_directory()); - filename += "/cloudstorage/localrepo"; - if (git_create_local_repo(qPrintable(filename))) + char *filename = NOCLOUD_LOCALSTORAGE; + if (git_create_local_repo(filename)) appendTextToLog(get_error_string()); - filename += "[master]"; - set_filename(qPrintable(filename), true); + set_filename(filename, true); GeneralSettingsObjectWrapper s(this); s.setDefaultFilename(filename); s.setDefaultFileBehavior(LOCAL_DEFAULT_FILE); From 711e5e76e8dfa5ed991d83c78f8e9ce51318e016 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Sat, 30 Apr 2016 10:15:15 -0700 Subject: [PATCH 32/38] QML UI: better diagnostics when cloud connection fails Signed-off-by: Dirk Hohndel --- mobile-widgets/qmlmanager.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/mobile-widgets/qmlmanager.cpp b/mobile-widgets/qmlmanager.cpp index 9b3471a7a..0989a109c 100644 --- a/mobile-widgets/qmlmanager.cpp +++ b/mobile-widgets/qmlmanager.cpp @@ -359,7 +359,9 @@ 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); revertToNoCloudIfNeeded(); return; From 879482e97758ca6bd4192b1892d6bdf096c9c739 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Sat, 30 Apr 2016 10:15:56 -0700 Subject: [PATCH 33/38] QML UI: forceRemoteSync means connect even without unsaved changes Signed-off-by: Dirk Hohndel --- mobile-widgets/qmlmanager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mobile-widgets/qmlmanager.cpp b/mobile-widgets/qmlmanager.cpp index 0989a109c..56c99be80 100644 --- a/mobile-widgets/qmlmanager.cpp +++ b/mobile-widgets/qmlmanager.cpp @@ -914,7 +914,7 @@ 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; } From bca2c2a101b344079d77abc2e7f5a9e986046c93 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Sat, 30 Apr 2016 10:16:00 -0700 Subject: [PATCH 34/38] Connect to the reply, not the manager This way if there are other pending connections we don't get triggered by the wrong completion. Signed-off-by: Dirk Hohndel --- mobile-widgets/qmlmanager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mobile-widgets/qmlmanager.cpp b/mobile-widgets/qmlmanager.cpp index 56c99be80..a0ed9873c 100644 --- a/mobile-widgets/qmlmanager.cpp +++ b/mobile-widgets/qmlmanager.cpp @@ -302,7 +302,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()); @@ -310,6 +309,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); } } From 9bec79a71b54d39f8934254c5f6cd5abc11cd32f Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Sat, 30 Apr 2016 10:34:11 -0700 Subject: [PATCH 35/38] Don't pretend that opening git repo succeeded Not sure why we claimed that this was successful when clearly it wasn't. There's a risk that this could break something on the desktop, but it makes no sense to me why that would be the right thing to do. Signed-off-by: Dirk Hohndel --- core/file.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/file.c b/core/file.c index 52c93a719..3ce084e03 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 */ From d943050a93ba59d500fa3196d00529e7b2e6b925 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Sat, 30 Apr 2016 10:36:01 -0700 Subject: [PATCH 36/38] QML UI: make offline the default Signed-off-by: Dirk Hohndel --- core/subsurfacestartup.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/subsurfacestartup.c b/core/subsurfacestartup.c index bb257960a..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, From 7afcadef532a06de7ae9bf515e81971babd139fa Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Sat, 30 Apr 2016 10:36:27 -0700 Subject: [PATCH 37/38] Remove pointless code This isn't how you remove the default filename Signed-off-by: Dirk Hohndel --- subsurface-mobile-main.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/subsurface-mobile-main.cpp b/subsurface-mobile-main.cpp index a354aa429..b7259c44a 100644 --- a/subsurface-mobile-main.cpp +++ b/subsurface-mobile-main.cpp @@ -40,8 +40,6 @@ int main(int argc, char **argv) } git_libgit2_init(); setup_system_prefs(); - free((void*)prefs.default_filename); - prefs.default_filename = NULL; if (uiLanguage(0).contains("-US")) default_prefs.units = IMPERIAL_units; prefs = default_prefs; From ba9288fab6d7aa8527f7a75a717cdc88a68facd9 Mon Sep 17 00:00:00 2001 From: Dirk Hohndel Date: Sat, 30 Apr 2016 11:08:33 -0700 Subject: [PATCH 38/38] QML UI: when first entering cloud credentials, force cloud connection With offline the default now, we need to force a connection at least once so that the repos are in sync. And then of course we need to return to the correct state, regardless on whether this connection succeeded or failed. Signed-off-by: Dirk Hohndel --- mobile-widgets/qmlmanager.cpp | 14 ++++++++++++++ mobile-widgets/qmlmanager.h | 1 + 2 files changed, 15 insertions(+) diff --git a/mobile-widgets/qmlmanager.cpp b/mobile-widgets/qmlmanager.cpp index a0ed9873c..82ea319bb 100644 --- a/mobile-widgets/qmlmanager.cpp +++ b/mobile-widgets/qmlmanager.cpp @@ -286,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); } } @@ -461,11 +465,21 @@ successful_exit: } } 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 diff --git a/mobile-widgets/qmlmanager.h b/mobile-widgets/qmlmanager.h index 965a68d40..906e7b0b2 100644 --- a/mobile-widgets/qmlmanager.h +++ b/mobile-widgets/qmlmanager.h @@ -174,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();