Skip to content

Commit

Permalink
Pause sync when on a metered Internet connection
Browse files Browse the repository at this point in the history
Add an advanced configuration setting to pause synchronization when a
metered internet connection is detected.

When the internet connection is metered, and the user chooses to
force-sync a folder, they will now be asked if they are sure that they
want to do this.

Fixes: #4808
  • Loading branch information
erikjv committed Nov 9, 2023
1 parent 83ae5d4 commit 2afff7b
Show file tree
Hide file tree
Showing 15 changed files with 190 additions and 40 deletions.
47 changes: 37 additions & 10 deletions src/gui/accountsettings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
#include <QIcon>
#include <QKeySequence>
#include <QMessageBox>
#include <QNetworkInformation>
#include <QPropertyAnimation>
#include <QSortFilterProxyModel>
#include <QToolTip>
Expand Down Expand Up @@ -687,20 +688,43 @@ void AccountSettings::slotScheduleCurrentFolderForceFullDiscovery()

void AccountSettings::slotForceSyncCurrentFolder()
{
auto isMetered = []() {
if (auto qNetInfo = QNetworkInformation::instance()) {
return qNetInfo->isMetered();
}
return false;
};

if (auto selectedFolder = this->selectedFolder()) {
// Terminate and reschedule any running sync
for (auto *folder : FolderMan::instance()->folders()) {
if (folder->isSyncRunning()) {
folder->slotTerminateSync(tr("User triggered force sync"));
FolderMan::instance()->scheduler()->enqueueFolder(folder);
}
if (isMetered() && ConfigFile().pauseSyncWhenMetered()) {
auto messageBox = new QMessageBox(QMessageBox::Question, tr("Internet connection is metered"),
tr("Synchronization is paused because the Internet connection is a metered connection"
"<p>Do you really want to force a Synchronization now?"),
QMessageBox::Yes | QMessageBox::No, ocApp()->gui()->settingsDialog());
messageBox->setAttribute(Qt::WA_DeleteOnClose);
connect(messageBox, &QMessageBox::accepted, this, [this, selectedFolder] { doForceSyncCurrentFolder(selectedFolder); });
messageBox->open();
ownCloudGui::raiseDialog(messageBox);
} else {
doForceSyncCurrentFolder(selectedFolder);
}
}
}

selectedFolder->slotWipeErrorBlacklist(); // issue #6757
selectedFolder->slotNextSyncFullLocalDiscovery(); // ensure we don't forget about local errors
// Insert the selected folder at the front of the queue
FolderMan::instance()->scheduler()->enqueueFolder(selectedFolder, SyncScheduler::Priority::High);
void AccountSettings::doForceSyncCurrentFolder(Folder *selectedFolder)
{
// Terminate and reschedule any running sync
for (auto *folder : FolderMan::instance()->folders()) {
if (folder->isSyncRunning()) {
folder->slotTerminateSync(tr("User triggered force sync"));
FolderMan::instance()->scheduler()->enqueueFolder(folder);
}
}

selectedFolder->slotWipeErrorBlacklist(); // issue #6757
selectedFolder->slotNextSyncFullLocalDiscovery(); // ensure we don't forget about local errors
// Insert the selected folder at the front of the queue
FolderMan::instance()->scheduler()->enqueueFolder(selectedFolder, SyncScheduler::Priority::High);
}

void AccountSettings::slotAccountStateChanged()
Expand Down Expand Up @@ -814,6 +838,9 @@ void AccountSettings::slotAccountStateChanged()
case AccountState::Disconnected:
showConnectionLabel(tr("Disconnected from: %1.").arg(server));
break;
case AccountState::PausedDueToMetered:
showConnectionLabel(tr("Sync to %1 is paused due to metered internet connection.").arg(server));
break;
}

// Disabling expansion of folders might require hiding the selective
Expand Down
1 change: 1 addition & 0 deletions src/gui/accountsettings.h
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ protected slots:
QStringList errors = QStringList());
bool event(QEvent *) override;
void createAccountToolbox();
void doForceSyncCurrentFolder(Folder *selectedFolder);

/// Returns the alias of the selected folder, empty string if none
Folder *selectedFolder() const;
Expand Down
15 changes: 15 additions & 0 deletions src/gui/accountstate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include "application.h"
#include "configfile.h"
#include "fetchserversettings.h"
#include "guiutility.h"

#include "libsync/creds/abstractcredentials.h"
#include "libsync/creds/httpcredentials.h"
Expand Down Expand Up @@ -136,6 +137,18 @@ AccountState::AccountState(AccountPtr account)
break;
}
});

connect(qNetInfo, &QNetworkInformation::isMeteredChanged, this, [this](bool isMetered) {
if (ConfigFile().pauseSyncWhenMetered()) {
if (state() == State::Connected && isMetered) {
qCInfo(lcAccountState) << "Network switched to a metered connection, setting account state to PausedDueToMetered";
setState(State::PausedDueToMetered);
} else if (state() == State::PausedDueToMetered && !isMetered) {
qCInfo(lcAccountState) << "Network switched to a NON-metered connection, setting account state to Connected";
setState(State::Connected);
}
}
});
}
#endif
// as a fallback and to recover after server issues we also poll
Expand Down Expand Up @@ -231,6 +244,8 @@ void AccountState::setState(State state)
_connectionValidator->deleteLater();
_connectionValidator.clear();
checkConnectivity();
} else if (_state == Connected && Utility::internetConnectionIsMetered() && ConfigFile().pauseSyncWhenMetered()) {
_state = PausedDueToMetered;
}
}

Expand Down
5 changes: 5 additions & 0 deletions src/gui/accountstate.h
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,11 @@ class AccountState : public QObject
/// We are currently asking the user for credentials
AskingCredentials,

/// We are on a metered internet connection, and the user preference
/// is to pause syncing in this case. This state is entered from and
/// left to a `Connected` state.
PausedDueToMetered,

Connecting
};
Q_ENUM(State)
Expand Down
5 changes: 4 additions & 1 deletion src/gui/folder.h
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,9 @@ class Folder : public QObject
{
return *_engine;
}

void abort(const QString &reason);

Vfs &vfs()
{
OC_ENFORCE(_vfs);
Expand Down Expand Up @@ -363,7 +366,7 @@ public slots:
/**
* terminate the current sync run
*/
void slotTerminateSync();
void slotTerminateSync(const QString &reason);

// connected to the corresponding signals in the SyncEngine
void slotAboutToRemoveAllFiles(SyncFileItem::Direction);
Expand Down
14 changes: 12 additions & 2 deletions src/gui/guiutility.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,14 @@
#include "application.h"
#include "settingsdialog.h"

#include <QClipboard>
#include <QApplication>
#include <QClipboard>
#include <QDesktopServices>
#include <QIcon>
#include <QLoggingCategory>
#include <QMessageBox>
#include <QNetworkInformation>
#include <QUrlQuery>
#include <QIcon>

#include "theme.h"

Expand Down Expand Up @@ -85,3 +86,12 @@ QString Utility::vfsFreeSpaceActionText()
{
return QCoreApplication::translate("utility", "Free up local space");
}

bool Utility::internetConnectionIsMetered()
{
if (auto *qNetInfo = QNetworkInformation::instance()) {
return qNetInfo->isMetered();
}

return false;
}
1 change: 1 addition & 0 deletions src/gui/guiutility.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ namespace Utility {

QString socketApiSocketPath();

bool internetConnectionIsMetered();
} // namespace Utility
} // namespace OCC

Expand Down
8 changes: 6 additions & 2 deletions src/gui/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -449,16 +449,20 @@ int main(int argc, char **argv)
return -1;
}

setupLogging(options);

#if QT_VERSION >= QT_VERSION_CHECK(6, 3, 0)
qCDebug(lcMain) << QNetworkInformation::availableBackends().join(QStringLiteral(", "));
if (!QNetworkInformation::loadDefaultBackend()) {
qCWarning(lcMain) << "Failed to load QNetworkInformation";
} else {
qCDebug(lcMain) << "Loaded network information backend:" << QNetworkInformation::instance()->backendName()
<< "supported features:" << QNetworkInformation::instance()->supportedFeatures();
}
#else
qCWarning(lcMain) << "QNetworkInformation is not available";
#endif

setupLogging(options);

platform->setApplication(&app);

auto folderManager = FolderMan::createInstance();
Expand Down
26 changes: 26 additions & 0 deletions src/gui/networksettings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include "theme.h"

#include <QList>
#include <QNetworkInformation>
#include <QNetworkProxy>
#include <QString>
#include <QtGui/QtEvents>
Expand Down Expand Up @@ -69,6 +70,7 @@ NetworkSettings::NetworkSettings(QWidget *parent)

loadProxySettings();
loadBWLimitSettings();
loadMeteredSettings();

// proxy
connect(_ui->typeComboBox, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &NetworkSettings::saveProxySettings);
Expand All @@ -92,6 +94,8 @@ NetworkSettings::NetworkSettings(QWidget *parent)
connect(_ui->hostLineEdit, &QLineEdit::textChanged, this, &NetworkSettings::checkEmptyProxyHost);
checkEmptyProxyHost();
checkAccountLocalhost();

connect(_ui->pauseSyncWhenMeteredCheckbox, &QAbstractButton::clicked, this, &NetworkSettings::saveMeteredSettings);
}

NetworkSettings::~NetworkSettings()
Expand Down Expand Up @@ -177,6 +181,21 @@ void NetworkSettings::loadBWLimitSettings()
_ui->uploadSpinBox->setValue(cfgFile.uploadLimit());
}

void NetworkSettings::loadMeteredSettings()
{
if (QNetworkInformation *qNetInfo = QNetworkInformation::instance()) {
if (Utility::isWindows() // The backend implements the metered feature, but does not report it as supported.
// See https://bugreports.qt.io/browse/QTBUG-118741
|| qNetInfo->supports(QNetworkInformation::Feature::Metered)) {
_ui->pauseSyncWhenMeteredCheckbox->setChecked(ConfigFile().pauseSyncWhenMetered());
return;
}
}

_ui->pauseSyncWhenMeteredCheckbox->setEnabled(false);
_ui->pauseSyncWhenMeteredCheckbox->setToolTip(tr("Querying metered connection status is not supported on this platform"));
}

void NetworkSettings::saveProxySettings()
{
ConfigFile cfgFile;
Expand Down Expand Up @@ -231,6 +250,13 @@ void NetworkSettings::saveBWLimitSettings()
FolderMan::instance()->setDirtyNetworkLimits();
}

void NetworkSettings::saveMeteredSettings()
{
bool pauseSyncWhenMetered = _ui->pauseSyncWhenMeteredCheckbox->isChecked();
ConfigFile().setPauseSyncWhenMetered(pauseSyncWhenMetered);
FolderMan::instance()->scheduler()->setPauseSyncWhenMetered(pauseSyncWhenMetered);
}

void NetworkSettings::checkEmptyProxyHost()
{
if (_ui->hostLineEdit->isEnabled() && _ui->hostLineEdit->text().isEmpty()) {
Expand Down
2 changes: 2 additions & 0 deletions src/gui/networksettings.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ class NetworkSettings : public QWidget
private slots:
void saveProxySettings();
void saveBWLimitSettings();
void saveMeteredSettings();

/// Red marking of host field if empty and enabled
void checkEmptyProxyHost();
Expand All @@ -53,6 +54,7 @@ private slots:
private:
void loadProxySettings();
void loadBWLimitSettings();
void loadMeteredSettings();
CredentialManager *_credentialManager;


Expand Down
7 changes: 7 additions & 0 deletions src/gui/networksettings.ui
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,13 @@
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_5">
<item>
<widget class="QCheckBox" name="pauseSyncWhenMeteredCheckbox">
<property name="text">
<string>Pause synchronization when the Internet connection is metered</string>
</property>
</widget>
</item>
<item>
<widget class="QGroupBox" name="proxyGroupBox">
<property name="enabled">
Expand Down
Loading

0 comments on commit 2afff7b

Please sign in to comment.