Commit d694fe85 authored by azubieta's avatar azubieta
Browse files

Merge branch 'update_dialog' into 'master'

Add updater dialog

See merge request dfn2/ocs-manager!3
parents 365e180d 4e9e0ef2
Pipeline #922 failed with stage
in 3 minutes and 46 seconds
...@@ -4,18 +4,23 @@ QT += \ ...@@ -4,18 +4,23 @@ QT += \
core \ core \
gui \ gui \
network \ network \
widgets \
websockets websockets
HEADERS += \ HEADERS += \
$${PWD}/src/updaters/appupdatedialog.h \
$${PWD}/src/websockets/websocketserver.h \ $${PWD}/src/websockets/websocketserver.h \
$${PWD}/src/handlers/confighandler.h \ $${PWD}/src/handlers/confighandler.h \
$${PWD}/src/handlers/systemhandler.h \ $${PWD}/src/handlers/systemhandler.h \
$${PWD}/src/handlers/ocsapihandler.h \ $${PWD}/src/handlers/ocsapihandler.h \
$${PWD}/src/handlers/itemhandler.h \ $${PWD}/src/handlers/itemhandler.h \
$${PWD}/src/handlers/updatehandler.h \ $${PWD}/src/handlers/updatehandler.h \
$${PWD}/src/handlers/desktopthemehandler.h $${PWD}/src/handlers/desktopthemehandler.h \
$${PWD}/src/updaters/appupdater.h
SOURCES += \ SOURCES += \
$${PWD}/src/updaters/appupdatedialog.cpp \
$${PWD}/src/updaters/appupdater.cpp \
$${PWD}/src/main.cpp \ $${PWD}/src/main.cpp \
$${PWD}/src/websockets/websocketserver.cpp \ $${PWD}/src/websockets/websocketserver.cpp \
$${PWD}/src/handlers/confighandler.cpp \ $${PWD}/src/handlers/confighandler.cpp \
...@@ -48,3 +53,6 @@ contains(DEFINES, APP_DESKTOP) { ...@@ -48,3 +53,6 @@ contains(DEFINES, APP_DESKTOP) {
$${PWD}/src/desktopthemes/cinnamontheme.cpp \ $${PWD}/src/desktopthemes/cinnamontheme.cpp \
$${PWD}/src/desktopthemes/matetheme.cpp $${PWD}/src/desktopthemes/matetheme.cpp
} }
FORMS += \
$$PWD/src/updaters/appimageupdatedialog.ui
...@@ -4,17 +4,18 @@ ...@@ -4,17 +4,18 @@
#include <QLocale> #include <QLocale>
#include <QCommandLineParser> #include <QCommandLineParser>
#include <QCommandLineOption> #include <QCommandLineOption>
#include <QGuiApplication> #include <QApplication>
#include <QIcon> #include <QIcon>
#include <QDebug> #include <QDebug>
#include "handlers/confighandler.h" #include "handlers/confighandler.h"
#include "websockets/websocketserver.h" #include "websockets/websocketserver.h"
#include "updaters/appupdater.h"
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
// Init // Init
QGuiApplication app(argc, argv); // This is backend program, but need GUI module QApplication app(argc, argv); // This is backend program, but need GUI module
auto envPath = QString::fromLocal8Bit(qgetenv("PATH").constData()) + ":" + app.applicationDirPath(); auto envPath = QString::fromLocal8Bit(qgetenv("PATH").constData()) + ":" + app.applicationDirPath();
qputenv("PATH", envPath.toUtf8().constData()); qputenv("PATH", envPath.toUtf8().constData());
...@@ -27,6 +28,7 @@ int main(int argc, char *argv[]) ...@@ -27,6 +28,7 @@ int main(int argc, char *argv[])
app.setOrganizationName(appConfigApplication["organization"].toString()); app.setOrganizationName(appConfigApplication["organization"].toString());
app.setOrganizationDomain(appConfigApplication["domain"].toString()); app.setOrganizationDomain(appConfigApplication["domain"].toString());
app.setWindowIcon(QIcon::fromTheme(appConfigApplication["id"].toString(), QIcon(appConfigApplication["icon"].toString()))); app.setWindowIcon(QIcon::fromTheme(appConfigApplication["id"].toString(), QIcon(appConfigApplication["icon"].toString())));
app.setQuitOnLastWindowClosed(false);
// Setup translator // Setup translator
QTranslator translator; QTranslator translator;
...@@ -45,13 +47,22 @@ int main(int argc, char *argv[]) ...@@ -45,13 +47,22 @@ int main(int argc, char *argv[])
QCommandLineOption clOptionPort(QStringList() << "p" << "port", "Port for websocket server [default: 49152].", "port", "49152"); QCommandLineOption clOptionPort(QStringList() << "p" << "port", "Port for websocket server [default: 49152].", "port", "49152");
clParser.addOption(clOptionPort); clParser.addOption(clOptionPort);
QCommandLineOption clOptionAppPath(QStringList() << "a" << "appFile", "Path to the main AppImage <file>.", "file");
clParser.addOption(clOptionAppPath);
clParser.process(app); clParser.process(app);
// Setup AppUpdater
auto appFile = clParser.value(clOptionAppPath);
AppUpdater appUpdater(appFile);
appUpdater.setSilentLookup(true);
appUpdater.doUpdateLookUp();
auto port = clParser.value(clOptionPort).toUShort(); auto port = clParser.value(clOptionPort).toUShort();
// Setup websocket server // Setup websocket server
auto *wsServer = new WebSocketServer(configHandler, appConfigApplication["id"].toString(), port, &app); auto *wsServer = new WebSocketServer(configHandler, appConfigApplication["id"].toString(), port, &app);
QObject::connect(wsServer, &WebSocketServer::stopped, &app, &QGuiApplication::quit); QObject::connect(wsServer, &WebSocketServer::stopped, &app, &QGuiApplication::quit);
if (wsServer->start()) { if (wsServer->start()) {
qInfo() << "Websocket server started at:" << wsServer->serverUrl().toString(); qInfo() << "Websocket server started at:" << wsServer->serverUrl().toString();
......
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>AppImageUpdateDialog</class>
<widget class="QDialog" name="AppImageUpdateDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>405</width>
<height>126</height>
</rect>
</property>
<property name="windowTitle">
<string>Pling Store Updater</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QStackedWidget" name="stackedWidget">
<widget class="QWidget" name="confirmationPage">
<layout class="QVBoxLayout" name="verticalLayout_3">
<property name="spacing">
<number>12</number>
</property>
<item>
<widget class="QLabel" name="confirmationLabel">
<property name="text">
<string>A new version of Pling-Store is available!
Do you want to download it?</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QPushButton" name="latterButton">
<property name="text">
<string>Latter</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="doitButton">
<property name="text">
<string>Update</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<widget class="QWidget" name="progressPage">
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QLabel" name="title">
<property name="text">
<string>Downloading update contents</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QProgressBar" name="progressBar">
<property name="value">
<number>0</number>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
#include <QDebug>
#include "appupdatedialog.h"
#include "ui_appimageupdatedialog.h"
AppUpdateDialog::AppUpdateDialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::AppImageUpdateDialog)
{
ui->setupUi(this);
connect(ui->latterButton, &QPushButton::clicked, this, &AppUpdateDialog::reject);
}
AppUpdateDialog::~AppUpdateDialog()
{
delete ui;
}
void AppUpdateDialog::showUpdateConfirmationMessage()
{
setWindowTitle(tr("Pling Store Update Available"));
ui->confirmationLabel->setText(tr("Do you want to update now to the new Pling Store version now?"));
ui->doitButton->setText("Yes");
disconnect(ui->doitButton, nullptr, this, nullptr);
connect(ui->doitButton, &QPushButton::released, this, &AppUpdateDialog::updateRequested);
ui->stackedWidget->setCurrentWidget(ui->confirmationPage);
show();
}
void AppUpdateDialog::showErrorMessage(const QString &msg)
{
setWindowTitle(tr("Pling Store Update Failed"));
ui->confirmationLabel->setText(tr("Do you want to try again?"));
ui->doitButton->setText("Yes");
disconnect(ui->doitButton, nullptr, this, nullptr);
connect(ui->doitButton, &QPushButton::released, this, &AppUpdateDialog::updateRequested);
ui->stackedWidget->setCurrentWidget(ui->confirmationPage);
show();
}
void AppUpdateDialog::showCompletionMessage()
{
setWindowTitle(tr("Plign Store Update Completed"));
ui->confirmationLabel->setText(tr("Do you want to open the new version now?"));
ui->doitButton->setText("Yes");
disconnect(ui->doitButton, nullptr, this, nullptr);
connect(ui->doitButton, &QPushButton::released, this, &AppUpdateDialog::restartRequested);
ui->stackedWidget->setCurrentWidget(ui->confirmationPage);
show();
}
void AppUpdateDialog::showProgress(int progress)
{
setWindowTitle(tr("Pling Store Update"));
ui->progressBar->setValue(progress);
ui->progressPage->show();
ui->stackedWidget->setCurrentWidget(ui->progressPage);
show();
}
#ifndef APPIMAGEUPDATEDIALOG_H
#define APPIMAGEUPDATEDIALOG_H
#include <QDialog>
namespace Ui {
class AppImageUpdateDialog;
}
namespace appimage {
namespace update {
class Updater;
}
}
class AppUpdateDialog : public QDialog
{
Q_OBJECT
public:
explicit AppUpdateDialog(QWidget *parent = nullptr);
~AppUpdateDialog();
signals:
void restartRequested();
void updateRequested();
public slots:
void showUpdateConfirmationMessage();
void showErrorMessage(const QString &msg);
void showCompletionMessage();
void showProgress(int progress);
private:
Ui::AppImageUpdateDialog *ui;
QString targetAction;
};
#endif // APPIMAGEUPDATEDIALOG_H
#include <QDebug>
#include <QtConcurrent/QtConcurrent>
#include "appimage/update.h"
#include "appupdater.h"
AppUpdater::AppUpdater(const QString &appImagePath, QObject *parent) : QObject(parent), appImagePath(appImagePath), updateHelper(nullptr)
{
connect(this, &AppUpdater::updateAvailable, &updateDialog, &AppUpdateDialog::showUpdateConfirmationMessage);
connect(&updateDialog, &AppUpdateDialog::updateRequested, this, &AppUpdater::doUpdate);
connect(&updateDialog, &AppUpdateDialog::restartRequested, this, &AppUpdater::doRestart);
connect(&updateDialog, &AppUpdateDialog::rejected, this, &AppUpdater::stop);
}
void AppUpdater::setSilentLookup(bool value)
{
silentLookup = value;
}
void AppUpdater::doUpdateLookUp()
{
if (appImagePath.isEmpty()) {
qWarning() << "Self-updates disabled: No app file provided.";
return;
}
if (!silentLookup)
updateDialog.show();
QtConcurrent::run([=]() {
appimage::update::Updater updater(appImagePath.toStdString());
bool updateAvailable; // this is an output parameter!!!
updater.checkForChanges(updateAvailable);
if (updateAvailable) {
qDebug() << "Update available";
emit this->updateAvailable();
}
});
}
void AppUpdater::doUpdate()
{
if (appImagePath.isEmpty()) {
qWarning() << "Self-updates disabled: No app file provided.";
return;
}
if (updateHelper !=nullptr)
delete updateHelper;
updateHelper = new appimage::update::Updater(appImagePath.toStdString());
updateHelper->start();
progressCheckTimer.setInterval(200);
progressCheckTimer.start();
connect(&progressCheckTimer, &QTimer::timeout, this, &AppUpdater::checkUpdateProgress);
}
void AppUpdater::doRestart()
{
QProcess::startDetached("pkill", {"pling-store"});
if (updateHelper != nullptr) {
std::string pathToNewFile;
updateHelper->pathToNewFile(pathToNewFile);
if (!pathToNewFile.empty()) {
QString path = QString::fromStdString(pathToNewFile);
QFile::setPermissions(path, QFileDevice::ReadUser | QFileDevice::ExeUser);
QProcess proc;
proc.setProgram(path);
proc.setEnvironment(getCleanSystemEnvironment());
qDebug() << proc.environment();
if (proc.startDetached()) {
updateDialog.accept();
} else
updateDialog.showErrorMessage("Unable to start: " + path);
}
}
}
void AppUpdater::stop()
{
}
void AppUpdater::checkUpdateProgress()
{
using namespace appimage::update;
auto state = updateHelper->state();
switch (state) {
case Updater::INITIALIZED:
break;
case Updater::RUNNING:
double progress;
updateHelper->progress(progress);
updateDialog.showProgress(progress*100);
break;
case Updater::STOPPING:
break;
case Updater::SUCCESS:
updateDialog.showCompletionMessage();
progressCheckTimer.stop();
break;
case Updater::ERROR:
updateDialog.showErrorMessage(tr("Update failed"));
progressCheckTimer.stop();
break;
}
}
QStringList AppUpdater::getCleanSystemEnvironment()
{
QString appDirPath = qgetenv("APPDIR");
QProcessEnvironment systenEnvironemnt = QProcessEnvironment::systemEnvironment();
QProcessEnvironment processEnvironment;
for (QString key: systenEnvironemnt.keys()) {
QString value = systenEnvironemnt.value(key);
QStringList oldValue = value.split(":");
QStringList newVaule;
for (const QString &valueSection: oldValue)
if (!valueSection.contains(appDirPath))
newVaule << valueSection;
if (!newVaule.empty())
processEnvironment.insert(key, newVaule.join(":"));
}
return processEnvironment.toStringList();
// return {"DISPLAY=:0"};
}
#pragma once
#include <QObject>
#include <QTimer>
#include "appupdatedialog.h"
namespace appimage {
namespace update {
class Updater;
}
}
class AppUpdater : public QObject
{
Q_OBJECT
public:
explicit AppUpdater(const QString &appImagePath, QObject *parent = nullptr);
void setSilentLookup(bool value);
signals:
void updateAvailable();
void restartApp(QString appPath);
public slots:
void doUpdateLookUp();
void doUpdate();
void doRestart();
void stop();
protected slots:
void checkUpdateProgress();
private:
/**
* @brief Clean reference to APPDIR from the environment
* @return
*/
QStringList getCleanSystemEnvironment();
bool silentLookup;
QString appImagePath;
appimage::update::Updater * updateHelper;
AppUpdateDialog updateDialog;
QTimer progressCheckTimer;
};
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment