Launcher: add update checker UI
This commit is contained in:
parent
1ef6578ad4
commit
9748b57971
6 changed files with 314 additions and 1 deletions
|
@ -130,6 +130,8 @@ if (HAVE_QT)
|
|||
CarriersLocationModel.hxx
|
||||
FavouriteAircraftData.cxx
|
||||
FavouriteAircraftData.hxx
|
||||
UpdateChecker.cxx
|
||||
UpdateChecker.hxx
|
||||
${uic_sources}
|
||||
${qrc_sources}
|
||||
${qml_sources})
|
||||
|
|
|
@ -57,6 +57,7 @@
|
|||
#include "StackController.hxx"
|
||||
#include "ThumbnailImageItem.hxx"
|
||||
#include "UnitsModel.hxx"
|
||||
#include "UpdateChecker.hxx"
|
||||
|
||||
using namespace simgear::pkg;
|
||||
|
||||
|
@ -154,6 +155,7 @@ void LauncherController::initQML()
|
|||
qmlRegisterUncreatableType<LauncherController>("FlightGear.Launcher", 1, 0, "LauncherController", "no");
|
||||
qmlRegisterUncreatableType<LocationController>("FlightGear.Launcher", 1, 0, "LocationController", "no");
|
||||
qmlRegisterUncreatableType<FlightPlanController>("FlightGear.Launcher", 1, 0, "FlightPlanController", "no");
|
||||
qmlRegisterUncreatableType<UpdateChecker>("FlightGear.Launcher", 1, 0, "UpdateChecker", "for enums");
|
||||
|
||||
qmlRegisterType<LauncherArgumentTokenizer>("FlightGear.Launcher", 1, 0, "ArgumentTokenizer");
|
||||
qmlRegisterUncreatableType<QAbstractItemModel>("FlightGear.Launcher", 1, 0, "QAIM", "no");
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
#include "LauncherController.hxx"
|
||||
#include "AddOnsController.hxx"
|
||||
#include "LocationController.hxx"
|
||||
|
||||
#include "UpdateChecker.hxx"
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
@ -99,6 +99,9 @@ LauncherMainWindow::LauncherMainWindow(bool inSimMode) : QQuickView()
|
|||
ctx->setContextProperty("_location", m_controller->location());
|
||||
ctx->setContextProperty("_osName", osName);
|
||||
|
||||
auto updater = new UpdateChecker(this);
|
||||
ctx->setContextProperty("_updates", updater);
|
||||
|
||||
if (!inSimMode) {
|
||||
auto addOnsCtl = new AddOnsController(this, m_controller->config());
|
||||
ctx->setContextProperty("_addOns", addOnsCtl);
|
||||
|
|
180
src/GUI/UpdateChecker.cxx
Normal file
180
src/GUI/UpdateChecker.cxx
Normal file
|
@ -0,0 +1,180 @@
|
|||
// Written by James Turner, started October 2020
|
||||
//
|
||||
// Copyright (C) 2020 James Turner <james@flightgear.org>
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation; either version 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "UpdateChecker.hxx"
|
||||
|
||||
#include <QSettings>
|
||||
#include <QDate>
|
||||
#include <QDebug>
|
||||
|
||||
#include <simgear/io/HTTPMemoryRequest.hxx>
|
||||
#include <simgear/props/props_io.hxx>
|
||||
#include <simgear/structure/exception.hxx>
|
||||
|
||||
#include <Network/HTTPClient.hxx>
|
||||
#include <Main/globals.hxx>
|
||||
|
||||
#include "LauncherNotificationsController.hxx"
|
||||
|
||||
namespace {
|
||||
|
||||
class UpdateXMLRequest : public simgear::HTTP::MemoryRequest
|
||||
{
|
||||
public:
|
||||
UpdateXMLRequest(UpdateChecker* u, const std::string& uri) :
|
||||
simgear::HTTP::MemoryRequest(uri),
|
||||
_owner(u)
|
||||
{
|
||||
// in case it's useful, send our version as an additional header
|
||||
// this would allow a server side script to return different content
|
||||
// based on our version in the future
|
||||
requestHeader("FlightGear-Version") = FLIGHTGEAR_VERSION;
|
||||
}
|
||||
private:
|
||||
void onFail() override
|
||||
{
|
||||
didFail();
|
||||
}
|
||||
|
||||
void onDone() override
|
||||
{
|
||||
|
||||
|
||||
int response = responseCode();
|
||||
if (response == 200) {
|
||||
// send response to the main thread for processing
|
||||
QMetaObject::invokeMethod(_owner, "receivedUpdateXML", Qt::QueuedConnection,
|
||||
Q_ARG(QByteArray, QByteArray::fromStdString(responseBody())));
|
||||
|
||||
} else {
|
||||
didFail();
|
||||
}
|
||||
}
|
||||
|
||||
void didFail()
|
||||
{
|
||||
// reset check time to tomorrow
|
||||
QSettings settings;
|
||||
const QDate n = QDate::currentDate().addDays(1);
|
||||
settings.setValue("next-update-check", n);
|
||||
}
|
||||
private:
|
||||
UpdateChecker* _owner;
|
||||
};
|
||||
|
||||
} // of namespace
|
||||
|
||||
UpdateChecker::UpdateChecker(QObject *parent) : QObject(parent)
|
||||
{
|
||||
QSettings settings;
|
||||
QDate nextCheck = settings.value("next-update-check").toDate();
|
||||
if (!nextCheck.isValid()) {
|
||||
// check tomorrow, so we don't nag immediately after insstallaion
|
||||
const QDate n = QDate::currentDate().addDays(1);
|
||||
settings.setValue("next-update-check", n);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (nextCheck <= QDate::currentDate()) {
|
||||
// start a check
|
||||
auto http = globals->get_subsystem<FGHTTPClient>();
|
||||
if (!http) {
|
||||
return;
|
||||
}
|
||||
|
||||
const string_list versionParts = simgear::strutils::split(FLIGHTGEAR_VERSION, ".");
|
||||
_majorMinorVersion = versionParts[0] + "." + versionParts[1];
|
||||
|
||||
// definitiely want to ensure HTTPS for this.
|
||||
std::string uri = "https://download.flightgear.org/builds/" + _majorMinorVersion + "/updates.xml";
|
||||
auto req = new UpdateXMLRequest(this, uri);
|
||||
http->makeRequest(req);
|
||||
} else {
|
||||
// nothing to do
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateChecker::ignoreUpdate()
|
||||
{
|
||||
QSettings settings;
|
||||
if (m_status == PointUpdate) {
|
||||
settings.setValue("ignored-point-release", _currentUpdateVersion);
|
||||
} else if (m_status == MajorUpdate) {
|
||||
settings.setValue("ignored-major-release", _currentUpdateVersion);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
m_status = NoUpdate;
|
||||
m_updateUri.clear();
|
||||
_currentUpdateVersion.clear();
|
||||
emit statusChanged(m_status);
|
||||
}
|
||||
|
||||
void UpdateChecker::receivedUpdateXML(QByteArray body)
|
||||
{
|
||||
SGPropertyNode_ptr props(new SGPropertyNode);
|
||||
const auto s = body.toStdString();
|
||||
|
||||
QSettings settings;
|
||||
|
||||
try {
|
||||
const char* buffer = s.c_str();
|
||||
readProperties(buffer, s.size(), props, true);
|
||||
|
||||
const QDate n = QDate::currentDate().addDays(7);
|
||||
settings.setValue("next-update-check", n);
|
||||
|
||||
const std::string newMajorVersion = props->getStringValue("current-major-release");
|
||||
if (simgear::strutils::compare_versions(FLIGHTGEAR_VERSION, newMajorVersion) < 0) {
|
||||
// we have a newer version!
|
||||
_currentUpdateVersion = QString::fromStdString(newMajorVersion);
|
||||
if (settings.value("ignored-major-release") == _currentUpdateVersion) {
|
||||
// ignore it
|
||||
} else {
|
||||
m_status = MajorUpdate;
|
||||
|
||||
const std::string newVersionUri = props->getStringValue("upgrade-uri");
|
||||
m_updateUri = QUrl(QString::fromStdString(newVersionUri));
|
||||
emit statusChanged(m_status);
|
||||
|
||||
return; // don't consider minor updates
|
||||
}
|
||||
}
|
||||
|
||||
// check current version
|
||||
const std::string newPointVersion = props->getStringValue("current-point-release");
|
||||
if (simgear::strutils::compare_versions(FLIGHTGEAR_VERSION, newPointVersion) < 0) {
|
||||
// we have a newer version!
|
||||
_currentUpdateVersion = QString::fromStdString(newPointVersion);
|
||||
if (settings.value("ignored-point-release") == _currentUpdateVersion) {
|
||||
// ignore it
|
||||
} else {
|
||||
m_status = PointUpdate;
|
||||
const std::string newVersionUri = props->getStringValue("download-uri");
|
||||
m_updateUri = QUrl(QString::fromStdString(newVersionUri));
|
||||
emit statusChanged(m_status);
|
||||
}
|
||||
}
|
||||
} catch (const sg_exception &e) {
|
||||
SG_LOG(SG_IO, SG_WARN, "parsing update XML failed: " << e.getFormattedMessage());
|
||||
}
|
||||
}
|
79
src/GUI/UpdateChecker.hxx
Normal file
79
src/GUI/UpdateChecker.hxx
Normal file
|
@ -0,0 +1,79 @@
|
|||
// Written by James Turner, started October 2020
|
||||
//
|
||||
// Copyright (C) 2020 James Turner <james@flightgear.org>
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License as
|
||||
// published by the Free Software Foundation; either version 2 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <QObject>
|
||||
#include <QByteArray>
|
||||
#include <QUrl>
|
||||
|
||||
class UpdateChecker : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY(Status status READ status NOTIFY statusChanged)
|
||||
Q_PROPERTY(QUrl updateUri READ updateUri NOTIFY statusChanged)
|
||||
Q_PROPERTY(QString updateVersion READ updateVersion NOTIFY statusChanged)
|
||||
public:
|
||||
explicit UpdateChecker(QObject *parent = nullptr);
|
||||
|
||||
enum Status {
|
||||
NoUpdate,
|
||||
PointUpdate,
|
||||
MajorUpdate
|
||||
};
|
||||
|
||||
Q_ENUMS(Status)
|
||||
|
||||
|
||||
Status status() const
|
||||
{
|
||||
return m_status;
|
||||
}
|
||||
|
||||
QUrl updateUri() const
|
||||
{
|
||||
return m_updateUri;
|
||||
}
|
||||
|
||||
QString updateVersion() const
|
||||
{
|
||||
return _currentUpdateVersion;
|
||||
}
|
||||
|
||||
signals:
|
||||
|
||||
void statusChanged(Status status);
|
||||
|
||||
public slots:
|
||||
void ignoreUpdate();
|
||||
|
||||
private slots:
|
||||
void receivedUpdateXML(QByteArray body);
|
||||
|
||||
private:
|
||||
std::string _majorMinorVersion; ///< version without patch level
|
||||
|
||||
QString _currentUpdateVersion;
|
||||
|
||||
Status m_status = NoUpdate;
|
||||
QUrl m_updateUri;
|
||||
};
|
||||
|
|
@ -76,7 +76,54 @@ Item {
|
|||
styleColor: "black"
|
||||
}
|
||||
|
||||
Row {
|
||||
spacing: Style.margin
|
||||
anchors {
|
||||
left: logoText.left
|
||||
right: logoText.right
|
||||
}
|
||||
|
||||
// anchoring to logoText bottom doesn't work as expected because of
|
||||
// dynamic text sizing, so bind it manually
|
||||
y: logoText.y + Style.margin + logoText.contentHeight
|
||||
|
||||
id: updateMessage
|
||||
|
||||
visible: _updates.status != UpdateChecker.NoUpdate
|
||||
|
||||
readonly property string msg: (_updates.status == UpdateChecker.MajorUpdate) ?
|
||||
qsTr("A new release of FlightGear is available (%1): click for more information")
|
||||
: qsTr("Updated version %1 is available: click here to download")
|
||||
|
||||
ClickableText {
|
||||
text: parent.msg.arg(_updates.updateVersion)
|
||||
baseTextColor: "white"
|
||||
style: Text.Outline
|
||||
styleColor: "black"
|
||||
font.bold: true
|
||||
font.pixelSize: Style.headingFontPixelSize
|
||||
|
||||
onClicked: {
|
||||
_launcher.launchUrl(_updates.updateUri);
|
||||
}
|
||||
}
|
||||
|
||||
ClickableText {
|
||||
text: qsTr("(or click to ignore this)")
|
||||
baseTextColor: "white"
|
||||
style: Text.Outline
|
||||
styleColor: "black"
|
||||
font.bold: true
|
||||
font.pixelSize: Style.headingFontPixelSize
|
||||
|
||||
onClicked: {
|
||||
_updates.ignoreUpdate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ClickableText {
|
||||
visible: !updateMessage.visible
|
||||
anchors {
|
||||
left: logoText.left
|
||||
right: logoText.right
|
||||
|
|
Loading…
Add table
Reference in a new issue