From 4ce3115fdd934717d57eee628dddb04f0b3447b8 Mon Sep 17 00:00:00 2001 From: Georgii Surkov Date: Tue, 24 Aug 2021 19:56:43 +0300 Subject: [PATCH] Application update UI improvements --- application/application.cpp | 1 + application/appupdater.cpp | 32 +++++++++++---- application/appupdater.h | 16 +++++++- application/screens/homescreen.qml | 66 +++++++++++++++++++----------- 4 files changed, 81 insertions(+), 34 deletions(-) diff --git a/application/application.cpp b/application/application.cpp index 798bc0a9..160731dd 100644 --- a/application/application.cpp +++ b/application/application.cpp @@ -75,6 +75,7 @@ void Application::initTranslations() void Application::initQmlTypes() { qmlRegisterType("QFlipper", 1, 0, "ScreenCanvas"); + qmlRegisterType("QFlipper", 1, 0, "AppUpdater"); } void Application::initGUI() diff --git a/application/appupdater.cpp b/application/appupdater.cpp index 9618209f..43168789 100644 --- a/application/appupdater.cpp +++ b/application/appupdater.cpp @@ -15,9 +15,15 @@ AppUpdater::AppUpdater(QObject *parent): QObject(parent), + m_state(State::Idle), m_progress(0) {} +AppUpdater::State AppUpdater::state() const +{ + return m_state; +} + double AppUpdater::progress() const { return m_progress; @@ -56,20 +62,19 @@ void AppUpdater::installUpdate(const Flipper::Updates::VersionInfo &versionInfo) }; connect(fetcher, &RemoteFileFetcher::finished, this, [=]() { - emit downloadFinished(); + info_msg("Application update download has finished."); + setState(State::Updating); // IMPORTANT -- The file is closed automatically before renaming (https://doc.qt.io/qt-5/qfile.html#rename) if(!file->rename(filePath)) { error_msg("Failed to rename .part file"); - emit errorOccured(); + setState(State::ErrorOccured); file->remove(); cleanup(); return; } - info_msg("Application update download has finished."); - #if defined(Q_OS_LINUX) const auto executable = QFile::Permission::ExeUser | QFile::Permission::ExeOwner | @@ -87,7 +92,7 @@ void AppUpdater::installUpdate(const Flipper::Updates::VersionInfo &versionInfo) QCoreApplication::exit(0); } else { error_msg("Failed to perform application update."); - emit errorOccured(); + setState(State::ErrorOccured); } cleanup(); @@ -96,15 +101,28 @@ void AppUpdater::installUpdate(const Flipper::Updates::VersionInfo &versionInfo) connect(fetcher, &RemoteFileFetcher::progressChanged, this, &AppUpdater::setProgress); if(!fetcher->fetch(fileInfo, file)) { - error_msg("Failed to fetch applicaton update file."); + error_msg("Failed to start the download."); + setState(State::ErrorOccured); file->remove(); cleanup(); - emit errorOccured(); + } else { + info_msg("Downloading the application update..."); + setState(State::Downloading); } } +void AppUpdater::setState(State state) +{ + if(m_state == state) { + return; + } + + m_state = state; + emit stateChanged(); +} + void AppUpdater::setProgress(double progress) { if(qFuzzyCompare(m_progress, progress)) { diff --git a/application/appupdater.h b/application/appupdater.h index e2d6c855..b202eb73 100644 --- a/application/appupdater.h +++ b/application/appupdater.h @@ -8,26 +8,38 @@ class AppUpdater : public QObject { Q_OBJECT Q_PROPERTY(double progress READ progress NOTIFY progressChanged) + Q_PROPERTY(State state READ state NOTIFY stateChanged) public: + enum class State { + Idle, + Downloading, + Updating, + ErrorOccured + }; + + Q_ENUM(State) + AppUpdater(QObject *parent = nullptr); + State state() const; double progress() const; signals: + void stateChanged(); void progressChanged(); - void downloadFinished(); - void errorOccured(); public slots: void installUpdate(const Flipper::Updates::VersionInfo &versionInfo); private slots: + void setState(State state); void setProgress(double progress); private: bool performUpdate(const QString &path); + State m_state; double m_progress; }; diff --git a/application/screens/homescreen.qml b/application/screens/homescreen.qml index eeeb035a..a4625d38 100644 --- a/application/screens/homescreen.qml +++ b/application/screens/homescreen.qml @@ -2,6 +2,8 @@ import QtQuick 2.12 import QtQuick.Dialogs 1.2 import QtQuick.Controls 2.12 +import QFlipper 1.0 + import "../components" Item { @@ -11,7 +13,22 @@ Item { signal versionsRequested(var device) signal streamRequested(var device) - property string channelName: "development" //TODO move this property into application settings + readonly property string channelName: "development" //TODO move this property into application settings + readonly property bool hasUpdates: { + const currentVersion = app.version; + const currentCommit = app.commit; + + if(applicationUpdates.channelNames.length === 0) { + return false; + } + + const latestVersion = applicationUpdates.channel(channelName).latestVersion; + + const newDevVersionAvailable = (channelName === "development") && (latestVersion.number !== currentCommit); + const newReleaseVersionAvailable = (channelName !== "development") && (latestVersion.number > currentVersion); + + return newDevVersionAvailable || newReleaseVersionAvailable; + } StyledConfirmationDialog { id: confirmationDialog @@ -190,29 +207,38 @@ Item { id: versionLabel text: { - const currentVersion = app.version; - const currentCommit = app.commit; - - const msg = "%1 %2 %3".arg(app.name).arg(qsTr("Version")).arg(currentVersion); + if(app.updater.state === AppUpdater.Idle) { + const msg = "%1 %2 %3".arg(app.name).arg(qsTr("Version")).arg(app.version); - if(applicationUpdates.channelNames.length !== 0) { - const latestVersion = applicationUpdates.channel(channelName).latestVersion; - - const newDevVersionAvailable = (channelName === "development") && (latestVersion.number !== currentCommit); - const newReleaseVersionAvailable = (channelName !== "development") && (latestVersion.number > currentVersion); - - if(newDevVersionAvailable || newReleaseVersionAvailable) { - return "%1".arg(qsTr("Application update available!")); + if(screen.hasUpdates) { + return "%1 - %2".arg(msg).arg(qsTr("update available!")); } else { return msg; } + } else if(app.updater.state === AppUpdater.Downloading) { + return qsTr("Downloading application update... %1%").arg(Math.floor(app.updater.progress)); + } else if(app.updater.state === AppUpdater.Updating) { + return qsTr("Starting the update process..."); + } else if(app.updater.state === AppUpdater.ErrorOccured) { + return qsTr("Update failed. Retry?"); + } + } + + color: { + if(app.updater.state === AppUpdater.Downloading) { + return "darkgray"; + } else if(app.updater.state === AppUpdater.Updating) { + return "darkgray"; + } else if(app.updater.state === AppUpdater.ErrorOccured) { + return "#F55"; + } else if(screen.hasUpdates) { + return "darkgray"; } else { - return msg; + return "#555"; } } - color: "#555" linkColor: "#5eba7d" anchors.horizontalCenter: parent.horizontalCenter @@ -224,16 +250,6 @@ Item { onLinkActivated: { app.updater.installUpdate(applicationUpdates.channel(channelName).latestVersion); - - app.updater.progressChanged.connect(function() { - versionLabel.text = qsTr("Downloading application update... %1%").arg(Math.floor(app.updater.progress)); - }); - - app.updater.errorOccured.connect(function() { - versionLabel.text = qsTr("Update failed"); - versionLabel.color = "#700"; - versionLabel.font.bold = true; - }); } } }