diff --git a/CMakeLists.txt b/CMakeLists.txt index 4f936b48db231d06ff9d25bec5f28e9372dc8f05..2f25d2db6c59c680f127c218a77f697940a01ba1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -245,7 +245,7 @@ set(COMMON_HEADERS ${APP_SRC_DIR}/avatarimageprovider.h ${APP_SRC_DIR}/networkmanager.h ${APP_SRC_DIR}/smartlistmodel.h - ${APP_SRC_DIR}/updatemanager.h + ${APP_SRC_DIR}/appversionmanager.h ${APP_SRC_DIR}/utils.h ${APP_SRC_DIR}/bannedlistmodel.h ${APP_SRC_DIR}/version.h @@ -336,7 +336,7 @@ if(MSVC) list(APPEND COMMON_SOURCES ${APP_SRC_DIR}/connectivitymonitor.cpp - ${APP_SRC_DIR}/updatemanager.cpp) + ${APP_SRC_DIR}/appversionmanager.cpp) # preprocessor defines add_definitions(-DUNICODE -DQT_NO_DEBUG -DNDEBUG) @@ -388,7 +388,7 @@ elseif (NOT APPLE) ${APP_SRC_DIR}/xrectsel.c ${APP_SRC_DIR}/connectivitymonitor.cpp ${APP_SRC_DIR}/dbuserrorhandler.cpp - ${APP_SRC_DIR}/updatemanager.cpp) + ${APP_SRC_DIR}/appversionmanager.cpp) list(APPEND COMMON_HEADERS ${APP_SRC_DIR}/xrectsel.h ${APP_SRC_DIR}/dbuserrorhandler.h) @@ -440,7 +440,7 @@ elseif (NOT APPLE) find_library(X11 X11) else() # APPLE list(APPEND COMMON_SOURCES - ${APP_SRC_DIR}/os/macos/updatemanager.mm + ${APP_SRC_DIR}/os/macos/appversionmanager.mm ${APP_SRC_DIR}/os/macos/connectivitymonitor.mm ${APP_SRC_DIR}/os/macos/macutils.mm) list(APPEND COMMON_HEADERS diff --git a/src/app/MainApplicationWindow.qml b/src/app/MainApplicationWindow.qml index 6410f6a54483f7d443fc377ae87b51e6b56ce37a..f75732477fed88dbc78bd380e267272bd5be6adf 100644 --- a/src/app/MainApplicationWindow.qml +++ b/src/app/MainApplicationWindow.qml @@ -205,8 +205,8 @@ ApplicationWindow { // Quiet check for updates on start if set to. if (Qt.platform.os.toString() === "windows") { if (UtilsAdapter.getAppValue(Settings.AutoUpdate)) { - UpdateManager.checkForUpdates(true); - UpdateManager.setAutoUpdateCheck(true); + AppVersionManager.checkForUpdates(true); + AppVersionManager.setAutoUpdateCheck(true); } } @@ -268,7 +268,7 @@ ApplicationWindow { } Connections { - target: UpdateManager + target: AppVersionManager function onDownloadStarted() { viewCoordinator.presentDialog(appWindow, "settingsview/components/UpdateDownloadDialog.qml", { @@ -290,7 +290,7 @@ ApplicationWindow { "buttonTitles": [JamiStrings.optionUpgrade, JamiStrings.optionLater], "buttonStyles": [SimpleMessageDialog.ButtonStyle.TintedBlue, SimpleMessageDialog.ButtonStyle.TintedBlue], "buttonCallBacks": [function () { - UpdateManager.applyUpdates(); + AppVersionManager.applyUpdates(); }] }); } diff --git a/src/app/updatemanager.cpp b/src/app/appversionmanager.cpp similarity index 52% rename from src/app/updatemanager.cpp rename to src/app/appversionmanager.cpp index ca300d336b1d69ad8f5e392123978240292ead10..c3ac06c9c57595b54f227e29aaea33240026eebb 100644 --- a/src/app/updatemanager.cpp +++ b/src/app/appversionmanager.cpp @@ -15,7 +15,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#include "updatemanager.h" +#include "appversionmanager.h" #include "lrcinstance.h" #include "version.h" @@ -37,9 +37,9 @@ static constexpr char betaVersionSubUrl[] = "/beta/version"; static constexpr char msiSubUrl[] = "/jami.release.x64.msi"; static constexpr char betaMsiSubUrl[] = "/beta/jami.beta.x64.msi"; -struct UpdateManager::Impl : public QObject +struct AppVersionManager::Impl : public QObject { - Impl(const QString& url, LRCInstance* instance, UpdateManager& parent) + Impl(const QString& url, LRCInstance* instance, AppVersionManager& parent) : QObject(nullptr) , parent_(parent) , lrcInstance_(instance) @@ -60,9 +60,9 @@ struct UpdateManager::Impl : public QObject // Fail without UI if this is a programmatic check. if (!quiet) connect(&parent_, - &NetworkManager::errorOccured, + &NetworkManager::errorOccurred, &parent_, - &UpdateManager::updateErrorOccurred); + &AppVersionManager::updateErrorOccurred); cleanUpdateFiles(); QUrl versionUrl {isBeta ? QUrl::fromUserInput(baseUrlString_ + betaVersionSubUrl) @@ -92,31 +92,21 @@ struct UpdateManager::Impl : public QObject { parent_.disconnect(); connect(&parent_, - &NetworkManager::errorOccured, + &NetworkManager::errorOccurred, &parent_, - &UpdateManager::updateErrorOccurred); - connect(&parent_, &UpdateManager::statusChanged, this, [this](Status status) { - switch (status) { - case Status::STARTED: - Q_EMIT parent_.updateDownloadStarted(); - break; - case Status::FINISHED: - Q_EMIT parent_.updateDownloadFinished(); - break; - default: - break; - } - }); + &AppVersionManager::updateErrorOccurred); - QUrl downloadUrl {(beta || isBeta) ? QUrl::fromUserInput(baseUrlString_ + betaMsiSubUrl) - : QUrl::fromUserInput(baseUrlString_ + msiSubUrl)}; + const QUrl downloadUrl {(beta || isBeta) + ? QUrl::fromUserInput(baseUrlString_ + betaMsiSubUrl) + : QUrl::fromUserInput(baseUrlString_ + msiSubUrl)}; - parent_.downloadFile( + int uuid = parent_.downloadFile( downloadUrl, + *(parent_.replyId_), [this, downloadUrl](bool success, const QString& errorMessage) { Q_UNUSED(success) Q_UNUSED(errorMessage) - QProcess process; + const QProcess process; auto basePath = tempPath_ + QDir::separator(); auto msiPath = QDir::toNativeSeparators(basePath + downloadUrl.fileName()); auto logPath = QDir::toNativeSeparators(basePath + "jami_x64_install.log"); @@ -127,11 +117,12 @@ struct UpdateManager::Impl : public QObject << "/L*V" << logPath); }, tempPath_); + parent_.replyId_.reset(&uuid); }; void cancelUpdate() { - parent_.cancelDownload(); + parent_.cancelDownload(*(parent_.replyId_)); }; void setAutoUpdateCheck(bool state) @@ -162,7 +153,7 @@ struct UpdateManager::Impl : public QObject } }; - UpdateManager& parent_; + AppVersionManager& parent_; LRCInstance* lrcInstance_ {nullptr}; QString baseUrlString_; @@ -170,51 +161,52 @@ struct UpdateManager::Impl : public QObject QTimer* updateTimer_; }; -UpdateManager::UpdateManager(const QString& url, - ConnectivityMonitor* cm, - LRCInstance* instance, - QObject* parent) +AppVersionManager::AppVersionManager(const QString& url, + ConnectivityMonitor* cm, + LRCInstance* instance, + QObject* parent) : NetworkManager(cm, parent) , pimpl_(std::make_unique<Impl>(url, instance, *this)) + , replyId_(new int(0)) {} -UpdateManager::~UpdateManager() +AppVersionManager::~AppVersionManager() { - cancelDownload(); + cancelDownload(*replyId_); } void -UpdateManager::checkForUpdates(bool quiet) +AppVersionManager::checkForUpdates(bool quiet) { pimpl_->checkForUpdates(quiet); } void -UpdateManager::applyUpdates(bool beta) +AppVersionManager::applyUpdates(bool beta) { pimpl_->applyUpdates(beta); } void -UpdateManager::cancelUpdate() +AppVersionManager::cancelUpdate() { pimpl_->cancelUpdate(); } void -UpdateManager::setAutoUpdateCheck(bool state) +AppVersionManager::setAutoUpdateCheck(bool state) { pimpl_->setAutoUpdateCheck(state); } bool -UpdateManager::isCurrentVersionBeta() +AppVersionManager::isCurrentVersionBeta() { return isBeta; } bool -UpdateManager::isUpdaterEnabled() +AppVersionManager::isUpdaterEnabled() { #ifdef Q_OS_WIN return true; @@ -223,116 +215,7 @@ UpdateManager::isUpdaterEnabled() } bool -UpdateManager::isAutoUpdaterEnabled() +AppVersionManager::isAutoUpdaterEnabled() { return false; } - -void -UpdateManager::cancelDownload() -{ - if (downloadReply_) { - Q_EMIT errorOccured(GetError::CANCELED); - downloadReply_->abort(); - resetDownload(); - } -} - -void -UpdateManager::downloadFile(const QUrl& url, - std::function<void(bool, const QString&)> onDoneCallback, - const QString& filePath) -{ - // If there is already a download in progress, return. - if (downloadReply_ && downloadReply_->isRunning()) { - qWarning() << Q_FUNC_INFO << "Download already in progress"; - return; - } - - // Clean up any previous download. - resetDownload(); - - // If the url is invalid, return. - if (!url.isValid()) { - Q_EMIT errorOccured(GetError::NETWORK_ERROR, "Invalid url"); - return; - } - - // If the file path is empty, return. - if (filePath.isEmpty()) { - Q_EMIT errorOccured(GetError::NETWORK_ERROR, "Invalid file path"); - return; - } - - // Create the file. Return if it cannot be created. - QFileInfo fileInfo(url.path()); - QString fileName = fileInfo.fileName(); - file_.reset(new QFile(filePath + "/" + fileName)); - if (!file_->open(QIODevice::WriteOnly)) { - Q_EMIT errorOccured(GetError::ACCESS_DENIED); - file_.reset(); - qWarning() << Q_FUNC_INFO << "Could not open file for writing"; - return; - } - - // Start the download. - QNetworkRequest request(url); - downloadReply_ = manager_->get(request); - - connect(downloadReply_, &QNetworkReply::readyRead, this, [=]() { - if (file_ && file_->isOpen()) { - file_->write(downloadReply_->readAll()); - } - }); - - connect(downloadReply_, - &QNetworkReply::downloadProgress, - this, - [=](qint64 bytesReceived, qint64 bytesTotal) { - Q_EMIT downloadProgressChanged(bytesReceived, bytesTotal); - }); - - connect(downloadReply_, - QOverload<QNetworkReply::NetworkError>::of(&QNetworkReply::errorOccurred), - this, - [this](QNetworkReply::NetworkError error) { - downloadReply_->disconnect(); - resetDownload(); - qWarning() << Q_FUNC_INFO - << QMetaEnum::fromType<QNetworkReply::NetworkError>().valueToKey(error); - Q_EMIT errorOccured(GetError::NETWORK_ERROR); - }); - - connect(downloadReply_, &QNetworkReply::finished, this, [this, onDoneCallback]() { - bool success = false; - QString errorMessage; - if (downloadReply_->error() == QNetworkReply::NoError) { - resetDownload(); - success = true; - } else { - errorMessage = downloadReply_->errorString(); - resetDownload(); - } - onDoneCallback(success, errorMessage); - Q_EMIT statusChanged(Status::FINISHED); - }); - - Q_EMIT statusChanged(Status::STARTED); -} - -void -UpdateManager::resetDownload() -{ - if (downloadReply_) { - downloadReply_->deleteLater(); - downloadReply_ = nullptr; - } - if (file_) { - if (file_->isOpen()) { - file_->flush(); - file_->close(); - } - file_->deleteLater(); - file_.reset(); - } -} diff --git a/src/app/updatemanager.h b/src/app/appversionmanager.h similarity index 59% rename from src/app/updatemanager.h rename to src/app/appversionmanager.h index 373c97fceb97feac2f899af1ae1685e0d156ff85..55fa94c5ff756e085024d3f67ab36b34828ae9c2 100644 --- a/src/app/updatemanager.h +++ b/src/app/appversionmanager.h @@ -1,6 +1,5 @@ /* * Copyright (C) 2020-2023 Savoir-faire Linux Inc. - * Author: Andreas Traczyk <andreas.traczyk@savoirfairelinux.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -25,51 +24,35 @@ class LRCInstance; class ConnectivityMonitor; -class UpdateManager final : public NetworkManager +class AppVersionManager final : public NetworkManager { Q_OBJECT - Q_DISABLE_COPY(UpdateManager) + Q_DISABLE_COPY(AppVersionManager) public: - explicit UpdateManager(const QString& url, - ConnectivityMonitor* cm, - LRCInstance* instance = nullptr, - QObject* parent = nullptr); - ~UpdateManager(); - - enum Status { STARTED, FINISHED }; - Q_ENUM(Status) + explicit AppVersionManager(const QString& url, + ConnectivityMonitor* cm, + LRCInstance* instance = nullptr, + QObject* parent = nullptr); + ~AppVersionManager(); Q_INVOKABLE void checkForUpdates(bool quiet = false); Q_INVOKABLE void applyUpdates(bool beta = false); - Q_INVOKABLE void cancelUpdate(); - Q_INVOKABLE void setAutoUpdateCheck(bool state); - Q_INVOKABLE bool isCurrentVersionBeta(); Q_INVOKABLE bool isUpdaterEnabled(); Q_INVOKABLE bool isAutoUpdaterEnabled(); - Q_INVOKABLE void cancelDownload(); - - void downloadFile(const QUrl& url, - std::function<void(bool, const QString&)> onDoneCallback, - const QString& filePath); + Q_INVOKABLE void setAutoUpdateCheck(bool state); + Q_INVOKABLE void cancelUpdate(); + Q_INVOKABLE bool isCurrentVersionBeta(); Q_SIGNALS: - void statusChanged(UpdateManager::Status status); - void downloadProgressChanged(qint64 bytesRead, qint64 totalBytes); - + void appCloseRequested(); void updateCheckReplyReceived(bool ok, bool found = false); - void updateErrorOccurred(const NetworkManager::GetError& error); - void updateDownloadStarted(); void updateDownloadProgressChanged(qint64 bytesRead, qint64 totalBytes); - void updateDownloadFinished(); - void appCloseRequested(); - -private: - void resetDownload(); - QNetworkReply* downloadReply_ {nullptr}; - QScopedPointer<QFile> file_; + void updateErrorOccurred(const NetworkManager::GetError& error); private: + QScopedPointer<int> replyId_; struct Impl; + friend struct Impl; std::unique_ptr<Impl> pimpl_; }; -Q_DECLARE_METATYPE(UpdateManager*) +Q_DECLARE_METATYPE(AppVersionManager*) diff --git a/src/app/constant/JamiStrings.qml b/src/app/constant/JamiStrings.qml index c04dc8030fe8d56018337af17596887a677b3977..c62fbd884d2b3b8cf1cff07b9f018a274006a10d 100644 --- a/src/app/constant/JamiStrings.qml +++ b/src/app/constant/JamiStrings.qml @@ -46,7 +46,7 @@ Item { property string reconnectTry: qsTr("Trying to reconnect to the Jami daemon (jamid)…") // AboutPopUp - property string version: qsTr("Version") + (UpdateManager.isCurrentVersionBeta() ? " (Beta)" : "") + property string version: qsTr("Version") + (AppVersionManager.isCurrentVersionBeta() ? " (Beta)" : "") property string companyDeclarationYear: declarationYear + " " + companyName property string declarationYear: "© 2015-2023" property string companyName: "Savoir-faire Linux Inc." diff --git a/src/app/imagedownloader.cpp b/src/app/imagedownloader.cpp index 945a4e6078fd46a124186501c38758f4e15c3957..9eb0784d50d64877c558223795dedbbb285c98b1 100644 --- a/src/app/imagedownloader.cpp +++ b/src/app/imagedownloader.cpp @@ -26,7 +26,7 @@ ImageDownloader::ImageDownloader(ConnectivityMonitor* cm, QObject* parent) void ImageDownloader::downloadImage(const QUrl& url, const QString& localPath) { - Utils::oneShotConnect(this, &NetworkManager::errorOccured, this, [this, localPath]() { + Utils::oneShotConnect(this, &NetworkManager::errorOccurred, this, [this, localPath]() { onDownloadImageFinished({}, localPath); }); diff --git a/src/app/lrcinstance.cpp b/src/app/lrcinstance.cpp index 7e7162d8ee61a26d5d7b0db795b74eadfd26fbea..323cab0b54daeac9df070b6580fb68ca01db50fe 100644 --- a/src/app/lrcinstance.cpp +++ b/src/app/lrcinstance.cpp @@ -34,7 +34,7 @@ LRCInstance::LRCInstance(migrateCallback willMigrateCb, bool debugMode, bool muteDaemon) : lrc_(std::make_unique<Lrc>(willMigrateCb, didMigrateCb, !debugMode || muteDaemon)) - , updateManager_(std::make_unique<UpdateManager>(updateUrl, connectivityMonitor, this)) + , updateManager_(std::make_unique<AppVersionManager>(updateUrl, connectivityMonitor, this)) , threadPool_(new QThreadPool(this)) { debugMode_ = debugMode; @@ -75,8 +75,8 @@ LRCInstance::LRCInstance(migrateCallback willMigrateCb, } }; -UpdateManager* -LRCInstance::getUpdateManager() +AppVersionManager* +LRCInstance::getAppVersionManager() { return updateManager_.get(); } diff --git a/src/app/lrcinstance.h b/src/app/lrcinstance.h index 4acf94884e7a6ccc421975f6235a8ad9ed132534..0da5abee14ca57e9b0b248988ac094fa3a63c329 100644 --- a/src/app/lrcinstance.h +++ b/src/app/lrcinstance.h @@ -24,7 +24,7 @@ #undef ERROR #endif -#include "updatemanager.h" +#include "appversionmanager.h" #include "qtutils.h" #include "utils.h" @@ -71,7 +71,7 @@ public: void finish(); - UpdateManager* getUpdateManager(); + AppVersionManager* getAppVersionManager(); AccountModel& accountModel(); ConversationModel* getCurrentConversationModel(); @@ -145,7 +145,7 @@ Q_SIGNALS: private: std::unique_ptr<Lrc> lrc_; - std::unique_ptr<UpdateManager> updateManager_; + std::unique_ptr<AppVersionManager> updateManager_; QString selectedConvUid_; MapStringString contentDrafts_; diff --git a/src/app/networkmanager.cpp b/src/app/networkmanager.cpp index 15cec11caa6fd735377284f4290e063ee72c56d0..0af5f216e8e5e1a87eecd431aedaf0100f2efa15 100644 --- a/src/app/networkmanager.cpp +++ b/src/app/networkmanager.cpp @@ -19,14 +19,18 @@ #include "connectivitymonitor.h" +#include <QMap> +#include <QDir> #include <QMetaEnum> #include <QtNetwork> +#include <QScopedPointer> NetworkManager::NetworkManager(ConnectivityMonitor* cm, QObject* parent) : QObject(parent) , manager_(new QNetworkAccessManager(this)) , connectivityMonitor_(cm) , lastConnectionState_(cm->isOnline()) + , rng_(std::random_device {}()) { #if QT_CONFIG(ssl) connect(manager_, @@ -36,7 +40,7 @@ NetworkManager::NetworkManager(ConnectivityMonitor* cm, QObject* parent) Q_UNUSED(reply); Q_FOREACH (const QSslError& error, errors) { qWarning() << Q_FUNC_INFO << error.errorString(); - Q_EMIT errorOccured(GetError::SSL_ERROR, error.errorString()); + Q_EMIT errorOccurred(GetError::SSL_ERROR, error.errorString()); } }); #endif @@ -53,15 +57,127 @@ NetworkManager::NetworkManager(ConnectivityMonitor* cm, QObject* parent) void NetworkManager::sendGetRequest(const QUrl& url, - std::function<void(const QByteArray&)> onDoneCallback) + std::function<void(const QByteArray&)>&& onDoneCallback) { - auto reply = manager_->get(QNetworkRequest(url)); + auto* const reply = manager_->get(QNetworkRequest(url)); QObject::connect(reply, &QNetworkReply::finished, this, [reply, onDoneCallback, this]() { if (reply->error() == QNetworkReply::NoError) { onDoneCallback(reply->readAll()); - } else{ - Q_EMIT errorOccured(GetError::NETWORK_ERROR, reply->errorString()); - } + } else { + Q_EMIT errorOccurred(GetError::NETWORK_ERROR, reply->errorString()); + } reply->deleteLater(); }); } + +int +NetworkManager::downloadFile(const QUrl& url, + unsigned int replyId, + std::function<void(bool, const QString&)>&& onDoneCallback, + const QString& filePath) +{ + // If there is already a download in progress, return. + if ((downloadReplies_.value(replyId) != NULL || !(replyId == 0)) + && downloadReplies_[replyId]->isRunning()) { + qWarning() << Q_FUNC_INFO << "Download already in progress"; + return replyId; + } + + // Clean up any previous download. + resetDownload(replyId); + + // If the url is invalid, return. + if (!url.isValid()) { + Q_EMIT errorOccurred(GetError::NETWORK_ERROR, "Invalid url"); + return 0; + } + + // If the file path is empty, return. + if (filePath.isEmpty()) { + Q_EMIT errorOccurred(GetError::NETWORK_ERROR, "Invalid file path"); + return 0; + } + + // set the id for the request + std::uniform_int_distribution<int> dist(1, std::numeric_limits<int>::max()); + auto uuid = dist(rng_); + + const QDir dir; + if (!dir.exists(filePath)) { + dir.mkpath(filePath); + } + + // Create the file. Return if it cannot be created. + const QFileInfo fileInfo(url.path()); + const QString fileName = fileInfo.fileName(); + auto& file = files_[uuid]; + file = new QFile(filePath + fileName + ".jpl"); + if (!file->open(QIODevice::WriteOnly)) { + Q_EMIT errorOccurred(GetError::ACCESS_DENIED); + files_.remove(uuid); + qWarning() << Q_FUNC_INFO << "Could not open file for writing"; + return 0; + } + + // Start the download. + const QNetworkRequest request(url); + + downloadReplies_[uuid] = manager_->get(request); + auto* const reply = downloadReplies_[uuid]; + connect(reply, &QNetworkReply::readyRead, this, [file, reply]() { + if (file && file->isOpen()) { + file->write(reply->readAll()); + } + }); + + connect(reply, + &QNetworkReply::downloadProgress, + this, + [this](qint64 bytesReceived, qint64 bytesTotal) { + Q_EMIT downloadProgressChanged(bytesReceived, bytesTotal); + }); + + connect(reply, + QOverload<QNetworkReply::NetworkError>::of(&QNetworkReply::errorOccurred), + this, + [this, uuid, reply](QNetworkReply::NetworkError error) { + reply->disconnect(); + resetDownload(uuid); + qWarning() << Q_FUNC_INFO + << QMetaEnum::fromType<QNetworkReply::NetworkError>().valueToKey(error); + Q_EMIT errorOccurred(GetError::NETWORK_ERROR); + }); + + connect(reply, &QNetworkReply::finished, this, [this, uuid, onDoneCallback, reply]() { + bool success = false; + QString errorMessage; + if (reply->error() == QNetworkReply::NoError) { + resetDownload(uuid); + success = true; + } else { + errorMessage = reply->errorString(); + resetDownload(uuid); + } + onDoneCallback(success, errorMessage); + Q_EMIT downloadFinished(uuid); + }); + Q_EMIT downloadStarted(uuid); + return uuid; +} + +void +NetworkManager::cancelDownload(int replyId) +{ + if (downloadReplies_.value(replyId) != NULL) { + Q_EMIT errorOccurred(GetError::CANCELED); + downloadReplies_[replyId]->abort(); + resetDownload(replyId); + } +} + +void +NetworkManager::resetDownload(int replyId) +{ + files_.remove(replyId); + downloadReplies_.remove(replyId); +} diff --git a/src/app/networkmanager.h b/src/app/networkmanager.h index bf80b0d95c0c2cdb7ac374063292218eee6dd397..67dacb237dbf14bdf820f5c1e04ac03e5cc5f2ef 100644 --- a/src/app/networkmanager.h +++ b/src/app/networkmanager.h @@ -20,7 +20,10 @@ #include <QObject> #include <QFile> #include <QSslError> +#include <QMap> +#include <QString> #include <QNetworkReply> +#include <random> class QNetworkAccessManager; class ConnectivityMonitor; @@ -35,10 +38,19 @@ public: enum GetError { DISCONNECTED, NETWORK_ERROR, ACCESS_DENIED, SSL_ERROR, CANCELED }; Q_ENUM(GetError) - void sendGetRequest(const QUrl& url, std::function<void(const QByteArray&)> onDoneCallback); + void sendGetRequest(const QUrl& url, std::function<void(const QByteArray&)>&& onDoneCallback); + int downloadFile(const QUrl& url, + unsigned int replyId, + std::function<void(bool, const QString&)>&& onDoneCallback, + const QString& filePath); + void resetDownload(int replyId); + void cancelDownload(int replyId); Q_SIGNALS: - void errorOccured(GetError error, const QString& msg = {}); + void errorOccurred(GetError error, const QString& msg = {}); + void downloadProgressChanged(qint64 bytesRead, qint64 totalBytes); + void downloadFinished(int replyId); + void downloadStarted(int replyId); protected: QNetworkAccessManager* manager_; @@ -46,5 +58,8 @@ protected: private: ConnectivityMonitor* connectivityMonitor_; bool lastConnectionState_; + QMap<int, QNetworkReply*> downloadReplies_ {}; + QMap<int, QFile*> files_ {}; + std::mt19937 rng_; }; Q_DECLARE_METATYPE(NetworkManager*) diff --git a/src/app/os/macos/updatemanager.mm b/src/app/os/macos/appversionmanager.mm similarity index 83% rename from src/app/os/macos/updatemanager.mm rename to src/app/os/macos/appversionmanager.mm index ecbac015222a72e0d2123cb07c3929f45b0c24fd..1933ab842132a0c1bdbd5b3404ff958fca9531c5 100644 --- a/src/app/os/macos/updatemanager.mm +++ b/src/app/os/macos/appversionmanager.mm @@ -16,7 +16,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#include "updatemanager.h" +#include "appversionmanager.h" #ifdef ENABLE_SPARKLE #include <Sparkle/Sparkle.h> @@ -29,7 +29,7 @@ static constexpr bool isBeta = false; #endif #ifdef ENABLE_SPARKLE - struct UpdateManager::Impl + struct AppVersionManager::Impl { Impl() @@ -63,7 +63,7 @@ static constexpr bool isBeta = false; }; #else -struct UpdateManager::Impl +struct AppVersionManager::Impl { void checkForUpdates() {}; @@ -80,7 +80,7 @@ struct UpdateManager::Impl }; #endif -UpdateManager::UpdateManager(const QString& url, +AppVersionManager::AppVersionManager(const QString& url, ConnectivityMonitor* cm, LRCInstance* instance, QObject* parent) @@ -88,51 +88,46 @@ UpdateManager::UpdateManager(const QString& url, , pimpl_(std::make_unique<Impl>()) {} -UpdateManager::~UpdateManager() +AppVersionManager::~AppVersionManager() {} void -UpdateManager::checkForUpdates(bool quiet) +AppVersionManager::checkForUpdates(bool quiet) { Q_UNUSED(quiet) pimpl_->checkForUpdates(); } void -UpdateManager::applyUpdates(bool beta) +AppVersionManager::applyUpdates(bool beta) { Q_UNUSED(beta) } void -UpdateManager::cancelUpdate() -{} - - -void -UpdateManager::cancelDownload() +AppVersionManager::cancelUpdate() {} void -UpdateManager::setAutoUpdateCheck(bool state) +AppVersionManager::setAutoUpdateCheck(bool state) { pimpl_->setAutoUpdateCheck(state); } bool -UpdateManager::isCurrentVersionBeta() +AppVersionManager::isCurrentVersionBeta() { return isBeta; } bool -UpdateManager::isUpdaterEnabled() +AppVersionManager::isUpdaterEnabled() { return pimpl_->isUpdaterEnabled(); } bool -UpdateManager::isAutoUpdaterEnabled() +AppVersionManager::isAutoUpdaterEnabled() { return pimpl_->isAutoUpdaterEnabled(); } diff --git a/src/app/pluginadapter.cpp b/src/app/pluginadapter.cpp index bde8370a46b1a1ef1a70c74288139e98f48758fe..6c5a97cf08cab2d3d17f576af08bcc8578dc7a58 100644 --- a/src/app/pluginadapter.cpp +++ b/src/app/pluginadapter.cpp @@ -18,10 +18,12 @@ #include "pluginadapter.h" +#include "networkmanager.h" #include "lrcinstance.h" PluginAdapter::PluginAdapter(LRCInstance* instance, QObject* parent) : QmlAdapterBase(instance, parent) + { set_isEnabled(lrcInstance_->pluginModel().getPluginsEnabled()); updateHandlersListCount(); diff --git a/src/app/qmlregister.cpp b/src/app/qmlregister.cpp index b199012643735481d35781eac020749e3fe2ed1c..5c5d677b1d212e5526bc7c871590dbccbc1082a5 100644 --- a/src/app/qmlregister.cpp +++ b/src/app/qmlregister.cpp @@ -55,8 +55,8 @@ #include "appsettingsmanager.h" #include "mainapplication.h" #include "namedirectory.h" -#include "updatemanager.h" #include "pluginlistmodel.h" +#include "appversionmanager.h" #include "pluginlistpreferencemodel.h" #include "preferenceitemlistmodel.h" #include "wizardviewstepmodel.h" @@ -156,7 +156,7 @@ registerTypes(QQmlEngine* engine, // TODO: remove these QML_REGISTERSINGLETONTYPE_CUSTOM(NS_MODELS, AVModel, &lrcInstance->avModel()) QML_REGISTERSINGLETONTYPE_CUSTOM(NS_MODELS, PluginModel, &lrcInstance->pluginModel()) - QML_REGISTERSINGLETONTYPE_CUSTOM(NS_HELPERS, UpdateManager, lrcInstance->getUpdateManager()) + QML_REGISTERSINGLETONTYPE_CUSTOM(NS_HELPERS, AppVersionManager, lrcInstance->getAppVersionManager()) // Hack for QtCreator autocomplete (part 2) // https://bugreports.qt.io/browse/QTCREATORBUG-20569 @@ -244,7 +244,7 @@ registerTypes(QQmlEngine* engine, engine->setObjectOwnership(&lrcInstance->avModel(), QQmlEngine::CppOwnership); engine->setObjectOwnership(&lrcInstance->pluginModel(), QQmlEngine::CppOwnership); - engine->setObjectOwnership(lrcInstance->getUpdateManager(), QQmlEngine::CppOwnership); + engine->setObjectOwnership(lrcInstance->getAppVersionManager(), QQmlEngine::CppOwnership); engine->setObjectOwnership(&NameDirectory::instance(), QQmlEngine::CppOwnership); } // clang-format on diff --git a/src/app/settingsview/SettingsSidePanel.qml b/src/app/settingsview/SettingsSidePanel.qml index 887a3dfb357a7961a75bf6e6a72b60224a9a04be..92511de41e6b3d4eeb76f704f239a33bfe38f14b 100644 --- a/src/app/settingsview/SettingsSidePanel.qml +++ b/src/app/settingsview/SettingsSidePanel.qml @@ -83,7 +83,7 @@ SidePanelBase { }, { "id": 11, "title": JamiStrings.updatesTitle, - "visible": UpdateManager.isUpdaterEnabled() + "visible": AppVersionManager.isUpdaterEnabled() }] }, { "title": JamiStrings.audioVideoSettingsTitle, diff --git a/src/app/settingsview/components/PluginItemDelegate.qml b/src/app/settingsview/components/PluginItemDelegate.qml index e273d8772beaf2313ce764924c544109798ea64c..578808bb8307516122f744582f2ed5658d2bfcfb 100644 --- a/src/app/settingsview/components/PluginItemDelegate.qml +++ b/src/app/settingsview/components/PluginItemDelegate.qml @@ -77,7 +77,21 @@ ItemDelegate { text: pluginName === "" ? pluginId : pluginName verticalAlignment: Text.AlignVCenter } - + MaterialButton { + id: update + TextMetrics { + id: updateTextSize + font.weight: Font.Bold + font.pixelSize: JamiTheme.wizardViewButtonFontPixelSize + font.capitalization: Font.AllUppercase + text: JamiStrings.updatePlugin + } + visible: false + secondary: true + preferredWidth: updateTextSize.width + text: JamiStrings.updatePlugin + fontSize: 15 + } ToggleSwitch { id: loadSwitch Layout.fillHeight: true diff --git a/src/app/settingsview/components/PluginSettingsPage.qml b/src/app/settingsview/components/PluginSettingsPage.qml index f1350b315c564b3f74a2f54d9a30e184babd66be..6b177e94bb5a3a82cc8b8cd356978dca766c2513 100644 --- a/src/app/settingsview/components/PluginSettingsPage.qml +++ b/src/app/settingsview/components/PluginSettingsPage.qml @@ -25,7 +25,7 @@ import "../../commoncomponents" SettingsPageBase { id: root - + contentFlickableWidth: Math.min(root.width, root.width - 2 * JamiTheme.preferredSettingsMarginSize) title: JamiStrings.pluginSettingsTitle flickableContent: ColumnLayout { @@ -38,34 +38,19 @@ SettingsPageBase { ColumnLayout { id: generalSettings - - width: parent.width + Layout.preferredWidth: root.width spacing: JamiTheme.settingsCategorySpacing + } + // View of installed plugins + PluginListView { + id: pluginListView - ToggleSwitch { - id: enabledplugin - - checked: PluginAdapter.isEnabled - Layout.alignment: Qt.AlignTop | Qt.AlignHCenter - Layout.fillWidth: true - labelText: JamiStrings.enable - - onSwitchToggled: { - PluginModel.setPluginsEnabled(checked); - PluginAdapter.isEnabled = checked; - } - } - - PluginListView { - id: pluginListView - - visible: PluginAdapter.isEnabled + visible: PluginAdapter.isEnabled - Layout.alignment: Qt.AlignTop | Qt.AlignHCenter - Layout.preferredWidth: parent.width - Layout.minimumHeight: 0 - Layout.preferredHeight: childrenRect.height - } + Layout.alignment: Qt.AlignTop | Qt.AlignHCenter + Layout.preferredWidth: parent.width + Layout.minimumHeight: 0 + Layout.preferredHeight: childrenRect.height } } } diff --git a/src/app/settingsview/components/UpdateDownloadDialog.qml b/src/app/settingsview/components/UpdateDownloadDialog.qml index d4827c223dd6885438e8e120ff51a4b09919026c..f183bd845d078a4a5e8e2f4fda1b81fc189b6775 100644 --- a/src/app/settingsview/components/UpdateDownloadDialog.qml +++ b/src/app/settingsview/components/UpdateDownloadDialog.qml @@ -34,9 +34,10 @@ SimpleMessageDialog { property alias progressBarValue: progressBar.value Connections { - target: UpdateManager + target: AppVersionManager - function onUpdateErrorOccurred(error) { + function onErrorOccurred(error, msg) { + console.warn("Error while downloading update: " + error + " - " + msg); downloadDialog.close(); } @@ -44,7 +45,7 @@ SimpleMessageDialog { downloadDialog.setDownloadProgress(bytesRead, totalBytes); } - function onUpdateDownloadFinished() { + function onDownloadFinished() { downloadDialog.close(); } } @@ -98,10 +99,10 @@ SimpleMessageDialog { buttonTitles: [JamiStrings.optionCancel] buttonStyles: [SimpleMessageDialog.ButtonStyle.TintedBlue] buttonCallBacks: [function () { - UpdateManager.cancelDownload(); + AppVersionManager.cancelUpdate(); }] onVisibleChanged: { if (!visible) - UpdateManager.cancelDownload(); + AppVersionManager.cancelUpdate(); } } diff --git a/src/app/settingsview/components/UpdateSettingsPage.qml b/src/app/settingsview/components/UpdateSettingsPage.qml index 186cf0c46d5577ae8e3e29f95face85b033c6730..e2a02e5468c02aba7913fc3baea46c2d2abb8e83 100644 --- a/src/app/settingsview/components/UpdateSettingsPage.qml +++ b/src/app/settingsview/components/UpdateSettingsPage.qml @@ -49,7 +49,7 @@ SettingsPageBase { "buttonTitles": [JamiStrings.optionUpgrade, JamiStrings.optionLater], "buttonStyles": [SimpleMessageDialog.ButtonStyle.TintedBlue, SimpleMessageDialog.ButtonStyle.TintedBlue], "buttonCallBacks": [function () { - UpdateManager.applyUpdates(beta); + AppVersionManager.applyUpdates(beta); }] }); } @@ -66,14 +66,14 @@ SettingsPageBase { Layout.fillWidth: true - checked: Qt.platform.os.toString() === "windows" ? UtilsAdapter.getAppValue(Settings.Key.AutoUpdate) : UpdateManager.isAutoUpdaterEnabled() + checked: Qt.platform.os.toString() === "windows" ? UtilsAdapter.getAppValue(Settings.Key.AutoUpdate) : AppVersionManager.isAutoUpdaterEnabled() labelText: JamiStrings.update tooltipText: JamiStrings.enableAutoUpdates onSwitchToggled: { UtilsAdapter.setAppValue(Settings.Key.AutoUpdate, checked); - UpdateManager.setAutoUpdateCheck(checked); + AppVersionManager.setAutoUpdateCheck(checked); } } @@ -98,13 +98,13 @@ SettingsPageBase { toolTipText: JamiStrings.checkForUpdates text: JamiStrings.checkForUpdates - onClicked: UpdateManager.checkForUpdates() + onClicked: AppVersionManager.checkForUpdates() } MaterialButton { id: installBetaButton - visible: !UpdateManager.isCurrentVersionBeta() && Qt.platform.os.toString() === "windows" + visible: !AppVersionManager.isCurrentVersionBeta() && Qt.platform.os.toString() === "windows" Layout.alignment: Qt.AlignHCenter @@ -125,7 +125,7 @@ SettingsPageBase { "buttonTitles": [JamiStrings.optionUpgrade, JamiStrings.optionLater], "buttonStyles": [SimpleMessageDialog.ButtonStyle.TintedBlue, SimpleMessageDialog.ButtonStyle.TintedBlue], "buttonCallBacks": [function () { - UpdateManager.applyUpdates(true); + AppVersionManager.applyUpdates(true); }] }) } diff --git a/src/app/wizardview/WizardView.qml b/src/app/wizardview/WizardView.qml index 461feb4718ff208fbf6ed2592946fe6cc74d94b6..036e695e8c51962d64bcd3c3eab5d7905ff75820 100644 --- a/src/app/wizardview/WizardView.qml +++ b/src/app/wizardview/WizardView.qml @@ -43,7 +43,7 @@ BaseView { var errorMessage = JamiStrings.errorCreateAccount; for (var i = 0; i < controlPanelStackView.children.length; i++) { if (i === controlPanelStackView.currentIndex) { - controlPanelStackView.children[i].errorOccured(errorMessage); + controlPanelStackView.children[i].errorOccurred(errorMessage); return; } } diff --git a/src/app/wizardview/components/ConnectToAccountManagerPage.qml b/src/app/wizardview/components/ConnectToAccountManagerPage.qml index 0f8848376f705f72a246ad6ee476810b09b97399..2564779a4488dca5207d979f60afc60ccdc682d5 100644 --- a/src/app/wizardview/components/ConnectToAccountManagerPage.qml +++ b/src/app/wizardview/components/ConnectToAccountManagerPage.qml @@ -36,7 +36,7 @@ Rectangle { errorText = ""; } - function errorOccured(errorMessage) { + function errorOccurred(errorMessage) { connectBtn.spinnerTriggered = false; errorText = errorMessage; } diff --git a/src/app/wizardview/components/ImportFromBackupPage.qml b/src/app/wizardview/components/ImportFromBackupPage.qml index b4fed5433c7dbaee2c380df281e1522434040b1f..9cd94783be0eafa42c865fb2e9872b74faebb650 100644 --- a/src/app/wizardview/components/ImportFromBackupPage.qml +++ b/src/app/wizardview/components/ImportFromBackupPage.qml @@ -43,7 +43,7 @@ Rectangle { fileImportBtnText = JamiStrings.selectArchiveFile; } - function errorOccured(errorMessage) { + function errorOccurred(errorMessage) { errorText = errorMessage; connectBtn.spinnerTriggered = false; } diff --git a/src/app/wizardview/components/ImportFromDevicePage.qml b/src/app/wizardview/components/ImportFromDevicePage.qml index e12295ffcfaf8b939e871cfe36d5c099708e85ec..d377cfff69736d21dee88102f1a1a59469db23e1 100644 --- a/src/app/wizardview/components/ImportFromDevicePage.qml +++ b/src/app/wizardview/components/ImportFromDevicePage.qml @@ -39,7 +39,7 @@ Rectangle { connectBtn.spinnerTriggered = false; } - function errorOccured(errorMessage) { + function errorOccurred(errorMessage) { errorText = errorMessage; connectBtn.spinnerTriggered = false; }