Add updater

This commit is contained in:
Tasos Sahanidis
2018-10-08 00:45:32 +03:00
parent a2fc54cf3e
commit 89e95b53b4
13 changed files with 596 additions and 4 deletions
+1
View File
@@ -100,6 +100,7 @@ cmake_dependent_option(WITH_WAVE "Build with 'Wave' animation." ON "WITH
cmake_dependent_option(WITH_INVADERS "Build with 'Invaders' animation." ON "WITH_ANIMATIONS;WITH_GUI" OFF)
# Options: daemon
option(DISABLE_UPDATER "Disable the ckb-next updater. This option should only be enabled during packaging." OFF)
option(DEBUG_USB_SEND "Show the contents of USB packets being sent to device." OFF)
option(DEBUG_USB_RECV "Show the contents of USB packets being received from device through usbrecv()." OFF)
option(DEBUG_USB_INPUT "Show the contents of USB packets being received from device through the input thread." OFF)
+2
View File
@@ -3,6 +3,8 @@
#define CKB_NEXT_PROFILE_VER ${ckb-next_PROFILE_VER}
#cmakedefine DISABLE_UPDATER
#cmakedefine DEBUG_USB_SEND
#cmakedefine DEBUG_USB_RECV
#cmakedefine DEBUG_USB_INPUT
+5
View File
@@ -120,6 +120,8 @@ if (MACOS OR LINUX)
autorun.cpp
ckbsettings.cpp
ckbsettingswriter.cpp
ckbupdater.cpp
ckbupdaterwidget.cpp
colorbutton.cpp
colormap.cpp
extrasettingswidget.cpp
@@ -159,6 +161,8 @@ if (MACOS OR LINUX)
autorun.h
ckbsettings.h
ckbsettingswriter.h
ckbupdater.h
ckbupdaterwidget.h
colorbutton.h
colormap.h
extrasettingswidget.h
@@ -194,6 +198,7 @@ if (MACOS OR LINUX)
settingswidget.h
animadddialog.ui
animsettingdialog.ui
ckbupdaterwidget.ui
extrasettingswidget.ui
fwupgradedialog.ui
gradientdialog.ui
+77
View File
@@ -0,0 +1,77 @@
#include "ckbupdater.h"
#include "kbmanager.h"
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QRegularExpression>
#include <QRegularExpressionMatch>
CkbUpdater::CkbUpdater(QObject *parent) : QObject(parent), _accessManager(new QNetworkAccessManager(this)), _latestVerStr(QString()){
connect(_accessManager, &QNetworkAccessManager::finished, this, &CkbUpdater::finished); // FIXME: use finished() on QNetworkReply instead
}
void CkbUpdater::checkForNewVersion(){
_accessManager->get(QNetworkRequest(QUrl("https://raw.githubusercontent.com/ckb-next/ckb-next/master/VERSION.cmake")));
}
// Read the VERSION. If there's a newer one, fetch the changelog and parse it.
void CkbUpdater::finished(QNetworkReply* reply){
if(reply->error() == QNetworkReply::NoError) {
QByteArray bytes = reply->readAll();
if(reply->url().url().endsWith("VERSION.cmake")) {
_latestVerStr = getVersionString(&bytes);
float latestVer = KbManager::parseVersionString(_latestVerStr);
float currentVer = KbManager::ckbGuiVersionF();
if(latestVer <= currentVer) {
emit checkedForNewVer(QString(), QString());
} else
_accessManager->get(QNetworkRequest(QUrl("https://raw.githubusercontent.com/ckb-next/ckb-next/master/CHANGELOG.md")));
} else
emit checkedForNewVer(_latestVerStr, parseChangeLog(&bytes));
} else
emit checkedForNewVer(QString(), QString());
reply->deleteLater();
}
QString CkbUpdater::getVersionString(QByteArray* bytes){
// return "0.3.2"; // Uncomment and set a higher version to force the update message to pop up
QString ret;
if(bytes->count() > 1000)
return ret;
QString str(*bytes);
QRegularExpression re("ckb-next_VERSION +\"([0-9\\.]+)\"", QRegularExpression::MultilineOption);
QRegularExpressionMatch match = re.match(str);
if(match.hasMatch())
ret = match.captured(1);
return ret;
}
QString CkbUpdater::parseChangeLog(QByteArray* bytes){
QByteArray lbytes = bytes->left(4000); // Don't read too much data
QList<QByteArray> bytelist = lbytes.split('\n');
bool append = false;
QString changelog;
for(int i = 0; i < bytelist.count(); i++) {
if(bytelist.at(i).startsWith("## [v")) {
if(append)
break;
append = true;
continue;
}
if(bytelist.at(i).startsWith("[Full Changelog]")) {
i++;
continue;
}
if(append) {
changelog.append(bytelist.at(i));
changelog.append('\n');
}
}
return changelog;
}
+28
View File
@@ -0,0 +1,28 @@
#ifndef CKBUPDATER_H
#define CKBUPDATER_H
#include <QObject>
#include <QNetworkAccessManager>
#include <QNetworkReply>
class CkbUpdater : public QObject
{
Q_OBJECT
public:
explicit CkbUpdater(QObject *parent = 0);
void checkForNewVersion();
private:
QNetworkAccessManager* _accessManager;
QString getVersionString(QByteArray* bytes);
QString _latestVerStr;
QString parseChangeLog(QByteArray* bytes);
signals:
void checkedForNewVer(QString ver, QString changelog);
private slots:
void finished(QNetworkReply* reply);
};
#endif // CKBUPDATER_H
+143
View File
@@ -0,0 +1,143 @@
#include "ckbupdaterwidget.h"
#include "ui_ckbupdaterwidget.h"
#include "kbmanager.h"
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QNetworkRequest>
#include <QBitmap>
#include <QCryptographicHash>
#include <QMessageBox>
CkbUpdaterDialog::CkbUpdaterDialog(QString ver, QString changelog, QWidget *parent) :
QDialog(parent),
ui(new Ui::CkbUpdaterWidget), _version(ver), _changelog(changelog), _manager(nullptr), _redirectCount(0), _url(QString("https://github.com/ckb-next/ckb-next/")), _quitApp(false){
ui->setupUi(this);
QPixmap pixmap = QPixmap(":/img/ckb-next.png").copy(QRect(0, 125, 512, 262)).scaledToWidth(70, Qt::SmoothTransformation);
ui->iconLabel->setPixmap(pixmap);
ui->iconLabel->setMask(pixmap.mask());
ui->versionLabel->setText(QString("ckb-next v") + _version);
#ifdef Q_OS_MAC
// dmg download
_url.append("releases/download/v");
_url.append(_version);
_url.append("/ckb-next_v");
_url.append(_version);
#ifdef OS_MAC_LEGACY
_url.append("-legacy");
#endif
_url.append(".dmg");
#else
// Source download
_url.append("archive/v");
_url.append(_version);
_url.append(".tar.gz");
#endif
ui->changelogLabel->setText("<a href=\"https://github.com/ckb-next/ckb-next/releases/tag/v" + _version + "\" style=\"text-decoration: none;\">v" + _version + " full changelog</a><br /><a href=\"" + _url + "\" style=\"text-decoration: none;\">v" + _version + " manual download</a>");
ui->changelogTextEdit->setPlainText(changelog);
ui->progressBar->setValue(0);
ui->progressBar->hide();
qDebug() << _url;
}
CkbUpdaterDialog::~CkbUpdaterDialog(){
if(_manager != nullptr)
_manager->deleteLater();
delete ui;
}
void CkbUpdaterDialog::on_remindMeLaterButton_clicked(){
this->close();
}
void CkbUpdaterDialog::on_updateButton_clicked(){
ui->remindMeLaterButton->setEnabled(false);
ui->updateButton->setEnabled(false);
ui->updateButton->setText("Downloading");
if(_manager != nullptr)
return;
ui->progressBar->show();
_manager = new QNetworkAccessManager(this);
connect(_manager, &QNetworkAccessManager::finished, this, &CkbUpdaterDialog::downloadFinished);
_reply = _manager->get(QNetworkRequest(QUrl(_url)));
connect(_reply, &QNetworkReply::downloadProgress, this, &CkbUpdaterDialog::downloadProgress);
}
void CkbUpdaterDialog::downloadProgress(qint64 bytesReceived, qint64 bytesTotal){
// Redirects are handled elsewhere
if(_reply->hasRawHeader("Location"))
return;
ui->progressBar->setRange(0, bytesTotal);
ui->progressBar->setValue(bytesReceived);
}
void CkbUpdaterDialog::downloadFinished(QNetworkReply* reply){
ui->progressBar->setRange(0, 0);
_redirectCount++;
if(_redirectCount > 3){
reply->deleteLater();
return;
}
QUrl redirect = reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl();
if(redirect.isEmpty()) {
QByteArray download = reply->readAll(); // We can afford to keep the downloaded file in ram
QCryptographicHash hash(QCryptographicHash::Sha256);
hash.addData(download);
QDir dir;
dir.mkdir("/tmp/ckb-next/");
#ifdef Q_OS_MAC
QString suffix(".dmg");
#else
QString suffix(".tar.gz");
#endif
QFile outFile(QString("/tmp/ckb-next/v") + _version + suffix);
if(outFile.open(QFile::WriteOnly)) {
outFile.write(download);
outFile.close();
}
reply->deleteLater();
QMessageBox::information(this, "Download Complete", QString(tr("Update has been downloaded to /tmp/ckb-next/v")
+ _version + suffix
#ifndef Q_OS_MAC
+ tr("<br />You will need to manually compile the source code.")
#endif
+ tr("<br /><br />Optionally, for added security, please check that the following value exists at the bottom of <a href=\"https://github.com/ckb-next/ckb-next/releases/tag/v")
+ _version
+ tr("\">this page</a>.<br /><pre>%1</pre><br />Press OK to continue.")).arg(QString(hash.result().toHex())));
QProcess process;
#if defined(Q_OS_MAC)
// Unmount any old ckb-next volume that might exist
QStringList detachArgs("detach");
detachArgs << QString("/Volumes/ckb-next/");
process.execute("hdiutil", detachArgs);
// Mount the new image
QStringList attachArgs("attach");
attachArgs << QString("/tmp/ckb-next/v") + _version + suffix;
process.execute("hdiutil", attachArgs);
// Open the mpkg inside
process.execute("open", QStringList(QString("/Volumes/ckb-next/ckb-next.mpkg")));
#elif defined(Q_OS_LINUX)
QStringList tarpit("-xzf");
tarpit << "/tmp/ckb-next/v" + _version + suffix;
tarpit << "-C";
tarpit << "/tmp/ckb-next/";
process.execute("tar", tarpit);
process.execute("xdg-open", QStringList(QString("/tmp/ckb-next/ckb-next-") + _version ));
#endif
// Close the GUI
qApp->quit();
} else {
QString host = redirect.host();
if(host != "github-production-release-asset-2e65be.s3.amazonaws.com" && host != "codeload.github.com") {
reply->deleteLater();
return;
}
_reply = _manager->get(QNetworkRequest(redirect));
connect(_reply, &QNetworkReply::downloadProgress, this, &CkbUpdaterDialog::downloadProgress);
}
}
+36
View File
@@ -0,0 +1,36 @@
#ifndef CKBUPDATERWIDGET_H
#define CKBUPDATERWIDGET_H
#include <QDialog>
#include <QWidget>
#include <QNetworkAccessManager>
namespace Ui {
class CkbUpdaterWidget;
}
class CkbUpdaterDialog : public QDialog
{
Q_OBJECT
public:
explicit CkbUpdaterDialog(QString ver, QString changelog, QWidget *parent = 0);
~CkbUpdaterDialog();
private slots:
void on_remindMeLaterButton_clicked();
void on_updateButton_clicked();
void downloadProgress(qint64 bytesReceived, qint64 bytesTotal);
void downloadFinished(QNetworkReply* reply);
private:
Ui::CkbUpdaterWidget *ui;
QString _version, _changelog;
QNetworkAccessManager* _manager;
QNetworkReply* _reply;
int _redirectCount;
QString _url;
bool _quitApp;
};
#endif // CKBUPDATERWIDGET_H
+182
View File
@@ -0,0 +1,182 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>CkbUpdaterWidget</class>
<widget class="QWidget" name="CkbUpdaterWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>654</width>
<height>410</height>
</rect>
</property>
<property name="windowTitle">
<string>Software Updater</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<spacer name="horizontalSpacer_2">
<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="QLabel" name="iconLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string/>
</property>
<property name="textFormat">
<enum>Qt::RichText</enum>
</property>
<property name="alignment">
<set>Qt::AlignHCenter|Qt::AlignTop</set>
</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>
</layout>
</item>
<item>
<widget class="QLabel" name="versionLabel">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string>ckb-next v</string>
</property>
<property name="alignment">
<set>Qt::AlignBottom|Qt::AlignHCenter</set>
</property>
</widget>
</item>
<item>
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="newVersionLabel">
<property name="text">
<string>A new version of ckb-next has been released.</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QPlainTextEdit" name="changelogTextEdit">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>500</width>
<height>0</height>
</size>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="changelogLabel">
<property name="text">
<string>&lt;a href=&quot;#&quot; style=&quot;text-decoration: none;&quot;&gt;v0.3.2 changelog&lt;/a&gt;&lt;br /&gt;&lt;a href=&quot;#&quot; style=&quot;text-decoration: none;&quot;&gt;v0.3.2 manual download&lt;/a&gt;</string>
</property>
<property name="openExternalLinks">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse</set>
</property>
</widget>
</item>
<item>
<widget class="QProgressBar" name="progressBar">
<property name="value">
<number>24</number>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<spacer name="horizontalSpacer_5">
<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="updateButton">
<property name="text">
<string>Update Now</string>
</property>
<property name="autoDefault">
<bool>false</bool>
</property>
<property name="default">
<bool>true</bool>
</property>
<property name="flat">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="remindMeLaterButton">
<property name="text">
<string>Remind Me Later</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
+35 -1
View File
@@ -15,6 +15,11 @@
#include <signal.h>
#include <QProcess>
#ifndef DISABLE_UPDATER
#include "ckbupdater.h"
#include "ckbupdaterwidget.h"
#endif
extern QSharedMemory appShare;
extern QString devpath;
@@ -165,9 +170,23 @@ MainWindow::MainWindow(QWidget *parent) :
showWindow();
dialog.exec();
}
#ifndef DISABLE_UPDATER
if(!CkbSettings::get("Program/DisableAutoUpdCheck", false).toBool())
checkForCkbUpdates();
connect(settingsWidget, &SettingsWidget::checkForUpdates, this, &MainWindow::checkForCkbUpdates);
#endif
}
void MainWindow::toggleTrayIcon(bool visible) {
void MainWindow::checkForCkbUpdates(){
#ifndef DISABLE_UPDATER
updater = new CkbUpdater(this);
connect(updater, &CkbUpdater::checkedForNewVer, this, &MainWindow::checkedForNewVer);
updater->checkForNewVersion();
#endif
}
void MainWindow::toggleTrayIcon(bool visible){
#ifdef USE_LIBAPPINDICATOR
if(useAppindicator)
app_indicator_set_status(indicator, visible ? APP_INDICATOR_STATUS_ACTIVE : APP_INDICATOR_STATUS_PASSIVE);
@@ -404,3 +423,18 @@ void MainWindow::PosixSignalHandler(int signal){
if(ret == -1)
qDebug() << "Error on PosixSignalHandler write";
}
void MainWindow::checkedForNewVer(QString ver, QString changelog){
#ifndef DISABLE_UPDATER
if(!ver.isEmpty()) {
settingsWidget->setUpdateButtonText("Update to v" + ver);
showWindow();
CkbUpdaterDialog updDialog(ver, changelog, this);
updDialog.exec();
} else {
settingsWidget->setUpdateButtonText("Up to date");
}
updater->deleteLater();
settingsWidget->enableUpdateButton();
#endif
}
+10 -3
View File
@@ -10,6 +10,10 @@
#include "settingswidget.h"
#include <QSocketNotifier>
#ifndef DISABLE_UPDATER
#include "ckbupdater.h"
#endif
#ifdef USE_LIBAPPINDICATOR
// 'signals' has to be undefined as GTK has its own signal mechanism
#undef signals
@@ -42,7 +46,6 @@ public:
private:
SettingsWidget* settingsWidget;
QList<KbWidget*> kbWidgets;
QAction* restoreAction;
QAction* closeAction;
@@ -63,23 +66,27 @@ public slots:
void showWindow();
void stateChange(Qt::ApplicationState state);
void quitApp();
void checkForCkbUpdates();
private slots:
void addDevice(Kb* device);
void removeDevice(Kb* device);
void updateVersion();
void checkFwUpdates();
void timerTick();
void iconClicked(QSystemTrayIcon::ActivationReason reason);
void cleanup();
void showFwUpdateNotification(QWidget* widget, float version);
void QSignalHandler();
void checkedForNewVer(QString ver, QString changelog);
private:
Ui::MainWindow *ui;
QSocketNotifier* sigNotifier;
#ifndef DISABLE_UPDATER
CkbUpdater* updater;
#endif
};
#endif // MAINWINDOW_H
+34
View File
@@ -8,11 +8,14 @@
#include <QMessageBox>
#include <QFileDialog>
#include <QDateTime>
#include "mainwindow.h"
#include <ckbnextconfig.h>
extern QString devpath;
// Modifier keys (OS-dependent)
static QStringList modKeys, modNames;
static bool updateRequestedByUser = false;
SettingsWidget::SettingsWidget(QWidget *parent) :
QWidget(parent), devDetect(nullptr),
@@ -87,6 +90,13 @@ SettingsWidget::SettingsWidget(QWidget *parent) :
ui->label_2->setText(labelText);
#endif
#endif
#ifdef DISABLE_UPDATER
ui->autoUpdBox->hide();
ui->pushButton_2->hide();
#else
ui->autoUpdBox->setChecked(!settings.value("DisableAutoUpdCheck", false).toBool());
#endif
}
SettingsWidget::~SettingsWidget(){
@@ -228,3 +238,27 @@ void SettingsWidget::devDetectFinished(int retVal){
void SettingsWidget::devDetectDestroyed(){
devDetect = nullptr;
}
void SettingsWidget::on_autoUpdBox_clicked(bool checked)
{
CkbSettings::set("Program/DisableAutoUpdCheck", !checked);
}
void SettingsWidget::on_pushButton_2_clicked()
{
emit checkForUpdates();
ui->pushButton_2->setEnabled(false);
ui->pushButton_2->setText("Checking...");
updateRequestedByUser = true;
}
void SettingsWidget::enableUpdateButton(){
ui->pushButton_2->setEnabled(true);
}
void SettingsWidget::setUpdateButtonText(QString text){
if(updateRequestedByUser) {
ui->pushButton_2->setText(text);
updateRequestedByUser = false;
}
}
+8
View File
@@ -24,6 +24,12 @@ public:
// Poll for setting updates and save (if necessary)
void pollUpdates();
void enableUpdateButton();
void setUpdateButtonText(QString text);
signals:
void checkForUpdates();
private slots:
void on_pushButton_clicked();
void on_capsBox_activated(int index);
@@ -38,6 +44,8 @@ private slots:
void on_generateReportButton_clicked();
void devDetectFinished(int retVal);
void devDetectDestroyed();
void on_autoUpdBox_clicked(bool checked);
void on_pushButton_2_clicked();
private:
QProcess* devDetect;
+35
View File
@@ -407,6 +407,16 @@
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="autoUpdBox">
<property name="text">
<string>Check for ckb-next updates on startup</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
<item>
@@ -445,6 +455,31 @@
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButton_2">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>142</width>
<height>0</height>
</size>
</property>
<property name="baseSize">
<size>
<width>142</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Check for updates</string>
</property>
</widget>
</item>
</layout>
</item>
<item>