1
0
Fork 0

Launcher: add update checker UI

This commit is contained in:
Automatic Release Builder 2020-10-03 15:35:22 +01:00 committed by James Turner
parent 1ef6578ad4
commit 9748b57971
6 changed files with 314 additions and 1 deletions

View file

@ -130,6 +130,8 @@ if (HAVE_QT)
CarriersLocationModel.hxx
FavouriteAircraftData.cxx
FavouriteAircraftData.hxx
UpdateChecker.cxx
UpdateChecker.hxx
${uic_sources}
${qrc_sources}
${qml_sources})

View file

@ -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");

View file

@ -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
View 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
View 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;
};

View file

@ -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