Compare commits
7 Commits
Author | SHA1 | Date | |
---|---|---|---|
ccbd223692 | |||
c613a1c958 | |||
a8b215b560 | |||
a0f043d790 | |||
113956ccc8 | |||
f25a9bc103 | |||
af76cf59b6 |
23
BUILD.md
23
BUILD.md
@ -26,11 +26,24 @@ Getting the project to build and run on Linux is easy if you use any modern and
|
||||
|
||||
## Build dependencies
|
||||
* A C++ compiler capable of building C++11 code.
|
||||
* Qt 5.6+ Development tools (http://qt-project.org/downloads) ("Qt Online Installer for Linux (64 bit)") or the equivalent from your package manager. It is always better to use the Qt from your distribution, as long as it has a new enough version.
|
||||
* cmake 3.1 or newer
|
||||
* zlib (for example, `zlib1g-dev`)
|
||||
* Java JDK 8 (for example, `openjdk-8-jdk`)
|
||||
* GL headers (for example, `libgl1-mesa-dev`)
|
||||
* Qt Development tools 5.6 or newer (`qtbase5-dev qtchooser qt5-qmake qtbase5-dev-tools libqt5core5a libqt5network5 libqt5gui5` on Debian-based system)
|
||||
* cmake 3.1 or newer (`cmake` on Debian-based system)
|
||||
* zlib (`zlib1g-dev` on Debian-based system)
|
||||
* Java JDK (`openjdk-17-jdk`on Debian-based system)
|
||||
* GL headers (`libgl1-mesa-dev` on Debian-based system)
|
||||
|
||||
### Building a .deb
|
||||
|
||||
You need to install the build dependencies first
|
||||
|
||||
```
|
||||
git clone https://github.com/PolyMC/PolyMC.git
|
||||
git submodule init && git submodule update
|
||||
cd packages/debian
|
||||
./makedeb.sh
|
||||
```
|
||||
|
||||
If everything works correctly, the .deb will be next to the build script, in `PolyMC/packages/debian`
|
||||
|
||||
### Building from command line
|
||||
You need a source folder, a build folder and an install folder.
|
||||
|
@ -78,9 +78,6 @@ set(Launcher_PASTE_EE_API_KEY "utLvciUouSURFzfjPxLBf5W4ISsUX4pwBDF7N1AfZ" CACHE
|
||||
# Imgur API Client ID
|
||||
set(Launcher_IMGUR_CLIENT_ID "5b97b0713fba4a3" CACHE STRING "Client ID you can get from Imgur when you register an application")
|
||||
|
||||
# Google analytics ID
|
||||
set(Launcher_ANALYTICS_ID "" CACHE STRING "ID you can get from Google analytics")
|
||||
|
||||
# MSA Client ID
|
||||
set(Launcher_MSA_CLIENT_ID "17b47edd-c884-4997-926d-9e7f9a6b4647" CACHE STRING "Client ID you can get from Microsoft Identity Platform when you register an application")
|
||||
|
||||
@ -203,20 +200,23 @@ elseif(Launcher_LAYOUT_REAL STREQUAL "lin-nodeps")
|
||||
install(PROGRAMS "${CMAKE_CURRENT_BINARY_DIR}/LauncherScript" DESTINATION ${BUNDLE_DEST_DIR} RENAME ${Launcher_Name})
|
||||
|
||||
elseif(Launcher_LAYOUT_REAL STREQUAL "lin-system")
|
||||
set(Launcher_APP_BINARY_NAME "polymc" CACHE STRING "Name of the Launcher binary")
|
||||
set(Launcher_BINARY_DEST_DIR "bin" CACHE STRING "Path to the binary directory")
|
||||
set(Launcher_LIBRARY_DEST_DIR "lib${LIB_SUFFIX}" CACHE STRING "Path to the library directory")
|
||||
set(Launcher_SHARE_DEST_DIR "share/polymc" CACHE STRING "Path to the shard data directory")
|
||||
set(JARS_DEST_DIR "${Launcher_SHARE_DEST_DIR}/jars")
|
||||
set(Launcher_APP_BINARY_NAME "polymc" CACHE STRING "Name of the Launcher binary")
|
||||
set(Launcher_BINARY_DEST_DIR "bin" CACHE STRING "Path to the binary directory")
|
||||
set(Launcher_LIBRARY_DEST_DIR "lib${LIB_SUFFIX}" CACHE STRING "Path to the library directory")
|
||||
set(Launcher_SHARE_DEST_DIR "share/polymc" CACHE STRING "Path to the shared data directory")
|
||||
set(JARS_DEST_DIR "${Launcher_SHARE_DEST_DIR}/jars")
|
||||
set(Launcher_DESKTOP_DEST_DIR "share/applications" CACHE STRING "Path to the desktop file directory")
|
||||
|
||||
set(BINARY_DEST_DIR ${Launcher_BINARY_DEST_DIR})
|
||||
set(LIBRARY_DEST_DIR ${Launcher_LIBRARY_DEST_DIR})
|
||||
set(BINARY_DEST_DIR ${Launcher_BINARY_DEST_DIR})
|
||||
set(LIBRARY_DEST_DIR ${Launcher_LIBRARY_DEST_DIR})
|
||||
|
||||
MESSAGE(STATUS "Compiling for linux system with ${Launcher_SHARE_DEST_DIR} and LAUNCHER_LINUX_DATADIR")
|
||||
SET(Launcher_APP_BINARY_DEFS "-DMULTIMC_JARS_LOCATION=${CMAKE_INSTALL_PREFIX}/${JARS_DEST_DIR}" "-DLAUNCHER_LINUX_DATADIR")
|
||||
MESSAGE(STATUS "Compiling for linux system with ${Launcher_SHARE_DEST_DIR} and LAUNCHER_LINUX_DATADIR")
|
||||
SET(Launcher_APP_BINARY_DEFS "-DMULTIMC_JARS_LOCATION=${CMAKE_INSTALL_PREFIX}/${JARS_DEST_DIR}" "-DLAUNCHER_LINUX_DATADIR")
|
||||
|
||||
# install as bundle with no dependencies included
|
||||
set(INSTALL_BUNDLE "nodeps")
|
||||
install(FILES ${Launcher_Desktop} DESTINATION ${Launcher_DESKTOP_DEST_DIR})
|
||||
|
||||
# install as bundle with no dependencies included
|
||||
set(INSTALL_BUNDLE "nodeps")
|
||||
|
||||
elseif(Launcher_LAYOUT_REAL STREQUAL "win-bundle")
|
||||
set(BINARY_DEST_DIR ".")
|
||||
@ -250,7 +250,6 @@ set(NBT_NAME Launcher_nbt++)
|
||||
set(NBT_DEST_DIR ${LIBRARY_DEST_DIR})
|
||||
add_subdirectory(libraries/libnbtplusplus)
|
||||
|
||||
add_subdirectory(libraries/ganalytics) # google analytics library
|
||||
add_subdirectory(libraries/systeminfo) # system information library
|
||||
add_subdirectory(libraries/hoedown) # markdown parser
|
||||
add_subdirectory(libraries/launcher) # java based launcher part for Minecraft
|
||||
@ -260,7 +259,7 @@ add_subdirectory(libraries/quazip) # zip manipulation library
|
||||
add_subdirectory(libraries/rainbow) # Qt extension for colors
|
||||
add_subdirectory(libraries/iconfix) # fork of Qt's QIcon loader
|
||||
add_subdirectory(libraries/LocalPeer) # fork of a library from Qt solutions
|
||||
add_subdirectory(libraries/classparser) # google analytics library
|
||||
add_subdirectory(libraries/classparser) # class parser library
|
||||
add_subdirectory(libraries/optional-bare)
|
||||
add_subdirectory(libraries/tomlc99) # toml parser
|
||||
add_subdirectory(libraries/katabasis) # An OAuth2 library that tried to do too much
|
||||
|
27
README.md
27
README.md
@ -9,28 +9,17 @@ PolyMC is a custom launcher for Minecraft that focuses on predictability, long t
|
||||
|
||||
This is a **fork** of the MultiMC Launcher and not endorsed by MultiMC. The PolyMC community felt that the maintainer was not acting in the spirit of Free Software so this fork was made. Read "[Why was this fork made?](https://github.com/PolyMC/PolyMC/wiki/FAQ)" on the wiki for more details.
|
||||
|
||||
## todo
|
||||
- [ ] Get a permanent name + icon.
|
||||
- [ ] Style the logo for different icon styles.
|
||||
- [ ] Packaging for Linux--Any help packaging for your favorite distro is appreciated!
|
||||
- [ ] Packaging for MacOS/Windows
|
||||
- [ ] Stop relying on MultiMC-Hosted metadata services
|
||||
- [ ] Remove references to MultiMC
|
||||
- [ ] Change up packaging, remove the install script junk
|
||||
- [ ] AppImage, Flatpak, .deb, ebuild, and AUR packages
|
||||
- [ ] Meson
|
||||
- [x] Long-term solution for the MSA client ID issue
|
||||
- [x] Figure out a way to switch to GPL.
|
||||
|
||||
## Packages
|
||||
Experimental packages are available for Linux (non-portable) and Windows (portable). Both versions are confirmed to work but the Windows version needs more testing--please volunteer if you use Windows!
|
||||
Several source build packages are available, along with experimental pre-built generic packages.
|
||||
|
||||
- An [AUR package](https://aur.archlinux.org/packages/polymc-git/) is also available.
|
||||
- A Gentoo ebuild is available in the [swirl](https://github.com/binex-dsk/ebuilds) overlay, available as `games-action/polymc`. Check the README for instructions on how to add the overlay.
|
||||
- A full list of generic, pre-built packages is available [here](https://jenkins.polymc.org/job/PolyMC/lastSuccessfulBuild/artifact/).
|
||||
- An [AUR package](https://aur.archlinux.org/packages/polymc-git/) is available.
|
||||
- A Gentoo ebuild is available in the [swirl](https://git.swurl.xyz/swirl/ebuilds) overlay, named `games-action/polymc`. Check the README for instructions on how to add the overlay.
|
||||
- A Flatpak is available. Instructions on building it can be found in [packages/flatpak/BUILDING.md](packages/flatpak/BUILDING.md).
|
||||
- Generic, prebuilt packages (archived by version) can be found [here](https://packages.polymc.org/) ([latest](https://packages.polymc.org/latest))
|
||||
- Last build status: https://jenkins.polymc.org/job/PolyMC/lastBuild/
|
||||
- [Linux (AMD64) System](https://jenkins.polymc.org/job/PolyMC/lastSuccessfulBuild/artifact/lin64-system.tar.zst) - this is a generic system package intended to be used as a base for making distro-specific packages.
|
||||
- [Windows (32-bit)](https://jenkins.polymc.org/job/PolyMC/lastSuccessfulBuild/artifact/win32.zip) - this is a portable package, you can extract it anywhere and run it. This package needs testing.
|
||||
- [Linux (AMD64) System](https://packages.polymc.org/latest/lin64-system/lin64-system.tar.zst) ([SHA256](https://packages.polymc.org/latest/lin64-system/lin64-system.tar.zst.sha256)) - this is a generic system package intended to be used as a base for making distro-specific packages.
|
||||
- [Windows (32-bit)](https://packages.polymc.org/latest/win32/win32.zip) ([SHA256](https://packages.polymc.org/latest/win32/win32.zip.sha256)) - this is a portable package, you can extract it anywhere and run it. This package needs testing.
|
||||
- [Debian (AMD64)](https://packages.polymc.org/latest/deb/polymc-amd64.deb) ([SHA256](https://packages.polymc.org/latest/deb/polymc-amd64.deb.sha256)) - this is intended to be installed with `dpkg -i`. Alternatively, you may build the `.deb` yourself, by going to `packages/debian` and running `./makedeb.sh`.
|
||||
- MacOS currently does not have any packages. We are still working on setting up MacOS packaging.
|
||||
|
||||
## Development
|
||||
|
@ -24,7 +24,6 @@ Config::Config()
|
||||
|
||||
BUILD_PLATFORM = "@Launcher_BUILD_PLATFORM@";
|
||||
UPDATER_BASE = "@Launcher_UPDATER_BASE@";
|
||||
ANALYTICS_ID = "@Launcher_ANALYTICS_ID@";
|
||||
NOTIFICATION_URL = "@Launcher_NOTIFICATION_URL@";
|
||||
FULL_VERSION_STR = "@Launcher_VERSION_MAJOR@.@Launcher_VERSION_MINOR@.@Launcher_VERSION_BUILD@";
|
||||
|
||||
|
@ -46,9 +46,6 @@ public:
|
||||
QString USER_AGENT_UNCACHED;
|
||||
|
||||
|
||||
/// Google analytics ID
|
||||
QString ANALYTICS_ID;
|
||||
|
||||
/// URL for notifications
|
||||
QString NOTIFICATION_URL;
|
||||
|
||||
@ -79,7 +76,7 @@ public:
|
||||
* Client ID you can get from Imgur when you register an application
|
||||
*/
|
||||
QString IMGUR_CLIENT_ID;
|
||||
|
||||
|
||||
/**
|
||||
* Client ID you can get from Microsoft Identity Platform when you register an application
|
||||
*/
|
||||
@ -115,3 +112,4 @@ public:
|
||||
};
|
||||
|
||||
extern const Config BuildConfig;
|
||||
|
||||
|
@ -26,7 +26,6 @@
|
||||
#include "ui/setupwizard/SetupWizard.h"
|
||||
#include "ui/setupwizard/LanguageWizardPage.h"
|
||||
#include "ui/setupwizard/JavaWizardPage.h"
|
||||
#include "ui/setupwizard/AnalyticsWizardPage.h"
|
||||
|
||||
#include "ui/dialogs/CustomMessageBox.h"
|
||||
|
||||
@ -73,7 +72,6 @@
|
||||
#include <DesktopServices.h>
|
||||
#include <LocalPeer.h>
|
||||
|
||||
#include <ganalytics.h>
|
||||
#include <sys.h>
|
||||
|
||||
|
||||
@ -719,14 +717,6 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
|
||||
// paste.ee API key
|
||||
m_settings->registerSetting("PasteEEAPIKey", "multimc");
|
||||
|
||||
if(!BuildConfig.ANALYTICS_ID.isEmpty())
|
||||
{
|
||||
// Analytics
|
||||
m_settings->registerSetting("Analytics", true);
|
||||
m_settings->registerSetting("AnalyticsSeen", 0);
|
||||
m_settings->registerSetting("AnalyticsClientID", QString());
|
||||
}
|
||||
|
||||
// Init page provider
|
||||
{
|
||||
m_globalSettingsProvider = std::make_shared<GenericPageProvider>(tr("Settings"));
|
||||
@ -908,46 +898,6 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
|
||||
qDebug() << "<> Application theme set.";
|
||||
}
|
||||
|
||||
// Initialize analytics
|
||||
[this]()
|
||||
{
|
||||
const int analyticsVersion = 2;
|
||||
if(BuildConfig.ANALYTICS_ID.isEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
auto analyticsSetting = m_settings->getSetting("Analytics");
|
||||
connect(analyticsSetting.get(), &Setting::SettingChanged, this, &Application::analyticsSettingChanged);
|
||||
QString clientID = m_settings->get("AnalyticsClientID").toString();
|
||||
if(clientID.isEmpty())
|
||||
{
|
||||
clientID = QUuid::createUuid().toString();
|
||||
clientID.remove(QLatin1Char('{'));
|
||||
clientID.remove(QLatin1Char('}'));
|
||||
m_settings->set("AnalyticsClientID", clientID);
|
||||
}
|
||||
m_analytics = new GAnalytics(BuildConfig.ANALYTICS_ID, clientID, analyticsVersion, this);
|
||||
m_analytics->setLogLevel(GAnalytics::Debug);
|
||||
m_analytics->setAnonymizeIPs(true);
|
||||
// FIXME: the ganalytics library has no idea about our fancy shared pointers...
|
||||
m_analytics->setNetworkAccessManager(network().get());
|
||||
|
||||
if(m_settings->get("AnalyticsSeen").toInt() < m_analytics->version())
|
||||
{
|
||||
qDebug() << "Analytics info not seen by user yet (or old version).";
|
||||
return;
|
||||
}
|
||||
if(!m_settings->get("Analytics").toBool())
|
||||
{
|
||||
qDebug() << "Analytics disabled by user.";
|
||||
return;
|
||||
}
|
||||
|
||||
m_analytics->enable();
|
||||
qDebug() << "<> Initialized analytics with tid" << BuildConfig.ANALYTICS_ID;
|
||||
}();
|
||||
|
||||
if(createSetupWizard())
|
||||
{
|
||||
return;
|
||||
@ -974,29 +924,13 @@ bool Application::createSetupWizard()
|
||||
}
|
||||
return false;
|
||||
}();
|
||||
bool analyticsRequired = [&]()
|
||||
{
|
||||
if(BuildConfig.ANALYTICS_ID.isEmpty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!settings()->get("Analytics").toBool())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (settings()->get("AnalyticsSeen").toInt() < analytics()->version())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}();
|
||||
bool languageRequired = [&]()
|
||||
{
|
||||
if (settings()->get("Language").toString().isEmpty())
|
||||
return true;
|
||||
return false;
|
||||
}();
|
||||
bool wizardRequired = javaRequired || analyticsRequired || languageRequired;
|
||||
bool wizardRequired = javaRequired || languageRequired;
|
||||
|
||||
if(wizardRequired)
|
||||
{
|
||||
@ -1009,10 +943,6 @@ bool Application::createSetupWizard()
|
||||
{
|
||||
m_setupWizard->addPage(new JavaWizardPage(m_setupWizard));
|
||||
}
|
||||
if(analyticsRequired)
|
||||
{
|
||||
m_setupWizard->addPage(new AnalyticsWizardPage(m_setupWizard));
|
||||
}
|
||||
connect(m_setupWizard, &QDialog::finished, this, &Application::setupWizardFinished);
|
||||
m_setupWizard->show();
|
||||
return true;
|
||||
@ -1169,22 +1099,6 @@ void Application::messageReceived(const QByteArray& message)
|
||||
}
|
||||
}
|
||||
|
||||
void Application::analyticsSettingChanged(const Setting&, QVariant value)
|
||||
{
|
||||
if(!m_analytics)
|
||||
return;
|
||||
bool enabled = value.toBool();
|
||||
if(enabled)
|
||||
{
|
||||
qDebug() << "Analytics enabled by user.";
|
||||
}
|
||||
else
|
||||
{
|
||||
qDebug() << "Analytics disabled by user.";
|
||||
}
|
||||
m_analytics->enable(enabled);
|
||||
}
|
||||
|
||||
std::shared_ptr<TranslationsModel> Application::translations()
|
||||
{
|
||||
return m_translations;
|
||||
@ -1454,60 +1368,6 @@ MainWindow* Application::showMainWindow(bool minimized)
|
||||
connect(m_mainWindow, &MainWindow::isClosing, this, &Application::on_windowClose);
|
||||
m_openWindows++;
|
||||
}
|
||||
// FIXME: move this somewhere else...
|
||||
if(m_analytics)
|
||||
{
|
||||
auto windowSize = m_mainWindow->size();
|
||||
auto sizeString = QString("%1x%2").arg(windowSize.width()).arg(windowSize.height());
|
||||
qDebug() << "Viewport size" << sizeString;
|
||||
m_analytics->setViewportSize(sizeString);
|
||||
/*
|
||||
* cm1 = java min heap [MB]
|
||||
* cm2 = java max heap [MB]
|
||||
* cm3 = system RAM [MB]
|
||||
*
|
||||
* cd1 = java version
|
||||
* cd2 = java architecture
|
||||
* cd3 = system architecture
|
||||
* cd4 = CPU architecture
|
||||
*/
|
||||
QVariantMap customValues;
|
||||
int min = m_settings->get("MinMemAlloc").toInt();
|
||||
int max = m_settings->get("MaxMemAlloc").toInt();
|
||||
if(min < max)
|
||||
{
|
||||
customValues["cm1"] = min;
|
||||
customValues["cm2"] = max;
|
||||
}
|
||||
else
|
||||
{
|
||||
customValues["cm1"] = max;
|
||||
customValues["cm2"] = min;
|
||||
}
|
||||
|
||||
constexpr uint64_t Mega = 1024ull * 1024ull;
|
||||
int ramSize = int(Sys::getSystemRam() / Mega);
|
||||
qDebug() << "RAM size is" << ramSize << "MB";
|
||||
customValues["cm3"] = ramSize;
|
||||
|
||||
customValues["cd1"] = m_settings->get("JavaVersion");
|
||||
customValues["cd2"] = m_settings->get("JavaArchitecture");
|
||||
customValues["cd3"] = Sys::isSystem64bit() ? "64":"32";
|
||||
customValues["cd4"] = Sys::isCPU64bit() ? "64":"32";
|
||||
auto kernelInfo = Sys::getKernelInfo();
|
||||
customValues["cd5"] = kernelInfo.kernelName;
|
||||
customValues["cd6"] = kernelInfo.kernelVersion;
|
||||
auto distInfo = Sys::getDistributionInfo();
|
||||
if(!distInfo.distributionName.isEmpty())
|
||||
{
|
||||
customValues["cd7"] = distInfo.distributionName;
|
||||
}
|
||||
if(!distInfo.distributionVersion.isEmpty())
|
||||
{
|
||||
customValues["cd8"] = distInfo.distributionVersion;
|
||||
}
|
||||
m_analytics->sendScreenView("Main Window", customValues);
|
||||
}
|
||||
return m_mainWindow;
|
||||
}
|
||||
|
||||
|
@ -33,7 +33,6 @@ class BaseDetachedToolFactory;
|
||||
class TranslationsModel;
|
||||
class ITheme;
|
||||
class MCEditTool;
|
||||
class GAnalytics;
|
||||
|
||||
namespace Meta {
|
||||
class Index;
|
||||
@ -60,10 +59,6 @@ public:
|
||||
Application(int &argc, char **argv);
|
||||
virtual ~Application();
|
||||
|
||||
GAnalytics *analytics() const {
|
||||
return m_analytics;
|
||||
}
|
||||
|
||||
std::shared_ptr<SettingsObject> settings() const {
|
||||
return m_settings;
|
||||
}
|
||||
@ -161,7 +156,6 @@ private slots:
|
||||
void messageReceived(const QByteArray & message);
|
||||
void controllerSucceeded();
|
||||
void controllerFailed(const QString & error);
|
||||
void analyticsSettingChanged(const Setting &setting, QVariant value);
|
||||
void setupWizardFinished(int status);
|
||||
|
||||
private:
|
||||
@ -226,7 +220,6 @@ private:
|
||||
// peer launcher instance connector - used to implement single instance launcher and signalling
|
||||
LocalPeer * m_peerInstance = nullptr;
|
||||
|
||||
GAnalytics * m_analytics = nullptr;
|
||||
SetupWizard * m_setupWizard = nullptr;
|
||||
public:
|
||||
QString m_instanceIdToLaunch;
|
||||
@ -236,3 +229,4 @@ public:
|
||||
QUrl m_zipToImport;
|
||||
std::unique_ptr<QFile> logFile;
|
||||
};
|
||||
|
||||
|
@ -627,8 +627,6 @@ SET(LAUNCHER_SOURCES
|
||||
# GUI - setup wizard
|
||||
ui/setupwizard/SetupWizard.h
|
||||
ui/setupwizard/SetupWizard.cpp
|
||||
ui/setupwizard/AnalyticsWizardPage.cpp
|
||||
ui/setupwizard/AnalyticsWizardPage.h
|
||||
ui/setupwizard/BaseWizardPage.h
|
||||
ui/setupwizard/JavaWizardPage.cpp
|
||||
ui/setupwizard/JavaWizardPage.h
|
||||
@ -932,7 +930,6 @@ target_link_libraries(Launcher_logic
|
||||
hoedown
|
||||
Launcher_rainbow
|
||||
LocalPeer
|
||||
ganalytics
|
||||
)
|
||||
|
||||
target_link_libraries(Launcher_logic)
|
||||
|
@ -73,11 +73,6 @@ LauncherPage::LauncherPage(QWidget *parent) : QWidget(parent), ui(new Ui::Launch
|
||||
{
|
||||
ui->updateSettingsBox->setHidden(true);
|
||||
}
|
||||
// Analytics
|
||||
if(BuildConfig.ANALYTICS_ID.isEmpty())
|
||||
{
|
||||
ui->tabWidget->removeTab(ui->tabWidget->indexOf(ui->analyticsTab));
|
||||
}
|
||||
connect(ui->fontSizeBox, SIGNAL(valueChanged(int)), SLOT(refreshFontPreview()));
|
||||
connect(ui->consoleFont, SIGNAL(currentFontChanged(QFont)), SLOT(refreshFontPreview()));
|
||||
|
||||
@ -321,12 +316,6 @@ void LauncherPage::applySettings()
|
||||
s->set("InstSortMode", "Name");
|
||||
break;
|
||||
}
|
||||
|
||||
// Analytics
|
||||
if(!BuildConfig.ANALYTICS_ID.isEmpty())
|
||||
{
|
||||
s->set("Analytics", ui->analyticsCheck->isChecked());
|
||||
}
|
||||
}
|
||||
void LauncherPage::loadSettings()
|
||||
{
|
||||
@ -422,12 +411,6 @@ void LauncherPage::loadSettings()
|
||||
{
|
||||
ui->sortByNameBtn->setChecked(true);
|
||||
}
|
||||
|
||||
// Analytics
|
||||
if(!BuildConfig.ANALYTICS_ID.isEmpty())
|
||||
{
|
||||
ui->analyticsCheck->setChecked(s->get("Analytics").toBool());
|
||||
}
|
||||
}
|
||||
|
||||
void LauncherPage::refreshFontPreview()
|
||||
|
@ -485,69 +485,6 @@
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="analyticsTab">
|
||||
<attribute name="title">
|
||||
<string>Analytics</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_8">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="consoleSettingsBox_2">
|
||||
<property name="title">
|
||||
<string>Analytics Settings</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_4">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="analyticsCheck">
|
||||
<property name="text">
|
||||
<string>Send anonymous usage statistics?</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="line">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_5">
|
||||
<property name="text">
|
||||
<string><html><head/>
|
||||
<body>
|
||||
<p>The launcher sends anonymous usage statistics on every start of the application.</p><p>The following data is collected:</p>
|
||||
<ul>
|
||||
<li>Launcher version.</li>
|
||||
<li>Operating system name, version and architecture.</li>
|
||||
<li>CPU architecture (kernel architecture on linux).</li>
|
||||
<li>Size of system memory.</li>
|
||||
<li>Java version, architecture and memory settings.</li>
|
||||
</ul>
|
||||
</body></html></string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
|
@ -1,63 +0,0 @@
|
||||
#include "AnalyticsWizardPage.h"
|
||||
#include <Application.h>
|
||||
|
||||
#include <QVBoxLayout>
|
||||
#include <QTextBrowser>
|
||||
#include <QCheckBox>
|
||||
|
||||
#include <ganalytics.h>
|
||||
#include <BuildConfig.h>
|
||||
|
||||
AnalyticsWizardPage::AnalyticsWizardPage(QWidget *parent)
|
||||
: BaseWizardPage(parent)
|
||||
{
|
||||
setObjectName(QStringLiteral("analyticsPage"));
|
||||
verticalLayout_3 = new QVBoxLayout(this);
|
||||
verticalLayout_3->setObjectName(QStringLiteral("verticalLayout_3"));
|
||||
textBrowser = new QTextBrowser(this);
|
||||
textBrowser->setObjectName(QStringLiteral("textBrowser"));
|
||||
textBrowser->setAcceptRichText(false);
|
||||
textBrowser->setOpenExternalLinks(true);
|
||||
verticalLayout_3->addWidget(textBrowser);
|
||||
|
||||
checkBox = new QCheckBox(this);
|
||||
checkBox->setObjectName(QStringLiteral("checkBox"));
|
||||
checkBox->setChecked(true);
|
||||
verticalLayout_3->addWidget(checkBox);
|
||||
retranslate();
|
||||
}
|
||||
|
||||
AnalyticsWizardPage::~AnalyticsWizardPage()
|
||||
{
|
||||
}
|
||||
|
||||
bool AnalyticsWizardPage::validatePage()
|
||||
{
|
||||
auto settings = APPLICATION->settings();
|
||||
auto analytics = APPLICATION->analytics();
|
||||
auto status = checkBox->isChecked();
|
||||
settings->set("AnalyticsSeen", analytics->version());
|
||||
settings->set("Analytics", status);
|
||||
return true;
|
||||
}
|
||||
|
||||
void AnalyticsWizardPage::retranslate()
|
||||
{
|
||||
setTitle(tr("Analytics"));
|
||||
setSubTitle(tr("We track some anonymous statistics about users."));
|
||||
textBrowser->setHtml(tr(
|
||||
"<html><body>"
|
||||
"<p>The launcher sends anonymous usage statistics on every start of the application. This helps us decide what platforms and issues to focus on.</p>"
|
||||
"<p>The data is processed by Google Analytics, see their <a href=\"https://support.google.com/analytics/answer/6004245?hl=en\">article on the "
|
||||
"matter</a>.</p>"
|
||||
"<p>The following data is collected:</p>"
|
||||
"<ul><li>A random unique ID of the installation.<br />It is stored in the application settings file.</li>"
|
||||
"<li>Anonymized (partial) IP address.</li>"
|
||||
"<li>Launcher version.</li>"
|
||||
"<li>Operating system name, version and architecture.</li>"
|
||||
"<li>CPU architecture (kernel architecture on linux).</li>"
|
||||
"<li>Size of system memory.</li>"
|
||||
"<li>Java version, architecture and memory settings.</li></ul>"
|
||||
"<p>If we change the tracked information, you will see this page again.</p></body></html>"));
|
||||
checkBox->setText(tr("Enable Analytics"));
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "BaseWizardPage.h"
|
||||
|
||||
class QVBoxLayout;
|
||||
class QTextBrowser;
|
||||
class QCheckBox;
|
||||
|
||||
class AnalyticsWizardPage : public BaseWizardPage
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit AnalyticsWizardPage(QWidget *parent = Q_NULLPTR);
|
||||
virtual ~AnalyticsWizardPage();
|
||||
|
||||
bool validatePage() override;
|
||||
|
||||
protected:
|
||||
void retranslate() override;
|
||||
|
||||
private:
|
||||
QVBoxLayout *verticalLayout_3 = nullptr;
|
||||
QTextBrowser *textBrowser = nullptr;
|
||||
QCheckBox *checkBox = nullptr;
|
||||
};
|
@ -2,12 +2,10 @@
|
||||
|
||||
#include "LanguageWizardPage.h"
|
||||
#include "JavaWizardPage.h"
|
||||
#include "AnalyticsWizardPage.h"
|
||||
|
||||
#include "translations/TranslationsModel.h"
|
||||
#include <Application.h>
|
||||
#include <FileSystem.h>
|
||||
#include <ganalytics.h>
|
||||
|
||||
#include <QAbstractButton>
|
||||
#include <BuildConfig.h>
|
||||
|
@ -9,13 +9,6 @@ This library has served as a base for some (much more full-featured and advanced
|
||||
|
||||
Copyright belongs to Petr Mrázek, unless explicitly stated otherwise in the source files. Available under the Apache 2.0 license.
|
||||
|
||||
## ganalytics
|
||||
A Google Analytics library for Qt.
|
||||
|
||||
BSD licensed, derived from [qt-google-analytics](https://github.com/HSAnet/qt-google-analytics).
|
||||
|
||||
Modifications include better handling of IP anonymization (can be enabled) and general improvements of the API (application handles persistence and ID generation instead of the library).
|
||||
|
||||
## hoedown
|
||||
Hoedown is a revived fork of Sundown, the Markdown parser based on the original code of the Upskirt library by Natacha Porté.
|
||||
|
||||
|
@ -1,17 +0,0 @@
|
||||
project(ganalytics)
|
||||
|
||||
find_package(Qt5Core)
|
||||
find_package(Qt5Gui)
|
||||
find_package(Qt5Network)
|
||||
|
||||
set(ganalytics_SOURCES
|
||||
src/ganalytics.cpp
|
||||
src/ganalytics_worker.cpp
|
||||
src/ganalytics_worker.h
|
||||
include/ganalytics.h
|
||||
)
|
||||
|
||||
add_library(ganalytics STATIC ${ganalytics_SOURCES})
|
||||
target_link_libraries(ganalytics Qt5::Core Qt5::Gui Qt5::Network)
|
||||
target_include_directories(ganalytics PUBLIC include)
|
||||
target_link_libraries(ganalytics systeminfo)
|
@ -1,24 +0,0 @@
|
||||
Copyright (c) 2014-2015, University of Applied Sciences Augsburg
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the University of Applied Sciences Augsburg nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
|
||||
BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
|
||||
OODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
UT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
@ -1,34 +0,0 @@
|
||||
qt-google-analytics
|
||||
================
|
||||
|
||||
Qt5 classes for providing google analytics usage in a Qt/QML application.
|
||||
|
||||
## Building
|
||||
Include ```qt-google-analytics.pri``` in your .pro file.
|
||||
|
||||
## Using
|
||||
Please make sure you have set your application information using ```QApplication::setApplicationName``` and ```QApplication::setApplicationVersion```.
|
||||
|
||||
### In C++:
|
||||
```
|
||||
GAnalytics tracker("UA-my-id");
|
||||
tracker.sendScreenView("Main Screen");
|
||||
```
|
||||
|
||||
### In QtQuick:
|
||||
Register the class on the C++ side using ```qmlRegisterType<GAnalytics>("analytics", 0, 1, "Tracker");```
|
||||
```
|
||||
Tracker {
|
||||
id: tracker
|
||||
trackingID: "UA-my-id"
|
||||
}
|
||||
|
||||
[...]
|
||||
tracker.sendScreenView("Main Screen")
|
||||
```
|
||||
|
||||
There is also an example application in the examples folder.
|
||||
|
||||
## License
|
||||
Copyright (c) 2014-2016, University of Applied Sciences Augsburg.
|
||||
All rights reserved. Distributed under the terms and conditions of the BSD License. See separate LICENSE.txt.
|
@ -1,67 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
#include <QVariantMap>
|
||||
|
||||
class QNetworkAccessManager;
|
||||
class GAnalyticsWorker;
|
||||
|
||||
class GAnalytics : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_ENUMS(LogLevel)
|
||||
|
||||
public:
|
||||
explicit GAnalytics(const QString &trackingID, const QString &clientID, const int version, QObject *parent = 0);
|
||||
~GAnalytics();
|
||||
|
||||
public:
|
||||
enum LogLevel
|
||||
{
|
||||
Debug,
|
||||
Info,
|
||||
Error
|
||||
};
|
||||
|
||||
int version();
|
||||
|
||||
void setLogLevel(LogLevel logLevel);
|
||||
LogLevel logLevel() const;
|
||||
|
||||
// Getter and Setters
|
||||
void setViewportSize(const QString &viewportSize);
|
||||
QString viewportSize() const;
|
||||
|
||||
void setLanguage(const QString &language);
|
||||
QString language() const;
|
||||
|
||||
void setAnonymizeIPs(bool anonymize);
|
||||
bool anonymizeIPs();
|
||||
|
||||
void setSendInterval(int milliseconds);
|
||||
int sendInterval() const;
|
||||
|
||||
void enable(bool state = true);
|
||||
bool isEnabled();
|
||||
|
||||
/// Get or set the network access manager. If none is set, the class creates its own on the first request
|
||||
void setNetworkAccessManager(QNetworkAccessManager *networkAccessManager);
|
||||
QNetworkAccessManager *networkAccessManager() const;
|
||||
|
||||
public slots:
|
||||
void sendScreenView(const QString &screenName, const QVariantMap &customValues = QVariantMap());
|
||||
void sendEvent(const QString &category, const QString &action, const QString &label = QString(), const QVariant &value = QVariant(),
|
||||
const QVariantMap &customValues = QVariantMap());
|
||||
void sendException(const QString &exceptionDescription, bool exceptionFatal = true, const QVariantMap &customValues = QVariantMap());
|
||||
void startSession();
|
||||
void endSession();
|
||||
|
||||
private:
|
||||
GAnalyticsWorker *d;
|
||||
|
||||
friend QDataStream &operator<<(QDataStream &outStream, const GAnalytics &analytics);
|
||||
friend QDataStream &operator>>(QDataStream &inStream, GAnalytics &analytics);
|
||||
};
|
||||
|
||||
QDataStream &operator<<(QDataStream &outStream, const GAnalytics &analytics);
|
||||
QDataStream &operator>>(QDataStream &inStream, GAnalytics &analytics);
|
@ -1,237 +0,0 @@
|
||||
#include "ganalytics.h"
|
||||
#include "ganalytics_worker.h"
|
||||
#include "sys.h"
|
||||
|
||||
#include <QDataStream>
|
||||
#include <QDebug>
|
||||
#include <QLocale>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QNetworkReply>
|
||||
#include <QNetworkRequest>
|
||||
#include <QQueue>
|
||||
#include <QSettings>
|
||||
#include <QTimer>
|
||||
#include <QUrlQuery>
|
||||
#include <QUuid>
|
||||
|
||||
GAnalytics::GAnalytics(const QString &trackingID, const QString &clientID, const int version, QObject *parent) : QObject(parent)
|
||||
{
|
||||
d = new GAnalyticsWorker(this);
|
||||
d->m_trackingID = trackingID;
|
||||
d->m_clientID = clientID;
|
||||
d->m_version = version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Destructor of class GAnalytics.
|
||||
*/
|
||||
GAnalytics::~GAnalytics()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
void GAnalytics::setLogLevel(GAnalytics::LogLevel logLevel)
|
||||
{
|
||||
d->m_logLevel = logLevel;
|
||||
}
|
||||
|
||||
GAnalytics::LogLevel GAnalytics::logLevel() const
|
||||
{
|
||||
return d->m_logLevel;
|
||||
}
|
||||
|
||||
// SETTER and GETTER
|
||||
void GAnalytics::setViewportSize(const QString &viewportSize)
|
||||
{
|
||||
d->m_viewportSize = viewportSize;
|
||||
}
|
||||
|
||||
QString GAnalytics::viewportSize() const
|
||||
{
|
||||
return d->m_viewportSize;
|
||||
}
|
||||
|
||||
void GAnalytics::setLanguage(const QString &language)
|
||||
{
|
||||
d->m_language = language;
|
||||
}
|
||||
|
||||
QString GAnalytics::language() const
|
||||
{
|
||||
return d->m_language;
|
||||
}
|
||||
|
||||
void GAnalytics::setAnonymizeIPs(bool anonymize)
|
||||
{
|
||||
d->m_anonymizeIPs = anonymize;
|
||||
}
|
||||
|
||||
bool GAnalytics::anonymizeIPs()
|
||||
{
|
||||
return d->m_anonymizeIPs;
|
||||
}
|
||||
|
||||
void GAnalytics::setSendInterval(int milliseconds)
|
||||
{
|
||||
d->m_timer.setInterval(milliseconds);
|
||||
}
|
||||
|
||||
int GAnalytics::sendInterval() const
|
||||
{
|
||||
return (d->m_timer.interval());
|
||||
}
|
||||
|
||||
bool GAnalytics::isEnabled()
|
||||
{
|
||||
return d->m_isEnabled;
|
||||
}
|
||||
|
||||
void GAnalytics::enable(bool state)
|
||||
{
|
||||
d->enable(state);
|
||||
}
|
||||
|
||||
int GAnalytics::version()
|
||||
{
|
||||
return d->m_version;
|
||||
}
|
||||
|
||||
void GAnalytics::setNetworkAccessManager(QNetworkAccessManager *networkAccessManager)
|
||||
{
|
||||
if (d->networkManager != networkAccessManager)
|
||||
{
|
||||
// Delete the old network manager if it was our child
|
||||
if (d->networkManager && d->networkManager->parent() == this)
|
||||
{
|
||||
d->networkManager->deleteLater();
|
||||
}
|
||||
|
||||
d->networkManager = networkAccessManager;
|
||||
}
|
||||
}
|
||||
|
||||
QNetworkAccessManager *GAnalytics::networkAccessManager() const
|
||||
{
|
||||
return d->networkManager;
|
||||
}
|
||||
|
||||
static void appendCustomValues(QUrlQuery &query, const QVariantMap &customValues)
|
||||
{
|
||||
for (QVariantMap::const_iterator iter = customValues.begin(); iter != customValues.end(); ++iter)
|
||||
{
|
||||
query.addQueryItem(iter.key(), iter.value().toString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sent screen view is called when the user changed the applications view.
|
||||
* These action of the user should be noticed and reported. Therefore
|
||||
* a QUrlQuery is build in this method. It holts all the parameter for
|
||||
* a http POST. The UrlQuery will be stored in a message Queue.
|
||||
*/
|
||||
void GAnalytics::sendScreenView(const QString &screenName, const QVariantMap &customValues)
|
||||
{
|
||||
d->logMessage(Info, QString("ScreenView: %1").arg(screenName));
|
||||
|
||||
QUrlQuery query = d->buildStandardPostQuery("screenview");
|
||||
query.addQueryItem("cd", screenName);
|
||||
query.addQueryItem("an", d->m_appName);
|
||||
query.addQueryItem("av", d->m_appVersion);
|
||||
appendCustomValues(query, customValues);
|
||||
|
||||
d->enqueQueryWithCurrentTime(query);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called whenever a button was pressed in the application.
|
||||
* A query for a POST message will be created to report this event. The
|
||||
* created query will be stored in a message queue.
|
||||
*/
|
||||
void GAnalytics::sendEvent(const QString &category, const QString &action, const QString &label, const QVariant &value, const QVariantMap &customValues)
|
||||
{
|
||||
QUrlQuery query = d->buildStandardPostQuery("event");
|
||||
query.addQueryItem("an", d->m_appName);
|
||||
query.addQueryItem("av", d->m_appVersion);
|
||||
query.addQueryItem("ec", category);
|
||||
query.addQueryItem("ea", action);
|
||||
if (!label.isEmpty())
|
||||
query.addQueryItem("el", label);
|
||||
if (value.isValid())
|
||||
query.addQueryItem("ev", value.toString());
|
||||
|
||||
appendCustomValues(query, customValues);
|
||||
|
||||
d->enqueQueryWithCurrentTime(query);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method is called after an exception was raised. It builds a
|
||||
* query for a POST message. These query will be stored in a
|
||||
* message queue.
|
||||
*/
|
||||
void GAnalytics::sendException(const QString &exceptionDescription, bool exceptionFatal, const QVariantMap &customValues)
|
||||
{
|
||||
QUrlQuery query = d->buildStandardPostQuery("exception");
|
||||
query.addQueryItem("an", d->m_appName);
|
||||
query.addQueryItem("av", d->m_appVersion);
|
||||
|
||||
query.addQueryItem("exd", exceptionDescription);
|
||||
|
||||
if (exceptionFatal)
|
||||
{
|
||||
query.addQueryItem("exf", "1");
|
||||
}
|
||||
else
|
||||
{
|
||||
query.addQueryItem("exf", "0");
|
||||
}
|
||||
appendCustomValues(query, customValues);
|
||||
|
||||
d->enqueQueryWithCurrentTime(query);
|
||||
}
|
||||
|
||||
/**
|
||||
* Session starts. This event will be sent by a POST message.
|
||||
* Query is setup in this method and stored in the message
|
||||
* queue.
|
||||
*/
|
||||
void GAnalytics::startSession()
|
||||
{
|
||||
QVariantMap customValues;
|
||||
customValues.insert("sc", "start");
|
||||
sendEvent("Session", "Start", QString(), QVariant(), customValues);
|
||||
}
|
||||
|
||||
/**
|
||||
* Session ends. This event will be sent by a POST message.
|
||||
* Query is setup in this method and stored in the message
|
||||
* queue.
|
||||
*/
|
||||
void GAnalytics::endSession()
|
||||
{
|
||||
QVariantMap customValues;
|
||||
customValues.insert("sc", "end");
|
||||
sendEvent("Session", "End", QString(), QVariant(), customValues);
|
||||
}
|
||||
|
||||
/**
|
||||
* Qut stream to persist class GAnalytics.
|
||||
*/
|
||||
QDataStream &operator<<(QDataStream &outStream, const GAnalytics &analytics)
|
||||
{
|
||||
outStream << analytics.d->persistMessageQueue();
|
||||
|
||||
return outStream;
|
||||
}
|
||||
|
||||
/**
|
||||
* In stream to read GAnalytics from file.
|
||||
*/
|
||||
QDataStream &operator>>(QDataStream &inStream, GAnalytics &analytics)
|
||||
{
|
||||
QList<QString> dataList;
|
||||
inStream >> dataList;
|
||||
analytics.d->readMessagesFromFile(dataList);
|
||||
|
||||
return inStream;
|
||||
}
|
@ -1,254 +0,0 @@
|
||||
#include "ganalytics.h"
|
||||
#include "ganalytics_worker.h"
|
||||
#include "sys.h"
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QNetworkReply>
|
||||
|
||||
#include <QGuiApplication>
|
||||
#include <QScreen>
|
||||
|
||||
const QLatin1String GAnalyticsWorker::dateTimeFormat("yyyy,MM,dd-hh:mm::ss:zzz");
|
||||
|
||||
GAnalyticsWorker::GAnalyticsWorker(GAnalytics *parent)
|
||||
: QObject(parent), q(parent), m_logLevel(GAnalytics::Error)
|
||||
{
|
||||
m_appName = QCoreApplication::instance()->applicationName();
|
||||
m_appVersion = QCoreApplication::instance()->applicationVersion();
|
||||
m_request.setUrl(QUrl("https://www.google-analytics.com/collect"));
|
||||
m_request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
|
||||
m_request.setHeader(QNetworkRequest::UserAgentHeader, getUserAgent());
|
||||
|
||||
m_language = QLocale::system().name().toLower().replace("_", "-");
|
||||
m_screenResolution = getScreenResolution();
|
||||
|
||||
m_timer.setInterval(m_timerInterval);
|
||||
connect(&m_timer, &QTimer::timeout, this, &GAnalyticsWorker::postMessage);
|
||||
}
|
||||
|
||||
void GAnalyticsWorker::enable(bool state)
|
||||
{
|
||||
// state change to the same is not valid.
|
||||
if(m_isEnabled == state)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_isEnabled = state;
|
||||
if(m_isEnabled)
|
||||
{
|
||||
// enable -> start doing things :)
|
||||
m_timer.start();
|
||||
}
|
||||
else
|
||||
{
|
||||
// disable -> stop the timer
|
||||
m_timer.stop();
|
||||
}
|
||||
}
|
||||
|
||||
void GAnalyticsWorker::logMessage(GAnalytics::LogLevel level, const QString &message)
|
||||
{
|
||||
if (m_logLevel > level)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
qDebug() << "[Analytics]" << message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the POST query. Adds all parameter to the query
|
||||
* which are used in every POST.
|
||||
* @param type Type of POST message. The event which is to post.
|
||||
* @return query Most used parameter in a query for a POST.
|
||||
*/
|
||||
QUrlQuery GAnalyticsWorker::buildStandardPostQuery(const QString &type)
|
||||
{
|
||||
QUrlQuery query;
|
||||
query.addQueryItem("v", "1");
|
||||
query.addQueryItem("tid", m_trackingID);
|
||||
query.addQueryItem("cid", m_clientID);
|
||||
if (!m_userID.isEmpty())
|
||||
{
|
||||
query.addQueryItem("uid", m_userID);
|
||||
}
|
||||
query.addQueryItem("t", type);
|
||||
query.addQueryItem("ul", m_language);
|
||||
query.addQueryItem("vp", m_viewportSize);
|
||||
query.addQueryItem("sr", m_screenResolution);
|
||||
if(m_anonymizeIPs)
|
||||
{
|
||||
query.addQueryItem("aip", "1");
|
||||
}
|
||||
return query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get primary screen resolution.
|
||||
* @return A QString like "800x600".
|
||||
*/
|
||||
QString GAnalyticsWorker::getScreenResolution()
|
||||
{
|
||||
QScreen *screen = QGuiApplication::primaryScreen();
|
||||
QSize size = screen->size();
|
||||
|
||||
return QString("%1x%2").arg(size.width()).arg(size.height());
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to gain information about the system where this application
|
||||
* is running. It needs to get the name and version of the operating
|
||||
* system, the language and screen resolution.
|
||||
* All this information will be send in POST messages.
|
||||
* @return agent A QString with all the information formatted for a POST message.
|
||||
*/
|
||||
QString GAnalyticsWorker::getUserAgent()
|
||||
{
|
||||
return QString("%1/%2").arg(m_appName).arg(m_appVersion);
|
||||
}
|
||||
|
||||
/**
|
||||
* The message queue contains a list of QueryBuffer object.
|
||||
* QueryBuffer holds a QUrlQuery object and a QDateTime object.
|
||||
* These both object are freed from the buffer object and
|
||||
* inserted as QString objects in a QList.
|
||||
* @return dataList The list with concartinated queue data.
|
||||
*/
|
||||
QList<QString> GAnalyticsWorker::persistMessageQueue()
|
||||
{
|
||||
QList<QString> dataList;
|
||||
foreach (QueryBuffer buffer, m_messageQueue)
|
||||
{
|
||||
dataList << buffer.postQuery.toString();
|
||||
dataList << buffer.time.toString(dateTimeFormat);
|
||||
}
|
||||
return dataList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads persistent messages from a file.
|
||||
* Gets all message data as a QList<QString>.
|
||||
* Two lines in the list build a QueryBuffer object.
|
||||
*/
|
||||
void GAnalyticsWorker::readMessagesFromFile(const QList<QString> &dataList)
|
||||
{
|
||||
QListIterator<QString> iter(dataList);
|
||||
while (iter.hasNext())
|
||||
{
|
||||
QString queryString = iter.next();
|
||||
QString dateString = iter.next();
|
||||
QUrlQuery query;
|
||||
query.setQuery(queryString);
|
||||
QDateTime dateTime = QDateTime::fromString(dateString, dateTimeFormat);
|
||||
QueryBuffer buffer;
|
||||
buffer.postQuery = query;
|
||||
buffer.time = dateTime;
|
||||
m_messageQueue.enqueue(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a QUrlQuery object and wrapp it together with
|
||||
* a QTime object into a QueryBuffer struct. These struct
|
||||
* will be stored in the message queue.
|
||||
*/
|
||||
void GAnalyticsWorker::enqueQueryWithCurrentTime(const QUrlQuery &query)
|
||||
{
|
||||
QueryBuffer buffer;
|
||||
buffer.postQuery = query;
|
||||
buffer.time = QDateTime::currentDateTime();
|
||||
|
||||
m_messageQueue.enqueue(buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* This function is called by a timer interval.
|
||||
* The function tries to send a messages from the queue.
|
||||
* If message was successfully send then this function
|
||||
* will be called back to send next message.
|
||||
* If message queue contains more than one message then
|
||||
* the connection will kept open.
|
||||
* The message POST is asyncroniously when the server
|
||||
* answered a signal will be emitted.
|
||||
*/
|
||||
void GAnalyticsWorker::postMessage()
|
||||
{
|
||||
if (m_messageQueue.isEmpty())
|
||||
{
|
||||
// queue empty -> try sending later
|
||||
m_timer.start();
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
// queue has messages -> stop timer and start sending
|
||||
m_timer.stop();
|
||||
}
|
||||
|
||||
QString connection = "close";
|
||||
if (m_messageQueue.count() > 1)
|
||||
{
|
||||
connection = "keep-alive";
|
||||
}
|
||||
|
||||
QueryBuffer buffer = m_messageQueue.head();
|
||||
QDateTime sendTime = QDateTime::currentDateTime();
|
||||
qint64 timeDiff = buffer.time.msecsTo(sendTime);
|
||||
|
||||
if (timeDiff > fourHours)
|
||||
{
|
||||
// too old.
|
||||
m_messageQueue.dequeue();
|
||||
emit postMessage();
|
||||
return;
|
||||
}
|
||||
|
||||
buffer.postQuery.addQueryItem("qt", QString::number(timeDiff));
|
||||
m_request.setRawHeader("Connection", connection.toUtf8());
|
||||
m_request.setHeader(QNetworkRequest::ContentLengthHeader, buffer.postQuery.toString().length());
|
||||
|
||||
logMessage(GAnalytics::Debug, "Query string = " + buffer.postQuery.toString());
|
||||
|
||||
// Create a new network access manager if we don't have one yet
|
||||
if (networkManager == NULL)
|
||||
{
|
||||
networkManager = new QNetworkAccessManager(this);
|
||||
}
|
||||
|
||||
QNetworkReply *reply = networkManager->post(m_request, buffer.postQuery.query(QUrl::EncodeUnicode).toUtf8());
|
||||
connect(reply, SIGNAL(finished()), this, SLOT(postMessageFinished()));
|
||||
}
|
||||
|
||||
/**
|
||||
* NetworkAccsessManager has finished to POST a message.
|
||||
* If POST message was successfully send then the message
|
||||
* query should be removed from queue.
|
||||
* SIGNAL "postMessage" will be emitted to send next message
|
||||
* if there is any.
|
||||
* If message couldn't be send then next try is when the
|
||||
* timer emits its signal.
|
||||
*/
|
||||
void GAnalyticsWorker::postMessageFinished()
|
||||
{
|
||||
QNetworkReply *reply = qobject_cast<QNetworkReply *>(sender());
|
||||
|
||||
int httpStausCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
|
||||
if (httpStausCode < 200 || httpStausCode > 299)
|
||||
{
|
||||
logMessage(GAnalytics::Error, QString("Error posting message: %1").arg(reply->errorString()));
|
||||
|
||||
// An error ocurred. Try sending later.
|
||||
m_timer.start();
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
logMessage(GAnalytics::Debug, "Message sent");
|
||||
}
|
||||
|
||||
m_messageQueue.dequeue();
|
||||
postMessage();
|
||||
reply->deleteLater();
|
||||
}
|
@ -1,65 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <QUrlQuery>
|
||||
#include <QDateTime>
|
||||
#include <QTimer>
|
||||
#include <QNetworkRequest>
|
||||
#include <QQueue>
|
||||
|
||||
struct QueryBuffer
|
||||
{
|
||||
QUrlQuery postQuery;
|
||||
QDateTime time;
|
||||
};
|
||||
|
||||
class GAnalyticsWorker : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit GAnalyticsWorker(GAnalytics *parent = 0);
|
||||
|
||||
GAnalytics *q;
|
||||
|
||||
QNetworkAccessManager *networkManager = nullptr;
|
||||
|
||||
QQueue<QueryBuffer> m_messageQueue;
|
||||
QTimer m_timer;
|
||||
QNetworkRequest m_request;
|
||||
GAnalytics::LogLevel m_logLevel;
|
||||
|
||||
QString m_trackingID;
|
||||
QString m_clientID;
|
||||
QString m_userID;
|
||||
QString m_appName;
|
||||
QString m_appVersion;
|
||||
QString m_language;
|
||||
QString m_screenResolution;
|
||||
QString m_viewportSize;
|
||||
|
||||
bool m_anonymizeIPs = false;
|
||||
bool m_isEnabled = false;
|
||||
int m_timerInterval = 30000;
|
||||
int m_version = 0;
|
||||
|
||||
const static int fourHours = 4 * 60 * 60 * 1000;
|
||||
const static QLatin1String dateTimeFormat;
|
||||
|
||||
public:
|
||||
void logMessage(GAnalytics::LogLevel level, const QString &message);
|
||||
|
||||
QUrlQuery buildStandardPostQuery(const QString &type);
|
||||
QString getScreenResolution();
|
||||
QString getUserAgent();
|
||||
QList<QString> persistMessageQueue();
|
||||
void readMessagesFromFile(const QList<QString> &dataList);
|
||||
|
||||
void enqueQueryWithCurrentTime(const QUrlQuery &query);
|
||||
void setIsSending(bool doSend);
|
||||
void enable(bool state);
|
||||
|
||||
public slots:
|
||||
void postMessage();
|
||||
void postMessageFinished();
|
||||
};
|
||||
|
@ -8,6 +8,7 @@ set(Launcher_UserAgent "${Launcher_CommonName}/5.0" PARENT_SCOPE)
|
||||
set(Launcher_ConfigFile "polymc.cfg" PARENT_SCOPE)
|
||||
set(Launcher_Git "https://github.com/PolyMC/PolyMC" PARENT_SCOPE)
|
||||
|
||||
set(Launcher_Desktop "program_info/polymc.desktop" PARENT_SCOPE)
|
||||
set(Launcher_Branding_ICNS "program_info/Launcher.icns" PARENT_SCOPE)
|
||||
set(Launcher_Branding_WindowsRC "program_info/launcher.rc" PARENT_SCOPE)
|
||||
set(Launcher_Branding_LogoQRC "program_info/logo.qrc" PARENT_SCOPE)
|
||||
|
11
program_info/polymc.desktop
Normal file
11
program_info/polymc.desktop
Normal file
@ -0,0 +1,11 @@
|
||||
[Desktop Entry]
|
||||
Version=1.0
|
||||
Name=PolyMC
|
||||
Comment=A custom launcher for Minecraft that allows you to easily manage multiple installations of Minecraft at once.
|
||||
Type=Application
|
||||
Terminal=false
|
||||
Exec=polymc
|
||||
StartupNotify=true
|
||||
Icon=polymc
|
||||
Categories=Game;
|
||||
Keywords=game;minecraft;launcher;
|
Reference in New Issue
Block a user