Multiple improvements to ApplicationUpdater.

This commit is contained in:
Leon Styhre 2023-02-26 12:50:30 +01:00
parent 90c3f8ff5e
commit c9a59994a6
5 changed files with 131 additions and 72 deletions

View file

@ -4,7 +4,7 @@
// ApplicationUpdater.cpp
//
// Checks for application updates.
// In the future updates will also be downloaded and possibly installed.
// In the future updates will also be downloaded, and installed on some platforms.
//
#include "ApplicationUpdater.h"
@ -12,6 +12,7 @@
#include "EmulationStation.h"
#include "Log.h"
#include "Settings.h"
#include "resources/ResourceManager.h"
#include "utils/StringUtil.h"
#include "utils/TimeUtil.h"
@ -23,17 +24,39 @@
#include <algorithm>
#include <deque>
#define LOCAL_TESTING_FILE false
#define MAX_DOWNLOAD_TIME 1
ApplicationUpdater::ApplicationUpdater()
: mTimer {0}
: mPackageType {PackageType::UNKNOWN}
, mTimer {0}
, mMaxTime {0}
, mAbortDownload {false}
, mApplicationShutdown {false}
, mCheckedForUpdate {false}
, mNewVersion {false}
{
mUrl = "https://gitlab.com/api/v4/projects/18817634/repository/files/latest_release.json/"
"raw?ref=master";
#if defined(_WIN64)
if (Settings::getInstance()->getBool("PortableMode"))
mPackageType = PackageType::WINDOWS_PORTABLE;
else
mPackageType = PackageType::WINDOWS_INSTALLER;
#elif defined(MACOS_APPLE_CPU)
mPackageType = PackageType::MACOS_APPLE;
#elif defined(MACOS_INTEL_CPU)
mPackageType = PackageType::MACOS_INTEL;
#elif defined(STEAM_DECK)
mPackageType = PackageType::LINUX_STEAM_DECK_APPIMAGE;
#elif defined(APPIMAGE_BUILD)
mPackageType = PackageType::LINUX_APPIMAGE;
#elif defined(LINUX_RPM_PACKAGE)
mPackageType = PackageType::LINUX_RPM;
#elif defined(LINUX_DEB_PACKAGE)
mPackageType = PackageType::LINUX_DEB;
#endif
}
ApplicationUpdater::~ApplicationUpdater()
@ -44,6 +67,12 @@ ApplicationUpdater::~ApplicationUpdater()
mThread->join();
}
ApplicationUpdater& ApplicationUpdater::getInstance()
{
static ApplicationUpdater instance;
return instance;
}
void ApplicationUpdater::checkForUpdates()
{
const std::string updateFrequency {
@ -176,10 +205,25 @@ void ApplicationUpdater::update()
void ApplicationUpdater::parseFile()
{
assert(mRequest->status() == HttpReq::REQ_SUCCESS);
const std::string fileContents {mRequest->getContent()};
rapidjson::Document doc;
#if (LOCAL_TESTING_FILE)
LOG(LogWarning) << "ApplicationUpdater: Using local \"latest_release.json\" testing file";
const std::string localReleaseFile {Utils::FileSystem::getHomePath() +
"/.emulationstation/latest_release.json"};
if (!Utils::FileSystem::exists(localReleaseFile))
throw std::runtime_error("Local testing file not found");
const ResourceData& localReleaseFileData {
ResourceManager::getInstance().getFileData(localReleaseFile)};
doc.Parse(reinterpret_cast<const char*>(localReleaseFileData.ptr.get()),
localReleaseFileData.length);
#else
const std::string fileContents {mRequest->getContent()};
doc.Parse(&fileContents[0], fileContents.length());
#endif
if (doc.HasParseError())
throw std::runtime_error(rapidjson::GetParseError_En(doc.GetParseError()));
@ -289,14 +333,14 @@ void ApplicationUpdater::compareVersions()
#endif
}
bool newVersion {false};
mNewVersion = false;
for (auto& releaseType : releaseTypes) {
// If the version does not follow the semantic versioning scheme then always consider it to
// be a new release as perhaps the version scheme will be changed sometime in the future.
if (count_if(releaseType->version.cbegin(), releaseType->version.cend(),
[](char c) { return c == '.'; }) != 2) {
newVersion = true;
mNewVersion = true;
}
else {
std::vector<std::string> fileVersion {
@ -328,45 +372,35 @@ void ApplicationUpdater::compareVersions()
++versionWeight;
if (versionWeight > 0)
newVersion = true;
mNewVersion = true;
}
if (newVersion) {
std::string message;
if (mNewVersion) {
for (auto& package : releaseType->packages) {
#if defined(_WIN64)
if (Settings::getInstance()->getBool("PortableMode")) {
if (package.name == "WindowsPortable")
message = package.message;
}
else {
if (package.name == "WindowsInstaller")
message = package.message;
}
#elif defined(MACOS_APPLE_CPU)
if (package.name == "macOSApple")
message = package.message;
#elif defined(MACOS_INTEL_CPU)
if (package.name == "macOSIntel")
message = package.message;
#elif defined(STEAM_DECK)
if (package.name == "LinuxSteamDeckAppImage")
message = package.message;
#elif defined(APPIMAGE_BUILD)
if (package.name == "LinuxAppImage")
message = package.message;
#elif defined(LINUX_DEB_PACKAGE)
if (package.name == "LinuxDEB")
message = package.message;
#elif defined(LINUX_RPM_PACKAGE)
if (package.name == "LinuxRPM")
message = package.message;
#endif
auto tempVar = package;
if (mPackageType == PackageType::WINDOWS_PORTABLE &&
package.name == "WindowsPortable")
mPackage = package;
else if (mPackageType == PackageType::WINDOWS_INSTALLER &&
package.name == "WindowsInstaller")
mPackage = package;
else if (mPackageType == PackageType::MACOS_APPLE && package.name == "macOSApple")
mPackage = package;
else if (mPackageType == PackageType::MACOS_INTEL && package.name == "macOSIntel")
mPackage = package;
else if (mPackageType == PackageType::LINUX_DEB && package.name == "LinuxDEB")
mPackage = package;
else if (mPackageType == PackageType::LINUX_RPM && package.name == "LinuxRPM")
mPackage = package;
else if (mPackageType == PackageType::LINUX_APPIMAGE &&
package.name == "LinuxAppImage")
mPackage = package;
else if (mPackageType == PackageType::LINUX_STEAM_DECK_APPIMAGE &&
package.name == "LinuxSteamDeckAppImage")
mPackage = package;
}
// Cut the message to 280 characters so we don't make the message box exceedingly large.
message = message.substr(0, 280);
mPackage.message = mPackage.message.substr(0, 280);
mLogInfo = "A new ";
mLogInfo.append(releaseType == &mStableRelease ? "stable release" : "prerelease")
@ -387,20 +421,20 @@ void ApplicationUpdater::compareVersions()
.append("can now be downloaded from\n")
.append("https://es-de.org/");
if (message != "")
mResults.append("\n").append(message);
if (mPackage.message != "")
mResults.append("\n").append(mPackage.message);
mResults = Utils::String::toUpper(mResults);
break;
}
}
if (!newVersion) {
if (!mNewVersion) {
mLogInfo = "No application updates available";
}
mCheckedForUpdate = true;
}
void ApplicationUpdater::getResults(std::string& results)
bool ApplicationUpdater::getResults()
{
mAbortDownload = true;
@ -408,8 +442,6 @@ void ApplicationUpdater::getResults(std::string& results)
mThread->join();
mThread.reset();
if (mCheckedForUpdate) {
if (mResults != "")
results = mResults;
Settings::getInstance()->setString(
"ApplicationUpdaterLastCheck",
Utils::Time::DateTime(Utils::Time::now()).getIsoString());
@ -428,4 +460,11 @@ void ApplicationUpdater::getResults(std::string& results)
if (mLogInfo != "") {
LOG(LogInfo) << mLogInfo;
}
if (mNewVersion && mPackage.name == "") {
LOG(LogWarning)
<< "ApplicationUpdater: Couldn't find a package type matching current platform";
}
return mNewVersion;
}

View file

@ -4,7 +4,7 @@
// ApplicationUpdater.h
//
// Checks for application updates.
// In the future updates will also be downloaded and possibly installed.
// In the future updates will also be downloaded, and installed on some platforms.
//
#ifndef ES_APP_APPLICATION_UPDATER_H
@ -21,8 +21,8 @@
class ApplicationUpdater : public AsyncHandle
{
public:
ApplicationUpdater();
~ApplicationUpdater();
virtual ~ApplicationUpdater();
static ApplicationUpdater& getInstance();
void checkForUpdates();
void updaterThread();
@ -30,9 +30,8 @@ public:
void update();
void parseFile();
void compareVersions();
void getResults(std::string& results);
bool getResults();
private:
struct Package {
std::string name;
std::string filename;
@ -41,6 +40,12 @@ private:
std::string message;
};
const std::string& getResultsString() { return mResults; }
const Package& getPackageInfo() { return mPackage; }
private:
ApplicationUpdater();
struct Release {
std::string releaseType;
std::string version;
@ -49,6 +54,20 @@ private:
std::vector<Package> packages;
};
enum class PackageType {
WINDOWS_PORTABLE,
WINDOWS_INSTALLER,
MACOS_APPLE,
MACOS_INTEL,
LINUX_DEB,
LINUX_RPM,
LINUX_APPIMAGE,
LINUX_STEAM_DECK_APPIMAGE,
UNKNOWN
};
PackageType mPackageType;
Package mPackage;
std::string mUrl;
std::string mResults;
std::string mLogInfo;
@ -59,6 +78,7 @@ private:
std::atomic<bool> mAbortDownload;
std::atomic<bool> mApplicationShutdown;
bool mCheckedForUpdate;
bool mNewVersion;
std::unique_ptr<std::thread> mThread;
std::unique_ptr<HttpReq> mRequest;

View file

@ -720,11 +720,8 @@ int main(int argc, char* argv[])
}
#if defined(APPLICATION_UPDATER)
std::unique_ptr<ApplicationUpdater> applicationUpdater;
if (!noUpdateCheck) {
applicationUpdater = std::make_unique<ApplicationUpdater>();
applicationUpdater->checkForUpdates();
}
if (!noUpdateCheck)
ApplicationUpdater::getInstance().checkForUpdates();
#endif
window->pushGui(ViewController::getInstance());
@ -773,14 +770,8 @@ int main(int argc, char* argv[])
}
if (!SystemData::sStartupExitSignal) {
std::string updaterResults;
if (loadSystemsStatus == loadSystemsReturnCode::LOADING_OK) {
if (loadSystemsStatus == loadSystemsReturnCode::LOADING_OK)
ThemeData::themeLoadedLogOutput();
#if defined(APPLICATION_UPDATER)
if (!noUpdateCheck)
applicationUpdater->getResults(updaterResults);
#endif
}
if (!loadSystemsStatus)
ViewController::getInstance()->goToStart(true);
@ -800,11 +791,6 @@ int main(int argc, char* argv[])
lastTime = SDL_GetTicks();
#if defined(APPLICATION_UPDATER)
if (!noUpdateCheck)
applicationUpdater.reset();
#endif
LOG(LogInfo) << "Application startup time: "
<< std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::system_clock::now() - applicationStartTime)
@ -812,8 +798,8 @@ int main(int argc, char* argv[])
<< " ms";
#if defined(APPLICATION_UPDATER)
if (updaterResults != "")
ViewController::getInstance()->updateAvailableDialog(updaterResults);
if (ApplicationUpdater::getInstance().getResults())
ViewController::getInstance()->updateAvailableDialog();
#endif
// Open the input configuration GUI if the force flag was passed from the command line.

View file

@ -12,6 +12,7 @@
#include "views/ViewController.h"
#include "ApplicationUpdater.h"
#include "CollectionSystemsManager.h"
#include "FileFilterIndex.h"
#include "InputManager.h"
@ -211,10 +212,23 @@ void ViewController::invalidAlternativeEmulatorDialog()
"OK", nullptr, "", nullptr, "", nullptr, true, true));
}
void ViewController::updateAvailableDialog(const std::string& message)
void ViewController::updateAvailableDialog()
{
cancelViewTransitions();
mWindow->pushGui(new GuiMsgBox(getHelpStyle(), message, "OK", nullptr, "", nullptr, "", nullptr,
std::string results {ApplicationUpdater::getInstance().getResultsString()};
ApplicationUpdater::Package package {ApplicationUpdater::getInstance().getPackageInfo()};
if (package.name != "") {
LOG(LogDebug) << "ViewController::updateAvailableDialog(): Package filename \""
<< package.filename << "\"";
LOG(LogDebug) << "ViewController::updateAvailableDialog(): Package url \"" << package.url
<< "\"";
LOG(LogDebug) << "ViewController::updateAvailableDialog(): Package md5 \"" << package.md5
<< "\"";
}
mWindow->pushGui(new GuiMsgBox(getHelpStyle(), results, "OK", nullptr, "", nullptr, "", nullptr,
true, true,
(mRenderer->getIsVerticalOrientation() ?
0.70f :

View file

@ -36,7 +36,7 @@ public:
void invalidSystemsFileDialog();
void noGamesDialog();
void invalidAlternativeEmulatorDialog();
void updateAvailableDialog(const std::string& message);
void updateAvailableDialog();
// Try to completely populate the GamelistView map.
// Caches things so there's no pauses during transitions.