Completely remove Google Analytics library

This commit is contained in:
swirl 2021-12-29 10:37:09 -05:00
parent af76cf59b6
commit f25a9bc103
No known key found for this signature in database
GPG Key ID: 46507311CD292A08
19 changed files with 5 additions and 1036 deletions

View File

@ -78,9 +78,6 @@ set(Launcher_PASTE_EE_API_KEY "utLvciUouSURFzfjPxLBf5W4ISsUX4pwBDF7N1AfZ" CACHE
# Imgur API Client ID # Imgur API Client ID
set(Launcher_IMGUR_CLIENT_ID "5b97b0713fba4a3" CACHE STRING "Client ID you can get from Imgur when you register an application") 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 # 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") 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")
@ -250,7 +247,6 @@ set(NBT_NAME Launcher_nbt++)
set(NBT_DEST_DIR ${LIBRARY_DEST_DIR}) set(NBT_DEST_DIR ${LIBRARY_DEST_DIR})
add_subdirectory(libraries/libnbtplusplus) add_subdirectory(libraries/libnbtplusplus)
add_subdirectory(libraries/ganalytics) # google analytics library
add_subdirectory(libraries/systeminfo) # system information library add_subdirectory(libraries/systeminfo) # system information library
add_subdirectory(libraries/hoedown) # markdown parser add_subdirectory(libraries/hoedown) # markdown parser
add_subdirectory(libraries/launcher) # java based launcher part for Minecraft add_subdirectory(libraries/launcher) # java based launcher part for Minecraft
@ -260,7 +256,7 @@ add_subdirectory(libraries/quazip) # zip manipulation library
add_subdirectory(libraries/rainbow) # Qt extension for colors add_subdirectory(libraries/rainbow) # Qt extension for colors
add_subdirectory(libraries/iconfix) # fork of Qt's QIcon loader add_subdirectory(libraries/iconfix) # fork of Qt's QIcon loader
add_subdirectory(libraries/LocalPeer) # fork of a library from Qt solutions 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/optional-bare)
add_subdirectory(libraries/tomlc99) # toml parser add_subdirectory(libraries/tomlc99) # toml parser
add_subdirectory(libraries/katabasis) # An OAuth2 library that tried to do too much add_subdirectory(libraries/katabasis) # An OAuth2 library that tried to do too much

View File

@ -24,7 +24,6 @@ Config::Config()
BUILD_PLATFORM = "@Launcher_BUILD_PLATFORM@"; BUILD_PLATFORM = "@Launcher_BUILD_PLATFORM@";
UPDATER_BASE = "@Launcher_UPDATER_BASE@"; UPDATER_BASE = "@Launcher_UPDATER_BASE@";
ANALYTICS_ID = "@Launcher_ANALYTICS_ID@";
NOTIFICATION_URL = "@Launcher_NOTIFICATION_URL@"; NOTIFICATION_URL = "@Launcher_NOTIFICATION_URL@";
FULL_VERSION_STR = "@Launcher_VERSION_MAJOR@.@Launcher_VERSION_MINOR@.@Launcher_VERSION_BUILD@"; FULL_VERSION_STR = "@Launcher_VERSION_MAJOR@.@Launcher_VERSION_MINOR@.@Launcher_VERSION_BUILD@";

View File

@ -46,9 +46,6 @@ public:
QString USER_AGENT_UNCACHED; QString USER_AGENT_UNCACHED;
/// Google analytics ID
QString ANALYTICS_ID;
/// URL for notifications /// URL for notifications
QString NOTIFICATION_URL; QString NOTIFICATION_URL;
@ -79,7 +76,7 @@ public:
* Client ID you can get from Imgur when you register an application * Client ID you can get from Imgur when you register an application
*/ */
QString IMGUR_CLIENT_ID; QString IMGUR_CLIENT_ID;
/** /**
* Client ID you can get from Microsoft Identity Platform when you register an application * Client ID you can get from Microsoft Identity Platform when you register an application
*/ */
@ -115,3 +112,4 @@ public:
}; };
extern const Config BuildConfig; extern const Config BuildConfig;

View File

@ -26,7 +26,6 @@
#include "ui/setupwizard/SetupWizard.h" #include "ui/setupwizard/SetupWizard.h"
#include "ui/setupwizard/LanguageWizardPage.h" #include "ui/setupwizard/LanguageWizardPage.h"
#include "ui/setupwizard/JavaWizardPage.h" #include "ui/setupwizard/JavaWizardPage.h"
#include "ui/setupwizard/AnalyticsWizardPage.h"
#include "ui/dialogs/CustomMessageBox.h" #include "ui/dialogs/CustomMessageBox.h"
@ -73,7 +72,6 @@
#include <DesktopServices.h> #include <DesktopServices.h>
#include <LocalPeer.h> #include <LocalPeer.h>
#include <ganalytics.h>
#include <sys.h> #include <sys.h>
@ -719,14 +717,6 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
// paste.ee API key // paste.ee API key
m_settings->registerSetting("PasteEEAPIKey", "multimc"); 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 // Init page provider
{ {
m_globalSettingsProvider = std::make_shared<GenericPageProvider>(tr("Settings")); 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."; 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()) if(createSetupWizard())
{ {
return; return;
@ -974,29 +924,13 @@ bool Application::createSetupWizard()
} }
return false; 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 = [&]() bool languageRequired = [&]()
{ {
if (settings()->get("Language").toString().isEmpty()) if (settings()->get("Language").toString().isEmpty())
return true; return true;
return false; return false;
}(); }();
bool wizardRequired = javaRequired || analyticsRequired || languageRequired; bool wizardRequired = javaRequired || languageRequired;
if(wizardRequired) if(wizardRequired)
{ {
@ -1009,10 +943,6 @@ bool Application::createSetupWizard()
{ {
m_setupWizard->addPage(new JavaWizardPage(m_setupWizard)); m_setupWizard->addPage(new JavaWizardPage(m_setupWizard));
} }
if(analyticsRequired)
{
m_setupWizard->addPage(new AnalyticsWizardPage(m_setupWizard));
}
connect(m_setupWizard, &QDialog::finished, this, &Application::setupWizardFinished); connect(m_setupWizard, &QDialog::finished, this, &Application::setupWizardFinished);
m_setupWizard->show(); m_setupWizard->show();
return true; 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() std::shared_ptr<TranslationsModel> Application::translations()
{ {
return m_translations; return m_translations;
@ -1454,60 +1368,6 @@ MainWindow* Application::showMainWindow(bool minimized)
connect(m_mainWindow, &MainWindow::isClosing, this, &Application::on_windowClose); connect(m_mainWindow, &MainWindow::isClosing, this, &Application::on_windowClose);
m_openWindows++; 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; return m_mainWindow;
} }

View File

@ -33,7 +33,6 @@ class BaseDetachedToolFactory;
class TranslationsModel; class TranslationsModel;
class ITheme; class ITheme;
class MCEditTool; class MCEditTool;
class GAnalytics;
namespace Meta { namespace Meta {
class Index; class Index;
@ -60,10 +59,6 @@ public:
Application(int &argc, char **argv); Application(int &argc, char **argv);
virtual ~Application(); virtual ~Application();
GAnalytics *analytics() const {
return m_analytics;
}
std::shared_ptr<SettingsObject> settings() const { std::shared_ptr<SettingsObject> settings() const {
return m_settings; return m_settings;
} }
@ -161,7 +156,6 @@ private slots:
void messageReceived(const QByteArray & message); void messageReceived(const QByteArray & message);
void controllerSucceeded(); void controllerSucceeded();
void controllerFailed(const QString & error); void controllerFailed(const QString & error);
void analyticsSettingChanged(const Setting &setting, QVariant value);
void setupWizardFinished(int status); void setupWizardFinished(int status);
private: private:
@ -226,7 +220,6 @@ private:
// peer launcher instance connector - used to implement single instance launcher and signalling // peer launcher instance connector - used to implement single instance launcher and signalling
LocalPeer * m_peerInstance = nullptr; LocalPeer * m_peerInstance = nullptr;
GAnalytics * m_analytics = nullptr;
SetupWizard * m_setupWizard = nullptr; SetupWizard * m_setupWizard = nullptr;
public: public:
QString m_instanceIdToLaunch; QString m_instanceIdToLaunch;
@ -236,3 +229,4 @@ public:
QUrl m_zipToImport; QUrl m_zipToImport;
std::unique_ptr<QFile> logFile; std::unique_ptr<QFile> logFile;
}; };

View File

@ -627,8 +627,6 @@ SET(LAUNCHER_SOURCES
# GUI - setup wizard # GUI - setup wizard
ui/setupwizard/SetupWizard.h ui/setupwizard/SetupWizard.h
ui/setupwizard/SetupWizard.cpp ui/setupwizard/SetupWizard.cpp
ui/setupwizard/AnalyticsWizardPage.cpp
ui/setupwizard/AnalyticsWizardPage.h
ui/setupwizard/BaseWizardPage.h ui/setupwizard/BaseWizardPage.h
ui/setupwizard/JavaWizardPage.cpp ui/setupwizard/JavaWizardPage.cpp
ui/setupwizard/JavaWizardPage.h ui/setupwizard/JavaWizardPage.h
@ -932,7 +930,6 @@ target_link_libraries(Launcher_logic
hoedown hoedown
Launcher_rainbow Launcher_rainbow
LocalPeer LocalPeer
ganalytics
) )
target_link_libraries(Launcher_logic) target_link_libraries(Launcher_logic)

View File

@ -73,11 +73,6 @@ LauncherPage::LauncherPage(QWidget *parent) : QWidget(parent), ui(new Ui::Launch
{ {
ui->updateSettingsBox->setHidden(true); 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->fontSizeBox, SIGNAL(valueChanged(int)), SLOT(refreshFontPreview()));
connect(ui->consoleFont, SIGNAL(currentFontChanged(QFont)), SLOT(refreshFontPreview())); connect(ui->consoleFont, SIGNAL(currentFontChanged(QFont)), SLOT(refreshFontPreview()));
@ -321,12 +316,6 @@ void LauncherPage::applySettings()
s->set("InstSortMode", "Name"); s->set("InstSortMode", "Name");
break; break;
} }
// Analytics
if(!BuildConfig.ANALYTICS_ID.isEmpty())
{
s->set("Analytics", ui->analyticsCheck->isChecked());
}
} }
void LauncherPage::loadSettings() void LauncherPage::loadSettings()
{ {
@ -422,12 +411,6 @@ void LauncherPage::loadSettings()
{ {
ui->sortByNameBtn->setChecked(true); ui->sortByNameBtn->setChecked(true);
} }
// Analytics
if(!BuildConfig.ANALYTICS_ID.isEmpty())
{
ui->analyticsCheck->setChecked(s->get("Analytics").toBool());
}
} }
void LauncherPage::refreshFontPreview() void LauncherPage::refreshFontPreview()

View File

@ -485,69 +485,6 @@
</item> </item>
</layout> </layout>
</widget> </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>&lt;html&gt;&lt;head/&gt;
&lt;body&gt;
&lt;p&gt;The launcher sends anonymous usage statistics on every start of the application.&lt;/p&gt;&lt;p&gt;The following data is collected:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Launcher version.&lt;/li&gt;
&lt;li&gt;Operating system name, version and architecture.&lt;/li&gt;
&lt;li&gt;CPU architecture (kernel architecture on linux).&lt;/li&gt;
&lt;li&gt;Size of system memory.&lt;/li&gt;
&lt;li&gt;Java version, architecture and memory settings.&lt;/li&gt;
&lt;/ul&gt;
&lt;/body&gt;&lt;/html&gt;</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> </widget>
</item> </item>
</layout> </layout>

View File

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

View File

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

View File

@ -2,12 +2,10 @@
#include "LanguageWizardPage.h" #include "LanguageWizardPage.h"
#include "JavaWizardPage.h" #include "JavaWizardPage.h"
#include "AnalyticsWizardPage.h"
#include "translations/TranslationsModel.h" #include "translations/TranslationsModel.h"
#include <Application.h> #include <Application.h>
#include <FileSystem.h> #include <FileSystem.h>
#include <ganalytics.h>
#include <QAbstractButton> #include <QAbstractButton>
#include <BuildConfig.h> #include <BuildConfig.h>

View File

@ -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. 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
Hoedown is a revived fork of Sundown, the Markdown parser based on the original code of the Upskirt library by Natacha Porté. Hoedown is a revived fork of Sundown, the Markdown parser based on the original code of the Upskirt library by Natacha Porté.

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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();
}

View File

@ -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();
};