24 #include <qsystemdetection.h>
25 #include <qxmlstream.h>
26 #include <QApplication>
29 #include <QDesktopServices>
30 #include <QLatin1String>
31 #include <QMessageBox>
32 #include <QMessageLogger>
34 #include <QNetworkReply>
35 #include <QNetworkRequest>
36 #include <QStaticStringData>
37 #include <QStringData>
38 #include <QStringDataPtr>
39 #include <QStringList>
43 #include <QJsonDocument>
44 #include <QJsonObject>
46 #include <QRegularExpression>
47 #include <QStandardPaths>
50 #include <QtConcurrent/QtConcurrent>
51 #include "../ifc/exception/vexception.h"
52 #include "../ifc/xml/vabstractconverter.h"
53 #include "../vmisc/projectversion.h"
54 #include "../vmisc/vabstractapplication.h"
55 #include "../vmisc/vcommonsettings.h"
57 const QString
defaultFeedURL = QStringLiteral(
"https://api.github.com/repos/FashionFreedom/Seamly2D/releases/latest");
84 m_silentAsMuchAsItCouldGet(true), m_feedURL(), m_qnam(), m_reply(nullptr),
85 m_httpRequestAborted(false), m_dropOnFinish(true) {
115 <<
"Please set feed URL via setFeedURL() before calling CheckForUpdates().";
123 if (QCoreApplication::organizationName().isEmpty()) {
125 <<
"QApplication::organizationName is not set. Please do that.";
128 if (QCoreApplication::organizationDomain().isEmpty()) {
130 <<
"QApplication::organizationDomain is not set. Please do that.";
135 if (QCoreApplication::applicationName().isEmpty()) {
137 <<
"QApplication::applicationName is not set. Please do that.";
141 if (QCoreApplication::applicationVersion().isEmpty()) {
143 <<
"QApplication::applicationVersion is not set. Please do that.";
156 if (
qApp->Settings()->GetDateOfLastRemind().daysTo(QDate::currentDate())
181 auto fileSizeHeader =
m_reply->header(QNetworkRequest::ContentLengthHeader).toInt();
182 if (
m_fileSize == 0 && fileSizeHeader > 1000000) {
188 QNetworkRequest request;
189 request.setHeader(QNetworkRequest::ContentTypeHeader,
190 QStringLiteral(
"application/text"));
191 request.setHeader(QNetworkRequest::UserAgentHeader,
192 QCoreApplication::applicationName());
194 request.setSslConfiguration(QSslConfiguration::defaultConfiguration());
198 QDir downloadDir = QStandardPaths::writableLocation(QStandardPaths::DownloadLocation);
201 auto downloadedFile =
new QFile(downloadDir.filePath(name),
this);
202 if(downloadedFile->exists() && !downloadedFile->remove()){
203 showErrorDialog(tr(
"Unable to get exclusive access to file \n%1\nPossibly the file is already being downloaded.").arg(downloadDir.filePath(name)),
false);
206 bool isOpen = downloadedFile->open(QIODevice::WriteOnly | QIODevice::Truncate);
208 showErrorDialog(tr(
"Unable to open file\n%1\nfor writing").arg(downloadDir.filePath(name)),
false);
211 connect(
m_reply.data(), &QNetworkReply::readyRead,
this, [
this, downloadedFile]() {
216 downloadedFile->write(m_reply->readAll());
217 int progress = int(downloadedFile->size() * 100 / m_fileSize);
218 setProgress(progress);
221 connect(
m_reply.data(), SIGNAL(error(QNetworkReply::NetworkError)),
this, SLOT(
networkError(QNetworkReply::NetworkError)));
224 connect(
m_reply.data(), &QNetworkReply::downloadProgress,
this,
225 [
this](qint64 bytesRead, qint64 totalBytes) {
229 if (m_httpRequestAborted) {
233 connect(m_reply.data(), &QNetworkReply::finished,
this, [=]() {
234 fileDownloadFinished(downloadedFile, name);
243 const QVariant redirectionTarget =
244 m_reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
248 tr(
"File download failed: %1.").arg(
m_reply->errorString()),
false);
249 }
else if (not redirectionTarget.isNull()) {
250 downloadedFile->close();
251 const QUrl newUrl =
m_feedURL.resolved(redirectionTarget.toUrl());
253 showInformationDialog(tr(
"Download has started, the installer will open once it's finished downloading"),
false);
259 downloadedFile->write(
m_reply->readAll());
260 downloadedFile->close();
261 auto fileInfo = QFileInfo(*downloadedFile);
264 QDesktopServices::openUrl(QUrl::fromLocalFile(fileInfo.dir().absolutePath()));
266 auto res = proc.startDetached(QDir::toNativeSeparators(fileInfo.absoluteFilePath()), QStringList());
267 auto err = proc.error();
268 qDebug() << res <<
" " << err;
271 QDesktopServices::openUrl(QUrl::fromLocalFile(fileInfo.absoluteFilePath()));
274 downloadedFile->deleteLater();
281 QNetworkRequest request;
282 request.setHeader(QNetworkRequest::ContentTypeHeader,
283 QStringLiteral(
"application/text"));
284 request.setHeader(QNetworkRequest::UserAgentHeader,
285 QCoreApplication::applicationName());
287 request.setSslConfiguration(QSslConfiguration::defaultConfiguration());
291 connect(
m_reply.data(), &QNetworkReply::downloadProgress,
this,
292 [
this](qint64 bytesRead, qint64 totalBytes) {
296 if (m_httpRequestAborted) {
300 connect(m_reply.data(), &QNetworkReply::finished,
this,
319 const QVariant redirectionTarget =
320 m_reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
324 tr(
"Feed download failed: %1.").arg(
m_reply->errorString()),
false);
325 }
else if (not redirectionTarget.isNull()) {
326 const QUrl newUrl =
m_feedURL.resolved(redirectionTarget.toUrl());
334 auto jsonDoc = QJsonDocument::fromJson(
m_reply->readAll());
335 qDebug() <<
"Response is a JSON object:" << jsonDoc.isObject();
336 if (jsonDoc.isObject()) {
337 auto tag = jsonDoc.object()[
"tag_name"].toString();
338 qDebug() <<
"Found the following tag" << tag;
344 if (
showConfirmationDialog(tr(
"A new release %1 is available.\nDo you want to download it?").arg(tag),
true))
358 qDebug() <<
"current application version" << QCoreApplication::applicationVersion();
361 auto searchPattern =
"AppImage";
364 auto searchPattern =
"macos";
367 auto searchPattern =
"windows";
369 auto searchPattern =
"win32";
374 for (
const QJsonValueRef asset : assets) {
375 auto name = asset.toObject()[
"name"].toString();
376 qDebug() <<
"Checking" << searchPattern <<
"against" << name;
377 if (name.contains(searchPattern,
378 Qt::CaseInsensitive)) {
379 QUrl downloadableUrl =
380 asset.toObject()[
"browser_download_url"].toString();
388 bool showEvenInSilentMode) {
390 if (not showEvenInSilentMode) {
396 QMessageBox dlFailedMsgBox;
397 dlFailedMsgBox.setIcon(QMessageBox::Critical);
398 dlFailedMsgBox.setText(tr(
"Error"));
399 dlFailedMsgBox.setInformativeText(message);
400 dlFailedMsgBox.exec();
405 bool showEvenInSilentMode) {
407 if (not showEvenInSilentMode) {
413 QMessageBox dlInformationMsgBox;
414 dlInformationMsgBox.setIcon(QMessageBox::Information);
415 dlInformationMsgBox.setText(tr(
"Information"));
416 dlInformationMsgBox.setInformativeText(message);
417 dlInformationMsgBox.exec();
421 bool showEvenInSilentMode) {
423 if (not showEvenInSilentMode) {
429 QMessageBox dlInformationMsgBox;
430 dlInformationMsgBox.setIcon(QMessageBox::Information);
431 dlInformationMsgBox.setText(tr(
"Information"));
432 dlInformationMsgBox.setInformativeText(message);
433 dlInformationMsgBox.setStandardButtons(QMessageBox::Yes
436 return QMessageBox::Yes == dlInformationMsgBox.exec();
441 const auto releaseVersion = releaseTag.mid(1).split(
'.');
442 const auto currentVersion = QCoreApplication::applicationVersion().split(
'.');
443 for (
int i = 0; i < releaseVersion.length(); i++) {
444 if (releaseVersion[i].toInt() > currentVersion[i].toInt())
bool CheckForUpdatesNotSilent()
bool showConfirmationDialog(const QString &message, bool showEvenInSilentMode=false)
bool releaseIsNewer(const QString &releaseTag)
void cancelDownloadFeed()
void SetDropOnFinish(bool value)
bool CheckForUpdates(bool silentAsMuchAsItCouldGet=true)
void SetFeedURL(const QUrl &feedURL)
QNetworkAccessManager m_qnam
bool m_httpRequestAborted
void httpFeedDownloadFinished()
QPointer< QNetworkReply > m_reply
void startDownloadFeed(const QUrl &url)
bool CheckForUpdatesSilent()
void getPLatformSpecificInstaller(QJsonArray assets)
QString GetFeedURL() const
static QPointer< FvUpdater > m_Instance
void fileDownloadFinished(QFile *downloadedFile, QString name)
static FvUpdater * sharedUpdater()
void showInformationDialog(const QString &message, bool showEvenInSilentMode=false)
bool m_silentAsMuchAsItCouldGet
void showErrorDialog(const QString &message, bool showEvenInSilentMode=false)
bool IsDropOnFinish() const
void networkError(QNetworkReply::NetworkError)
void startDownloadFile(QUrl url, QString name)
const QString defaultFeedURL