|
|
|
@ -1,12 +1,15 @@ |
|
|
|
#include "updaterdialog.h" |
|
|
|
#include "updaterdialog.h" |
|
|
|
#include "ui_updaterdialog.h" |
|
|
|
|
|
|
|
#include <QApplication> |
|
|
|
#include <QDesktopServices> |
|
|
|
#include <QFileInfo> |
|
|
|
#include <QJsonDocument> |
|
|
|
#include <QJsonObject> |
|
|
|
#include <QMessageBox> |
|
|
|
#include <QMetaEnum> |
|
|
|
#include <cmath> |
|
|
|
#include <QUrl> |
|
|
|
#include <QtGlobal> |
|
|
|
|
|
|
|
#include <private/qzipreader_p.h> |
|
|
|
|
|
|
|
@ -18,16 +21,67 @@ QString APPNAME = ""; |
|
|
|
QString APPDATE = "0"; |
|
|
|
QString MODIFYCNT = "0"; |
|
|
|
|
|
|
|
namespace |
|
|
|
{ |
|
|
|
/**
|
|
|
|
* @brief 比较两个 x.y.z 形式的版本号。 |
|
|
|
* @param left 左侧版本号。 |
|
|
|
* @param right 右侧版本号。 |
|
|
|
* @return left < right 返回 -1,left == right 返回 0,left > right 返回 1。 |
|
|
|
* |
|
|
|
* 设计意图:统一版本比较规则,缺失段按 0 处理,非数字段也按 0 处理并保持保守行为。 |
|
|
|
* 边界条件:空版本号小于任何非空版本号;两者均空则视为相等。 |
|
|
|
*/ |
|
|
|
int compareVersionString(const QString &left, const QString &right) |
|
|
|
{ |
|
|
|
const QStringList leftParts = left.trimmed().split(".", Qt::SkipEmptyParts); |
|
|
|
const QStringList rightParts = right.trimmed().split(".", Qt::SkipEmptyParts); |
|
|
|
const int partCount = qMax(leftParts.count(), rightParts.count()); |
|
|
|
|
|
|
|
for(int i = 0; i < partCount; ++i) |
|
|
|
{ |
|
|
|
const uint leftValue = (i < leftParts.count()) ? leftParts.at(i).toUInt() : 0U; |
|
|
|
const uint rightValue = (i < rightParts.count()) ? rightParts.at(i).toUInt() : 0U; |
|
|
|
if(leftValue < rightValue) |
|
|
|
{ |
|
|
|
return -1; |
|
|
|
} |
|
|
|
if(leftValue > rightValue) |
|
|
|
{ |
|
|
|
return 1; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief 拼接基础 URL 和文件相对路径。 |
|
|
|
* @param baseUrl 远程基础地址,允许带或不带末尾斜杠。 |
|
|
|
* @param appName 应用名目录。 |
|
|
|
* @param fileName 文件名。 |
|
|
|
* @return 标准化后的下载 URL 字符串。 |
|
|
|
*/ |
|
|
|
QString buildDownloadUrl(QString baseUrl, const QString &appName, const QString &fileName) |
|
|
|
{ |
|
|
|
if(!baseUrl.endsWith('/')) |
|
|
|
{ |
|
|
|
baseUrl.append('/'); |
|
|
|
} |
|
|
|
|
|
|
|
return baseUrl + appName + "/" + fileName; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
UpdaterDialog::UpdaterDialog(QWidget *parent) : |
|
|
|
QDialog(parent), |
|
|
|
ui(new Ui::UpdaterDialog) |
|
|
|
{ |
|
|
|
ui->setupUi(this); |
|
|
|
this->setWindowTitle("Updater"); |
|
|
|
ui->btn_update->setEnabled(0); |
|
|
|
ui->btn_update_patch->setEnabled(0); |
|
|
|
setUpdateButtonsEnabled(false); |
|
|
|
|
|
|
|
m_startTime = 0; |
|
|
|
m_totalSize = 0; |
|
|
|
|
|
|
|
m_version = APPVERSION; |
|
|
|
ui->versionInput->setText(m_version); |
|
|
|
@ -36,45 +90,36 @@ UpdaterDialog::UpdaterDialog(QWidget *parent) : |
|
|
|
ui->show->setEnabled(0); |
|
|
|
ui->label_2->setText(""); |
|
|
|
ui->label_3->setText(""); |
|
|
|
// ui->label_4->setText(APPNAME);
|
|
|
|
// ui->versionInput->setEnabled(false);
|
|
|
|
// ui->show->appendPlainText(APPNAME);
|
|
|
|
// ui->show->appendPlainText(APPVERSION);
|
|
|
|
// ui->show->appendPlainText(APPDATE);
|
|
|
|
// ui->show->appendPlainText(MODIFYCNT);
|
|
|
|
|
|
|
|
m_pDownloader = new Downloader(this); |
|
|
|
|
|
|
|
connect(m_pDownloader, &Downloader::doShowInfo, this, [&](const QString & s) |
|
|
|
connect(m_pDownloader, &Downloader::doShowInfo, this, [this](const QString &s) |
|
|
|
{ |
|
|
|
showStatus(s); |
|
|
|
}); |
|
|
|
|
|
|
|
connect(m_pDownloader, &Downloader::onError, this, [&](QNetworkReply::NetworkError e) |
|
|
|
connect(m_pDownloader, &Downloader::onError, this, [this](QNetworkReply::NetworkError e) |
|
|
|
{ |
|
|
|
QMetaEnum metaEnum = QMetaEnum::fromType<QNetworkReply::NetworkError>(); |
|
|
|
QString str = QString(metaEnum.valueToKey(e)); |
|
|
|
const QMetaEnum metaEnum = QMetaEnum::fromType<QNetworkReply::NetworkError>(); |
|
|
|
const QString str = QString::fromLatin1(metaEnum.valueToKey(e)); |
|
|
|
ui->show->appendPlainText(str); |
|
|
|
setUpdateButtonsEnabled(true); |
|
|
|
}); |
|
|
|
|
|
|
|
connect(m_pDownloader, &Downloader::doFinished, this, [&]() |
|
|
|
connect(m_pDownloader, &Downloader::doFinished, this, [this]() |
|
|
|
{ |
|
|
|
uint _end_time = QDateTime::currentSecsSinceEpoch(); |
|
|
|
uint _total_time = _end_time - m_startTime; |
|
|
|
ui->label_3->setText("total time: " + QString::number(_total_time, 'g', 3) + |
|
|
|
"s average speed: " + QString::number((m_totalSize / 1024.0 / _total_time), 'g', 3) + "kb/s"); |
|
|
|
ui->show->appendPlainText("download finished"); |
|
|
|
onDownloadFinished(); |
|
|
|
}); |
|
|
|
|
|
|
|
connect(m_pDownloader, &Downloader::doProgress, this, [&](quint64 received, quint64 total) |
|
|
|
connect(m_pDownloader, &Downloader::doProgress, this, [this](qint64 received, qint64 total) |
|
|
|
{ |
|
|
|
m_totalSize = total; |
|
|
|
if (total > 0) |
|
|
|
m_totalSize = total > 0 ? static_cast<quint64>(total) : 0; |
|
|
|
if(total > 0) |
|
|
|
{ |
|
|
|
ui->progressBar->setMinimum(0); |
|
|
|
ui->progressBar->setMaximum(100); |
|
|
|
ui->progressBar->setValue((received * 100) / total); |
|
|
|
ui->progressBar->setValue(static_cast<int>((received * 100) / total)); |
|
|
|
calculateSizes(received, total); |
|
|
|
calculateTimeRemaining(received, total); |
|
|
|
} |
|
|
|
@ -83,319 +128,348 @@ UpdaterDialog::UpdaterDialog(QWidget *parent) : |
|
|
|
ui->progressBar->setMinimum(0); |
|
|
|
ui->progressBar->setMaximum(0); |
|
|
|
ui->progressBar->setValue(-1); |
|
|
|
ui->show->appendPlainText(tr("Downloading Updates") + "..."); |
|
|
|
ui->label_2->setText(tr("Downloading updates")); |
|
|
|
} |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
|
UpdaterDialog::~UpdaterDialog() |
|
|
|
{ |
|
|
|
delete m_UpdateProc; |
|
|
|
delete ui; |
|
|
|
} |
|
|
|
|
|
|
|
void UpdaterDialog::showStatus(const QString &s) |
|
|
|
{ |
|
|
|
// ui->show->clear();
|
|
|
|
ui->show->appendPlainText(s); |
|
|
|
} |
|
|
|
|
|
|
|
void UpdaterDialog::on_btn_check_clicked() |
|
|
|
{ |
|
|
|
APPNAME = ui->appNameInput->text(); |
|
|
|
if(QString(APPNAME) == "") |
|
|
|
APPNAME = ui->appNameInput->text().trimmed(); |
|
|
|
if(APPNAME.isEmpty()) |
|
|
|
{ |
|
|
|
ui->show->appendPlainText("Please input app name"); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
ui->show->clear(); |
|
|
|
ui->btn_update->setEnabled(0); |
|
|
|
ui->btn_update_patch->setEnabled(0); |
|
|
|
//version
|
|
|
|
m_version = ui->versionInput->text(); |
|
|
|
setUpdateButtonsEnabled(false); |
|
|
|
|
|
|
|
m_version = ui->versionInput->text().trimmed(); |
|
|
|
|
|
|
|
if(!m_pManager) |
|
|
|
{ |
|
|
|
m_pManager = new QNetworkAccessManager(this); |
|
|
|
connect(m_pManager, SIGNAL(finished(QNetworkReply *)), this, SLOT(onCheckReply(QNetworkReply *))); |
|
|
|
} |
|
|
|
QString _url = CHECK_URL.arg(APPNAME); |
|
|
|
qDebug() << APPNAME << _url; |
|
|
|
QNetworkRequest request(_url); |
|
|
|
|
|
|
|
const QString url = CHECK_URL.arg(APPNAME); |
|
|
|
QNetworkRequest request{QUrl(url)}; |
|
|
|
m_pManager->get(request); |
|
|
|
} |
|
|
|
|
|
|
|
bool UpdaterDialog::checkVersion(const QString &_localVersion, const QString &_remoteVersion) |
|
|
|
{ |
|
|
|
//版本号分割,1.1.2分为1 1 2
|
|
|
|
QStringList _list_curr = _localVersion.split("."); |
|
|
|
QStringList _list_lastest = _remoteVersion.split("."); |
|
|
|
|
|
|
|
//检查是否需要更新
|
|
|
|
bool _isNotLatest = false; |
|
|
|
//版本号为空
|
|
|
|
if(_list_curr.count() == 0) |
|
|
|
// 本地版本为空或为 0 时视为首次安装/未知版本,优先下载完整包降低补丁失败风险。
|
|
|
|
if(_localVersion.trimmed().isEmpty() || _localVersion.trimmed() == "0") |
|
|
|
{ |
|
|
|
m_bDownloadFullExe = true; |
|
|
|
return true; |
|
|
|
} |
|
|
|
if(_list_curr.count() != _list_lastest.count()) |
|
|
|
_isNotLatest = true; |
|
|
|
|
|
|
|
for(int i = 0; i < _list_curr.count(); ++i) |
|
|
|
const int versionCompareResult = compareVersionString(_localVersion, _remoteVersion); |
|
|
|
if(versionCompareResult > 0) |
|
|
|
{ |
|
|
|
if(_list_curr[i].toUInt() < _list_lastest[i].toUInt()) |
|
|
|
{ |
|
|
|
_isNotLatest = true; |
|
|
|
} |
|
|
|
return false; |
|
|
|
} |
|
|
|
|
|
|
|
//版本号一致,检查日期
|
|
|
|
if(!_isNotLatest) |
|
|
|
bool isNotLatest = versionCompareResult < 0; |
|
|
|
if(!isNotLatest) |
|
|
|
{ |
|
|
|
// 版本号一致时再比较构建日期和修改计数,兼容同版本热修复包。
|
|
|
|
if(m_lastChangeTime.toUInt() > APPDATE.toUInt()) |
|
|
|
{ |
|
|
|
_isNotLatest = true; |
|
|
|
isNotLatest = true; |
|
|
|
} |
|
|
|
|
|
|
|
//检查修改次数
|
|
|
|
if(m_modifyCnt != "" && m_modifyCnt != " " && MODIFYCNT != "" && MODIFYCNT != " ") |
|
|
|
if(!m_modifyCnt.trimmed().isEmpty() && !MODIFYCNT.trimmed().isEmpty()) |
|
|
|
{ |
|
|
|
if(m_modifyCnt.toUInt() > MODIFYCNT.toUInt()) |
|
|
|
{ |
|
|
|
_isNotLatest = true; |
|
|
|
isNotLatest = true; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
return _isNotLatest; |
|
|
|
return isNotLatest; |
|
|
|
} |
|
|
|
|
|
|
|
void UpdaterDialog::onCheckReply(QNetworkReply *reply) |
|
|
|
{ |
|
|
|
/* There was a network error */ |
|
|
|
if (reply->error() != QNetworkReply::NoError) |
|
|
|
reply->deleteLater(); |
|
|
|
|
|
|
|
if(reply->error() != QNetworkReply::NoError) |
|
|
|
{ |
|
|
|
showStatus("check error"); |
|
|
|
showStatus("check error: " + reply->errorString()); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
/* Try to create a JSON document from downloaded data */ |
|
|
|
QJsonDocument document = QJsonDocument::fromJson(reply->readAll()); |
|
|
|
|
|
|
|
/* JSON is invalid */ |
|
|
|
if (document.isNull()) |
|
|
|
const QJsonDocument document = QJsonDocument::fromJson(reply->readAll()); |
|
|
|
if(document.isNull() || !document.isObject()) |
|
|
|
{ |
|
|
|
showStatus("reply doc is null"); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
/* Get the platform information */ |
|
|
|
QJsonObject updates = document.object().value("updates").toObject(); |
|
|
|
QJsonObject platform = updates.value(PLATFORM).toObject(); |
|
|
|
const QJsonObject updates = document.object().value("updates").toObject(); |
|
|
|
const QJsonObject platform = updates.value(PLATFORM).toObject(); |
|
|
|
if(platform.isEmpty()) |
|
|
|
{ |
|
|
|
showStatus(tr("no update information for platform: %1").arg(PLATFORM)); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
/* Get update information */ |
|
|
|
m_changelog = platform.value("changelog").toString(); |
|
|
|
m_patchVersion = platform.value("patch-version").toString(); |
|
|
|
m_fullVersion = platform.value("full-version").toString(); |
|
|
|
m_lastChangeTime = platform.value("modify-time").toString(); |
|
|
|
m_modifyCnt = platform.value("modifyCnt").toString(); |
|
|
|
m_bDownloadFullExe = platform.value("download-full").toBool(); |
|
|
|
if(m_version == "" || m_version == "0") //当前版本号为空
|
|
|
|
m_patchBaseUrl = platform.value("patch-url").toString(); |
|
|
|
m_fullBaseUrl = platform.value("full-url").toString(); |
|
|
|
|
|
|
|
if(m_version.isEmpty() || m_version == "0") |
|
|
|
{ |
|
|
|
m_bDownloadFullExe = true; |
|
|
|
} |
|
|
|
m_fileName = APPNAME + "-patch-v" + m_patchVersion + ".zip"; |
|
|
|
m_patchUrl = platform.value("patch-url").toString() + APPNAME + "/" + APPNAME + "-patch-v" + m_patchVersion + ".zip"; |
|
|
|
m_fullUrl = platform.value("full-url").toString() + APPNAME + "/" + APPNAME + "-full-v" + m_fullVersion + ".exe"; |
|
|
|
|
|
|
|
QString _remoteVersion = m_bDownloadFullExe ? m_fullVersion : m_patchVersion; |
|
|
|
bool _isNotLatest = checkVersion(m_version, _remoteVersion); |
|
|
|
rebuildDownloadInfo(); |
|
|
|
|
|
|
|
const QString remoteVersion = m_bDownloadFullExe ? m_fullVersion : m_patchVersion; |
|
|
|
const bool isNotLatest = checkVersion(m_version, remoteVersion); |
|
|
|
|
|
|
|
if(_isNotLatest) |
|
|
|
if(isNotLatest) |
|
|
|
{ |
|
|
|
showStatus("======================================" |
|
|
|
+ tr("\ncurrent version: v") + m_version |
|
|
|
+ tr("\nmodify time: ") + APPDATE |
|
|
|
+ tr("\nmodify cnt: ") + MODIFYCNT |
|
|
|
+ "\n ------------------------------" |
|
|
|
+ tr("\nlatest version: v") + _remoteVersion |
|
|
|
+ tr("\nlatest version: v") + remoteVersion |
|
|
|
+ tr("\nmodify time: ") + m_lastChangeTime |
|
|
|
+ tr("\nmodify cnt: ") + m_modifyCnt |
|
|
|
+ tr("\nchange log: ") + m_changelog |
|
|
|
+ "\n======================================" |
|
|
|
); |
|
|
|
+ "\n======================================"); |
|
|
|
|
|
|
|
if(m_bDownloadFullExe) |
|
|
|
{ |
|
|
|
showStatus("need download full exe"); |
|
|
|
m_fileName = APPNAME + "-full-v" + m_patchVersion + ".exe"; |
|
|
|
} |
|
|
|
|
|
|
|
showStatus(tr("click update button to update")); |
|
|
|
setUpdateButtonsEnabled(true); |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
showStatus("======================================" |
|
|
|
+ tr("\ncurrent software is up-to-date") |
|
|
|
+ tr("\nversion : v") + _remoteVersion |
|
|
|
+ tr("\nversion : v") + remoteVersion |
|
|
|
+ tr("\nmodify time: ") + m_lastChangeTime |
|
|
|
+ tr("\nmodify cnt: ") + m_modifyCnt |
|
|
|
+ "\n======================================" |
|
|
|
+ tr("\nno need to update") |
|
|
|
+ tr("\nclick cancel button to quit")); |
|
|
|
setUpdateButtonsEnabled(false); |
|
|
|
} |
|
|
|
ui->btn_update->setEnabled(1); |
|
|
|
ui->btn_update_patch->setEnabled(1); |
|
|
|
} |
|
|
|
|
|
|
|
void UpdaterDialog::calculateSizes(qint64 received, qint64 total) |
|
|
|
{ |
|
|
|
QString totalSize; |
|
|
|
QString receivedSize; |
|
|
|
|
|
|
|
if (total < 1024) |
|
|
|
totalSize = tr("%1 bytes").arg(total); |
|
|
|
|
|
|
|
else if (total < 1048576) |
|
|
|
totalSize = tr("%1 KB").arg(round(total / 1024)); |
|
|
|
|
|
|
|
else |
|
|
|
totalSize = tr("%1 MB").arg((total / 1048576.0)); |
|
|
|
|
|
|
|
if (received < 1024) |
|
|
|
receivedSize = tr("%1 bytes").arg(received); |
|
|
|
|
|
|
|
else if (received < 1048576) |
|
|
|
receivedSize = tr("%1 KB").arg(received / 1024); |
|
|
|
|
|
|
|
else |
|
|
|
receivedSize = tr("%1 MB").arg(received / 1048576.0); |
|
|
|
const QString totalSize = formatBytes(total); |
|
|
|
const QString receivedSize = formatBytes(received); |
|
|
|
|
|
|
|
ui->label_2->setText(tr("Downloading updates") + " (" + receivedSize + " " + tr("of") + " " + totalSize |
|
|
|
+ ")"); |
|
|
|
} |
|
|
|
|
|
|
|
/**
|
|
|
|
* Uses two time samples (from the current time and a previous sample) to |
|
|
|
* calculate how many bytes have been downloaded. |
|
|
|
* |
|
|
|
* Then, this function proceeds to calculate the appropiate units of time |
|
|
|
* (hours, minutes or seconds) and constructs a user-friendly string, which |
|
|
|
* is displayed in the dialog. |
|
|
|
*/ |
|
|
|
void UpdaterDialog::calculateTimeRemaining(qint64 received, qint64 total) |
|
|
|
{ |
|
|
|
static quint64 lastRec = 0; |
|
|
|
static uint lastTime = 0; |
|
|
|
uint _time = QDateTime::currentDateTime().toMSecsSinceEpoch(); |
|
|
|
double _speed = (received - lastRec) / 1024.0 / (_time - lastTime) * 1000.0; |
|
|
|
lastTime = _time; |
|
|
|
lastRec = received; |
|
|
|
uint difference = QDateTime::currentDateTime().toSecsSinceEpoch() - m_startTime; |
|
|
|
|
|
|
|
if (difference > 0) |
|
|
|
if(total <= 0 || received <= 0) |
|
|
|
{ |
|
|
|
QString timeString; |
|
|
|
qreal timeRemaining = (total - received) / (received / difference); |
|
|
|
|
|
|
|
if (timeRemaining > 7200) |
|
|
|
{ |
|
|
|
timeRemaining /= 3600; |
|
|
|
int hours = int(timeRemaining + 0.5); |
|
|
|
|
|
|
|
if (hours > 1) |
|
|
|
timeString = tr("about %1 hours").arg(hours); |
|
|
|
else |
|
|
|
timeString = tr("about one hour"); |
|
|
|
} |
|
|
|
|
|
|
|
else if (timeRemaining > 60) |
|
|
|
{ |
|
|
|
timeRemaining /= 60; |
|
|
|
int minutes = int(timeRemaining + 0.5); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
if (minutes > 1) |
|
|
|
timeString = tr("%1 minutes").arg(minutes); |
|
|
|
else |
|
|
|
timeString = tr("1 minute"); |
|
|
|
} |
|
|
|
const qint64 currentMs = m_downloadTimer.elapsed(); |
|
|
|
const qint64 elapsedSinceLast = currentMs - m_lastProgressMs; |
|
|
|
double speed = 0.0; |
|
|
|
if(elapsedSinceLast > 0) |
|
|
|
{ |
|
|
|
speed = (received - m_lastProgressBytes) / 1024.0 / elapsedSinceLast * 1000.0; |
|
|
|
} |
|
|
|
|
|
|
|
else if (timeRemaining <= 60) |
|
|
|
{ |
|
|
|
int seconds = int(timeRemaining + 0.5); |
|
|
|
m_lastProgressMs = currentMs; |
|
|
|
m_lastProgressBytes = received; |
|
|
|
|
|
|
|
if (seconds > 1) |
|
|
|
timeString = tr("%1 seconds").arg(seconds); |
|
|
|
else |
|
|
|
timeString = tr("1 second"); |
|
|
|
} |
|
|
|
const qint64 elapsedSeconds = qMax<qint64>(1, currentMs / 1000); |
|
|
|
qreal timeRemaining = (total - received) / (received / static_cast<qreal>(elapsedSeconds)); |
|
|
|
|
|
|
|
ui->label_3->setText(tr("speed: %1 kb/s ").arg(_speed) + tr("Time remaining") + ": " + timeString); |
|
|
|
QString timeString; |
|
|
|
if(timeRemaining > 7200) |
|
|
|
{ |
|
|
|
timeRemaining /= 3600; |
|
|
|
const int hours = int(timeRemaining + 0.5); |
|
|
|
timeString = hours > 1 ? tr("about %1 hours").arg(hours) : tr("about one hour"); |
|
|
|
} |
|
|
|
else if(timeRemaining > 60) |
|
|
|
{ |
|
|
|
timeRemaining /= 60; |
|
|
|
const int minutes = int(timeRemaining + 0.5); |
|
|
|
timeString = minutes > 1 ? tr("%1 minutes").arg(minutes) : tr("1 minute"); |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
const int seconds = int(timeRemaining + 0.5); |
|
|
|
timeString = seconds > 1 ? tr("%1 seconds").arg(seconds) : tr("1 second"); |
|
|
|
} |
|
|
|
|
|
|
|
ui->label_3->setText(tr("speed: %1 kb/s ").arg(speed, 0, 'f', 1) + tr("Time remaining") + ": " + timeString); |
|
|
|
} |
|
|
|
|
|
|
|
void UpdaterDialog::on_btn_update_clicked() |
|
|
|
{ |
|
|
|
rebuildDownloadInfo(); |
|
|
|
m_pDownloader->setFileName(m_fileName); |
|
|
|
m_startTime = QDateTime::currentDateTime().toSecsSinceEpoch(); |
|
|
|
m_downloadTimer.restart(); |
|
|
|
m_lastProgressMs = 0; |
|
|
|
m_lastProgressBytes = 0; |
|
|
|
|
|
|
|
QString _url = m_bDownloadFullExe ? m_fullUrl : m_patchUrl; |
|
|
|
m_pDownloader->startDownload(_url); |
|
|
|
qDebug() << _url; |
|
|
|
const QString url = m_bDownloadFullExe ? m_fullUrl : m_patchUrl; |
|
|
|
m_pDownloader->startDownload(url); |
|
|
|
} |
|
|
|
|
|
|
|
void UpdaterDialog::on_btn_cancel_clicked() |
|
|
|
{ |
|
|
|
if(!m_UpdateProc) |
|
|
|
if(restartApplication()) |
|
|
|
{ |
|
|
|
m_UpdateProc = new QProcess; |
|
|
|
QApplication::quit(); |
|
|
|
} |
|
|
|
#if defined Q_OS_WIN |
|
|
|
m_UpdateProc->setProgram(APPNAME + ".exe"); |
|
|
|
#elif defined Q_OS_LINUX |
|
|
|
m_UpdateProc->setProgram("./" + APPNAME); |
|
|
|
#endif |
|
|
|
m_UpdateProc->start(); |
|
|
|
m_UpdateProc->waitForStarted(); |
|
|
|
QApplication::quit(); |
|
|
|
} |
|
|
|
|
|
|
|
void UpdaterDialog::onDownloadFinished() |
|
|
|
{ |
|
|
|
// ui->show->appendPlainText(m_pDownloader->fileName());
|
|
|
|
// ui->show->appendPlainText("m_bDownloadFullExe");
|
|
|
|
auto localInformation = QMessageBox::question(this, "update", "download finished\nstart update?"); |
|
|
|
if(localInformation == QMessageBox::Yes) |
|
|
|
const qreal totalSeconds = qMax<qreal>(0.001, m_downloadTimer.elapsed() / 1000.0); |
|
|
|
if(m_totalSize > 0) |
|
|
|
{ |
|
|
|
QString _appName; |
|
|
|
if(!m_bDownloadFullExe) |
|
|
|
{ |
|
|
|
//解压文件
|
|
|
|
QFileInfo f(m_pDownloader->getFile()); |
|
|
|
QString _path = f.fileName(); |
|
|
|
QZipReader * zipReader = new QZipReader(_path); |
|
|
|
zipReader->extractAll(f.path()); |
|
|
|
_appName = APPNAME + ".exe"; |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
_appName = m_pDownloader->fileName(); |
|
|
|
} |
|
|
|
ui->label_3->setText("total time: " + QString::number(totalSeconds, 'f', 1) + |
|
|
|
"s average speed: " + QString::number((m_totalSize / 1024.0 / totalSeconds), 'g', 3) + "kb/s"); |
|
|
|
} |
|
|
|
|
|
|
|
const auto localInformation = QMessageBox::question(this, "update", "download finished\nstart update?"); |
|
|
|
if(localInformation != QMessageBox::Yes) |
|
|
|
{ |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
QFileInfo f2(QCoreApplication::applicationDirPath() + "/" + _appName); |
|
|
|
if(f2.exists()) |
|
|
|
QString appName; |
|
|
|
if(!m_bDownloadFullExe) |
|
|
|
{ |
|
|
|
// 补丁包下载完成后在应用目录解压;解压失败时保留更新器,避免退出后主程序缺文件。
|
|
|
|
QFileInfo fileInfo(m_pDownloader->getFile()); |
|
|
|
QZipReader zipReader(fileInfo.absoluteFilePath()); |
|
|
|
if(!zipReader.extractAll(fileInfo.absolutePath())) |
|
|
|
{ |
|
|
|
QDesktopServices::openUrl(f2.absoluteFilePath()); |
|
|
|
showStatus("extract patch failed"); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
appName = APPNAME + ".exe"; |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
appName = m_pDownloader->fileName(); |
|
|
|
} |
|
|
|
|
|
|
|
const QFileInfo appFile(QCoreApplication::applicationDirPath() + "/" + appName); |
|
|
|
if(appFile.exists()) |
|
|
|
{ |
|
|
|
QDesktopServices::openUrl(QUrl::fromLocalFile(appFile.absoluteFilePath())); |
|
|
|
QApplication::quit(); |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
showStatus("update target file not exists: " + appFile.absoluteFilePath()); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
void UpdaterDialog::on_btn_update_patch_clicked() |
|
|
|
{ |
|
|
|
m_pDownloader->setFileName(m_fileName); |
|
|
|
m_startTime = QDateTime::currentDateTime().toSecsSinceEpoch(); |
|
|
|
m_bDownloadFullExe = false; |
|
|
|
QString _url = m_patchUrl; |
|
|
|
m_pDownloader->startDownload(_url); |
|
|
|
rebuildDownloadInfo(); |
|
|
|
m_pDownloader->setFileName(m_fileName); |
|
|
|
m_downloadTimer.restart(); |
|
|
|
m_lastProgressMs = 0; |
|
|
|
m_lastProgressBytes = 0; |
|
|
|
m_pDownloader->startDownload(m_patchUrl); |
|
|
|
} |
|
|
|
|
|
|
|
void UpdaterDialog::setUpdateButtonsEnabled(bool enabled) |
|
|
|
{ |
|
|
|
ui->btn_update->setEnabled(enabled); |
|
|
|
ui->btn_update_patch->setEnabled(enabled && !m_patchUrl.isEmpty()); |
|
|
|
} |
|
|
|
|
|
|
|
void UpdaterDialog::rebuildDownloadInfo() |
|
|
|
{ |
|
|
|
const QString patchFileName = APPNAME + "-patch-v" + m_patchVersion + ".zip"; |
|
|
|
const QString fullFileName = APPNAME + "-full-v" + m_fullVersion + ".exe"; |
|
|
|
|
|
|
|
m_patchUrl = buildDownloadUrl(m_patchBaseUrl, APPNAME, patchFileName); |
|
|
|
m_fullUrl = buildDownloadUrl(m_fullBaseUrl, APPNAME, fullFileName); |
|
|
|
m_fileName = m_bDownloadFullExe ? fullFileName : patchFileName; |
|
|
|
} |
|
|
|
|
|
|
|
QString UpdaterDialog::formatBytes(qint64 bytes) const |
|
|
|
{ |
|
|
|
if(bytes < 0) |
|
|
|
{ |
|
|
|
return tr("unknown"); |
|
|
|
} |
|
|
|
if(bytes < 1024) |
|
|
|
{ |
|
|
|
return tr("%1 bytes").arg(bytes); |
|
|
|
} |
|
|
|
if(bytes < 1048576) |
|
|
|
{ |
|
|
|
return tr("%1 KB").arg(QString::number(bytes / 1024.0, 'f', 1)); |
|
|
|
} |
|
|
|
|
|
|
|
return tr("%1 MB").arg(QString::number(bytes / 1048576.0, 'f', 2)); |
|
|
|
} |
|
|
|
|
|
|
|
bool UpdaterDialog::restartApplication() |
|
|
|
{ |
|
|
|
if(APPNAME.trimmed().isEmpty()) |
|
|
|
{ |
|
|
|
showStatus("app name is empty"); |
|
|
|
return false; |
|
|
|
} |
|
|
|
|
|
|
|
QString program; |
|
|
|
#if defined Q_OS_WIN |
|
|
|
program = APPNAME + ".exe"; |
|
|
|
#elif defined Q_OS_LINUX |
|
|
|
program = "./" + APPNAME; |
|
|
|
#else |
|
|
|
program = APPNAME; |
|
|
|
#endif |
|
|
|
|
|
|
|
// 取消更新后需要让原程序独立运行,使用 startDetached 避免更新器退出时影响子进程生命周期。
|
|
|
|
if(!QProcess::startDetached(program, QStringList())) |
|
|
|
{ |
|
|
|
showStatus("restart app failed"); |
|
|
|
return false; |
|
|
|
} |
|
|
|
|
|
|
|
return true; |
|
|
|
} |
|
|
|
|