diff --git a/Example/example.cpp b/Example/example.cpp index f7a51e6..1619cb9 100644 --- a/Example/example.cpp +++ b/Example/example.cpp @@ -63,6 +63,9 @@ void Example::checkForUpdates() { updater->setReferenceUrl("https://raw.githubusercontent.com/alex-97/" "QSimpleUpdater/Files-for-example-project/current_version.txt"); + // Tell the updater where to download the update, its recommended to use direct links + updater->setDownloadUrl("https://github.com/alex-97/QSimpleUpdater/archive/master.zip"); + // Finally, check for updates... updater->checkForUpdates(); } @@ -81,6 +84,7 @@ void Example::onCheckingFinished() { QMessageBox::information(this, tr("Update available"), tr("There's a newer version available! The latest version is ") + updater->latestVersion()); + updater->downloadLatestVersion(); } // The installed version is equal or greater to the "official" latest version, diff --git a/QSimpleUpdater/qsimpleupdater.pri b/QSimpleUpdater/qsimpleupdater.pri index 2d8a49d..17c59b1 100755 --- a/QSimpleUpdater/qsimpleupdater.pri +++ b/QSimpleUpdater/qsimpleupdater.pri @@ -8,8 +8,10 @@ QT += network -HEADERS += $$PWD/src/qsimpleupdater.h -SOURCES += $$PWD/src/qsimpleupdater.cpp +HEADERS += $$PWD/src/qsimpleupdater.h \ + $$PWD/src/dialogs/download_dialog.h +SOURCES += $$PWD/src/qsimpleupdater.cpp \ + $$PWD/src/dialogs/download_dialog.cpp OTHER_FILES += $$PWD/src/QSimpleUpdater INCLUDEPATH += $$PWD/src @@ -22,3 +24,9 @@ win32* { CONFIG += openssl-linked LIBS += -L$$PWD/dependencies/win32/ -llibeay32 } + +RESOURCES += \ + $$PWD/res/res.qrc + +FORMS += \ + $$PWD/src/dialogs/download_dialog.ui diff --git a/QSimpleUpdater/res/internet_icon.png b/QSimpleUpdater/res/internet_icon.png new file mode 100644 index 0000000..8fd54fb Binary files /dev/null and b/QSimpleUpdater/res/internet_icon.png differ diff --git a/QSimpleUpdater/res/res.qrc b/QSimpleUpdater/res/res.qrc new file mode 100644 index 0000000..aebed36 --- /dev/null +++ b/QSimpleUpdater/res/res.qrc @@ -0,0 +1,5 @@ + + + internet_icon.png + + diff --git a/QSimpleUpdater/src/dialogs/download_dialog.cpp b/QSimpleUpdater/src/dialogs/download_dialog.cpp new file mode 100644 index 0000000..3eed979 --- /dev/null +++ b/QSimpleUpdater/src/dialogs/download_dialog.cpp @@ -0,0 +1,188 @@ +// +// This file is part of QSimpleUpdater +// +// Copyright (c) 2014 Alex Spataru +// +// Please check the license.txt file for more information. +// + +#include "download_dialog.h" +#include "ui_download_dialog.h" + +DownloadDialog::DownloadDialog(QWidget *parent) : + QWidget(parent), + ui(new Ui::DownloadDialog) +{ + // Setup the UI + ui->setupUi(this); + ui->installButton->setEnabled(false); + + // Connect SIGNALS/SLOTS + connect(ui->installButton, SIGNAL(clicked()), this, SLOT(openDownload())); + connect(ui->cancelButton, SIGNAL(clicked()), this, SLOT(cancelDownload())); + + // Initialize the network access manager + m_manager = new QNetworkAccessManager(this); + + // Avoid SSL issues + connect(m_manager, SIGNAL(sslErrors(QNetworkReply*,QList)), + this, SLOT(ignoreSslErrors(QNetworkReply*,QList))); +} + +DownloadDialog::~DownloadDialog() +{ + delete ui; +} + +void DownloadDialog::beginDownload(const QUrl &url) +{ + // Reset the UI + ui->progressBar->setValue(0); + ui->installButton->setEnabled(false); + ui->downloadLabel->setText(tr("Downloading update...")); + + // Begin the download + m_reply = m_manager->get(QNetworkRequest(url)); + + // Update the progress bar value automatically + connect(m_reply, SIGNAL(downloadProgress(qint64,qint64)), + this, SLOT(updateProgress(qint64,qint64))); + + // Write the file to the hard disk once the download is finished + connect(m_reply, SIGNAL(finished()), this, SLOT(downloadFinished())); + + // Show the dialog + showNormal(); +} + +void DownloadDialog::openDownload() +{ + if (!m_path.isEmpty()) { + QString url = m_path; + + // Build a correct URL to open local files + if (url.startsWith("/")) + url = "file://" + url; + else + url = "file:///" + url; + + // Let the system open the downloaded file + QDesktopServices::openUrl(url); + } + + else { + qWarning() << "QSimpleUpdater: cannot open downloaded file!"; + } +} + +void DownloadDialog::cancelDownload() +{ + // Cancel download + m_reply->abort(); + + // Close the dialog + close(); +} + +void DownloadDialog::downloadFinished() +{ + // Setup the UI + ui->installButton->setEnabled(true); + + // Write the file + QByteArray data = m_reply->readAll(); + + if (!data.isEmpty()) { + // Create a QFile with the name of the downloaded file + QStringList list = m_reply->url().toString().split("/"); + QFile file(QDir::tempPath() + "/" + list.at(list.count() - 1)); + + // Write download data to the opened file + if (file.open(QIODevice::WriteOnly)) { + file.write(data); + m_path = file.fileName(); + } + + // Show a warning if we cannot open the file for writting + else { + qWarning() << "QSimpleUpdater: cannot write downloaded data!"; + } + + // Close the file + file.close(); + } + + else { + qWarning() << "QSimpleUpdater: invalid download data!"; + } +} + +void DownloadDialog::updateProgress(qint64 received, qint64 total) +{ + // We know the size of the download, so we can calculate the progress.... + if (total > 0) { + ui->progressBar->setMinimum(0); + ui->progressBar->setMaximum(100); + + // Calculate and show download progress + int _progress = (int) ((received * 100) / total); + ui->progressBar->setValue(_progress); + ui->downloadLabel->setText(tr("Downloading update (%1%)...").arg(_progress)); + + // Get size information + QString _total_string; + QString _received_string; + + float _total = total; + float _received = received; + + // Calculate the lenght unit for + // the total size of the download + if (_total < 1024) { + _total_string = tr("%1 bytes").arg(_total); + } else if (_total < 1024 * 1024) { + _total = roundNumber(_total / 1024); + _total_string = tr("%1 KB").arg(_total); + } else { + _total = roundNumber(_total / (1024 * 1024)); + _total_string = tr("%1 MB").arg(_total); + } + + // Calculate the lenght unit for + // the received data of the download + if (_received < 1024) { + _received_string = tr("%1 bytes").arg(_received); + } else if (received < 1024 * 1024) { + _received = roundNumber(_received / 1024); + _received_string = tr("%1 KB").arg(_received); + } else { + _received = roundNumber(_received / (1024 * 1024)); + _received_string = tr("%1 MB").arg(_received); + } + + // Update the progress label + ui->progressLabel->setText(_received_string + " " + tr("of") + " " + _total_string); + } + + // We do not know the size of the download, so we improvise... + else { + + // Enable a marquee animation + ui->progressBar->setValue(-1); + ui->progressBar->setMinimum(0); + ui->progressBar->setMaximum(0); + + ui->downloadLabel->setText(tr("Downloading update...")); + } +} + +void DownloadDialog::ignoreSslErrors (QNetworkReply *reply, const QList &error) +{ + reply->ignoreSslErrors (error); +} + +float DownloadDialog::roundNumber(const float &input) +{ + // Round the input number to two decimal places + return roundf(input * 100) / 100; +} diff --git a/QSimpleUpdater/src/dialogs/download_dialog.h b/QSimpleUpdater/src/dialogs/download_dialog.h new file mode 100644 index 0000000..d3815ac --- /dev/null +++ b/QSimpleUpdater/src/dialogs/download_dialog.h @@ -0,0 +1,44 @@ +#ifndef DOWNLOAD_DIALOG_H +#define DOWNLOAD_DIALOG_H + +#include +#include +#include +#include +#include + +#include + +namespace Ui { +class DownloadDialog; +} + +class DownloadDialog : public QWidget +{ + Q_OBJECT + +public: + explicit DownloadDialog(QWidget *parent = 0); + ~DownloadDialog(); + + void beginDownload(const QUrl &url); + +private slots: + void openDownload(); + void cancelDownload(); + void downloadFinished(); + void updateProgress(qint64 received, qint64 total); + void ignoreSslErrors(QNetworkReply *reply, const QList &error); + +private: + Ui::DownloadDialog *ui; + + QString m_path; + + QNetworkReply *m_reply; + QNetworkAccessManager *m_manager; + + float roundNumber(const float &input); +}; + +#endif diff --git a/QSimpleUpdater/src/dialogs/download_dialog.ui b/QSimpleUpdater/src/dialogs/download_dialog.ui new file mode 100644 index 0000000..eab0c2d --- /dev/null +++ b/QSimpleUpdater/src/dialogs/download_dialog.ui @@ -0,0 +1,144 @@ + + + DownloadDialog + + + + 0 + 0 + 385 + 168 + + + + + 0 + 0 + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + Updater + + + + 0 + + + 12 + + + 0 + + + 0 + + + 0 + + + + + + + + :/icons/internet_icon.png + + + Qt::AlignCenter + + + + + + + + + + + + + + 75 + true + + + + Downloading update... + + + + + + + 0 + + + false + + + + + + + 0 kb / 0 kb + + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Cancel + + + + + + + Install + + + + + + + + + + + + + + + + diff --git a/QSimpleUpdater/src/qsimpleupdater.cpp b/QSimpleUpdater/src/qsimpleupdater.cpp index 0fbf471..3503b1e 100755 --- a/QSimpleUpdater/src/qsimpleupdater.cpp +++ b/QSimpleUpdater/src/qsimpleupdater.cpp @@ -8,25 +8,23 @@ #include "qsimpleupdater.h" -QSimpleUpdater::QSimpleUpdater(QObject *parent) : QObject(parent) +QSimpleUpdater::QSimpleUpdater(QObject *parent) + : QObject(parent) + , m_changelog_downloaded(false) + , m_version_check_finished(false) + , m_new_version_available(false) { - m_new_version_available = false; + m_downloadDialog = new DownloadDialog(); } -QString QSimpleUpdater::changeLog() { - // Return the contents of the downloaded changelog - if (!m_changelog.isEmpty()) { - return m_changelog; - } - - // If the changelog is empty, we issue a warning message in - // the console and return an empty string - else { +// Return the contents of the downloaded changelog +QString QSimpleUpdater::changeLog() const { + if (m_changelog.isEmpty()) { qWarning() << "QSimpleUpdater: change log is empty," << "did you call setChangelogUrl() and checkForUpdates()?"; - return NULL; } + return m_changelog; } void QSimpleUpdater::checkForUpdates() { @@ -56,38 +54,7 @@ void QSimpleUpdater::checkForUpdates() { } } -QString QSimpleUpdater::latestVersion() { - // Return the application version referenced by the string - // that we downloaded - if (!m_latest_version.isEmpty()) { - return m_latest_version; - } - - // Issue a warning message in the case that the downloaded - // application version string is empty - else { - qWarning() << "QSimpleUpdater: latest version is empty," - << "did you call checkForUpdates() and setReferenceUrl()?"; - return NULL; - } -} - -QString QSimpleUpdater::installedVersion() { - // Return the string issued by the user in the setApplicationVersion() function - if (!m_installed_version.isEmpty()) { - return m_installed_version; - } - - // Issue a warning message in the case that the installed application - // version is empty - else { - qWarning() << "QSimpleUpdater: installed version is empty," - << "did you call setApplicationVersion()?"; - return NULL; - } -} - -void QSimpleUpdater::downloadLatestVersion() { +void QSimpleUpdater::openDownloadLink() { // Open the download URL in a web browser if (!m_download_url.isEmpty()) { QDesktopServices::openUrl(m_download_url); @@ -100,59 +67,87 @@ void QSimpleUpdater::downloadLatestVersion() { } } -bool QSimpleUpdater::newerVersionAvailable() { +// Return the application version referenced by the string +// that we downloaded +QString QSimpleUpdater::latestVersion() const { + if (m_latest_version.isEmpty()) { + qWarning() << "QSimpleUpdater: latest version is empty," + << "did you call checkForUpdates() and setReferenceUrl()?"; + } + return m_latest_version; +} + +// Return the string issued by the user in the setApplicationVersion() function +QString QSimpleUpdater::installedVersion() const { + if (m_installed_version.isEmpty()) { + qWarning() << "QSimpleUpdater: installed version is empty," + << "did you call setApplicationVersion()?"; + } + + return m_installed_version; +} + +void QSimpleUpdater::downloadLatestVersion() { + // Show the download dialog + if (!m_download_url.isEmpty()) { + m_downloadDialog->beginDownload(m_download_url); + } + + // The m_download_url is empty, so we issue another warning message + else { + qWarning() << "QSimpleUpdater: cannot download latest version," + << "did you call setDownloadUrl() and checkForUpdates()?"; + } +} + +bool QSimpleUpdater::newerVersionAvailable() const { return m_new_version_available; } -void QSimpleUpdater::setDownloadUrl(const QString url) { - // Change the download URL if the issued URL is valid +// Change the download URL if the issued URL is valid +void QSimpleUpdater::setDownloadUrl(const QString &url) { + Q_ASSERT(!url.isEmpty()); + if (!url.isEmpty()) { m_download_url.setUrl(url); - } - - // The issued URL is ilegal, so we warn the user - else { + } else { qWarning() << "QSimpleUpdater: input URL cannot be empty!"; } } -void QSimpleUpdater::setReferenceUrl(const QString url) { - // Change the reference URL if the issued URL is valid +// Change the reference URL if the issued URL is valid +void QSimpleUpdater::setReferenceUrl(const QString &url) { + Q_ASSERT(!url.isEmpty()); + if (!url.isEmpty()) { m_reference_url.setUrl(url); - } - - // The issued URL is ilegal, so we warn the user - else { + } else { qWarning() << "QSimpleUpdater: input URL cannot be empty!"; } } -void QSimpleUpdater::setChangelogUrl(const QString url) { - // Change the changelog URL if the issued URL is valid +// Change the changelog URL if the issued URL is valid +void QSimpleUpdater::setChangelogUrl(const QString &url) { + Q_ASSERT(!url.isEmpty()); + if (!url.isEmpty()) { m_changelog_url.setUrl(url); - } - - // The issued URL is ilegal, so we warn the user - else { + } else { qWarning() << "QSimpleUpdater: input URL cannot be empty!"; } } -void QSimpleUpdater::setApplicationVersion(const QString version) { - // Change the installed application version if the issued string is valid +// Change the installed application version if the issued string is valid +void QSimpleUpdater::setApplicationVersion(const QString &version) { + Q_ASSERT(!version.isEmpty()); + if (!version.isEmpty()) { m_installed_version = version; - } - - // The application version cannot be empty, so we warn the user - else { + } else { qWarning() << "QSimpleUpdater: input string cannot be empty!"; } } - void QSimpleUpdater::checkDownloadedVersion(QNetworkReply *reply) { bool _new_update = false; @@ -261,6 +256,6 @@ void QSimpleUpdater::processDownloadedChangelog(QNetworkReply *reply) { emit checkingFinished(); } -void QSimpleUpdater::ignoreSslErrors (QNetworkReply *reply, QList error) { +void QSimpleUpdater::ignoreSslErrors (QNetworkReply *reply, const QList &error) { reply->ignoreSslErrors (error); } diff --git a/QSimpleUpdater/src/qsimpleupdater.h b/QSimpleUpdater/src/qsimpleupdater.h index ededa7e..edb115a 100755 --- a/QSimpleUpdater/src/qsimpleupdater.h +++ b/QSimpleUpdater/src/qsimpleupdater.h @@ -13,29 +13,32 @@ #include #include +#include "dialogs/download_dialog.h" + class QSimpleUpdater : public QObject { Q_OBJECT public: QSimpleUpdater(QObject *parent = 0); - QString changeLog(); + QString changeLog() const; void checkForUpdates(); - QString latestVersion(); - QString installedVersion(); + void openDownloadLink(); + QString latestVersion() const; + QString installedVersion() const; void downloadLatestVersion(); - bool newerVersionAvailable(); + bool newerVersionAvailable() const; public slots: - void setDownloadUrl(const QString url); - void setReferenceUrl(const QString url); - void setChangelogUrl(const QString url); - void setApplicationVersion(const QString version); + void setDownloadUrl(const QString &url); + void setReferenceUrl(const QString &url); + void setChangelogUrl(const QString &url); + void setApplicationVersion(const QString &version); private slots: void checkDownloadedVersion(QNetworkReply *reply); void processDownloadedChangelog(QNetworkReply *reply); - void ignoreSslErrors(QNetworkReply *reply, QList error); + void ignoreSslErrors(QNetworkReply *reply, const QList &error); signals: void checkingFinished(); @@ -55,6 +58,8 @@ private: bool m_version_check_finished; bool m_new_version_available; + + DownloadDialog *m_downloadDialog; }; #endif