summaryrefslogtreecommitdiffstats
path: root/libraries/ganalytics
diff options
context:
space:
mode:
authorPetr Mrázek <peterix@gmail.com>2016-11-20 12:32:27 +0100
committerPetr Mrázek <peterix@gmail.com>2016-11-21 01:19:22 +0100
commit2f8c752d1fe9976fdbd683d34ae3dcbf4e797591 (patch)
tree2b9fbba468b93accbf9affb07f9ea3b5f9938c66 /libraries/ganalytics
parent2ec15c32e4419c94995f78b1d1ef8790e71b3317 (diff)
downloadMultiMC-2f8c752d1fe9976fdbd683d34ae3dcbf4e797591.tar
MultiMC-2f8c752d1fe9976fdbd683d34ae3dcbf4e797591.tar.gz
MultiMC-2f8c752d1fe9976fdbd683d34ae3dcbf4e797591.tar.lz
MultiMC-2f8c752d1fe9976fdbd683d34ae3dcbf4e797591.tar.xz
MultiMC-2f8c752d1fe9976fdbd683d34ae3dcbf4e797591.zip
NOISSUE reformat and sanitize ganalytics
Diffstat (limited to 'libraries/ganalytics')
-rw-r--r--libraries/ganalytics/CMakeLists.txt23
-rw-r--r--libraries/ganalytics/ganalytics.cpp923
-rw-r--r--libraries/ganalytics/ganalytics.h108
-rw-r--r--libraries/ganalytics/include/ganalytics.h65
-rw-r--r--libraries/ganalytics/src/ganalytics.cpp222
-rw-r--r--libraries/ganalytics/src/ganalytics_worker.cpp252
-rw-r--r--libraries/ganalytics/src/ganalytics_worker.h61
-rw-r--r--libraries/ganalytics/src/sys.h11
-rw-r--r--libraries/ganalytics/src/sys_apple.cpp114
-rw-r--r--libraries/ganalytics/src/sys_unix.cpp13
-rw-r--r--libraries/ganalytics/src/sys_win32.cpp50
11 files changed, 808 insertions, 1034 deletions
diff --git a/libraries/ganalytics/CMakeLists.txt b/libraries/ganalytics/CMakeLists.txt
index f6051d05..4b9a0cee 100644
--- a/libraries/ganalytics/CMakeLists.txt
+++ b/libraries/ganalytics/CMakeLists.txt
@@ -4,7 +4,24 @@ find_package(Qt5Core)
find_package(Qt5Gui)
find_package(Qt5Network)
-add_library(ganalytics STATIC ganalytics.cpp ganalytics.h)
+set(ganalytics_SOURCES
+src/ganalytics.cpp
+src/ganalytics_worker.cpp
+src/ganalytics_worker.h
+src/sys.h
+include/ganalytics.h
+)
+
+if (WIN32)
+ list(APPEND ganalytics_SOURCES src/sys_win32.cpp)
+elseif (UNIX)
+ if(APPLE)
+ list(APPEND ganalytics_SOURCES src/sys_apple.cpp)
+ else()
+ list(APPEND ganalytics_SOURCES src/sys_unix.cpp)
+ endif()
+endif()
+
+add_library(ganalytics STATIC ${ganalytics_SOURCES})
qt5_use_modules(ganalytics Core Gui Network)
-target_include_directories(ganalytics PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
-target_compile_definitions(ganalytics PRIVATE -DQT_GUI_LIB)
+target_include_directories(ganalytics PUBLIC include)
diff --git a/libraries/ganalytics/ganalytics.cpp b/libraries/ganalytics/ganalytics.cpp
deleted file mode 100644
index e2caad50..00000000
--- a/libraries/ganalytics/ganalytics.cpp
+++ /dev/null
@@ -1,923 +0,0 @@
-#include "ganalytics.h"
-
-#include <QCoreApplication>
-#include <QDataStream>
-#include <QDateTime>
-#include <QDebug>
-#include <QLocale>
-#include <QNetworkAccessManager>
-#include <QNetworkReply>
-#include <QNetworkRequest>
-#include <QQueue>
-#include <QSettings>
-#include <QTimer>
-#include <QUrlQuery>
-#include <QUuid>
-
-#ifdef QT_GUI_LIB
-#include <QScreen>
-#include <QGuiApplication>
-#endif // QT_GUI_LIB
-
-#ifdef QT_QML_LIB
-#include <QQmlEngine>
-#include <QQmlContext>
-#endif // QT_QML_LIB
-
-struct QueryBuffer
-{
- QUrlQuery postQuery;
- QDateTime time;
-};
-
-/**
- * Class Private
- * Private members and functions.
- */
-class GAnalytics::Private : public QObject
-{
- Q_OBJECT
-
-public:
- explicit Private(GAnalytics *parent = 0);
- ~Private();
-
- GAnalytics *q;
-
- QNetworkAccessManager *networkManager;
-
- QQueue<QueryBuffer> messageQueue;
- QTimer timer;
- QNetworkRequest request;
- GAnalytics::LogLevel logLevel;
-
- QString trackingID;
- QString clientID;
- QString userID;
- QString appName;
- QString appVersion;
- QString language;
- QString screenResolution;
- QString viewportSize;
-
- bool isSending;
-
- const static int fourHours = 4 * 60 * 60 * 1000;
- const static QString dateTimeFormat;
-
-public:
- void logMessage(GAnalytics::LogLevel level, const QString &message);
-
- QUrlQuery buildStandardPostQuery(const QString &type);
-#ifdef QT_GUI_LIB
- QString getScreenResolution();
-#endif // QT_GUI_LIB
- QString getUserAgent();
- QString getSystemInfo();
- QList<QString> persistMessageQueue();
- void readMessagesFromFile(const QList<QString> &dataList);
- QString getClientID();
- QString getUserID();
- void setUserID(const QString &userID);
- void enqueQueryWithCurrentTime(const QUrlQuery &query);
- void setIsSending(bool doSend);
-
-signals:
- void postNextMessage();
-
-public slots:
- void postMessage();
- void postMessageFinished();
-};
-
-const QString GAnalytics::Private::dateTimeFormat = "yyyy,MM,dd-hh:mm::ss:zzz";
-
-/**
- * Constructor
- * Constructs an object of class Private.
- * @param parent
- */
-GAnalytics::Private::Private(GAnalytics *parent)
-: QObject(parent)
-, q(parent)
-, networkManager(NULL)
-, request(QUrl("http://www.google-analytics.com/collect"))
-, logLevel(GAnalytics::Error)
-, isSending(false)
-{
- clientID = getClientID();
- userID = getUserID();
- language = QLocale::system().name().toLower().replace("_", "-");
-#ifdef QT_GUI_LIB
- screenResolution = getScreenResolution();
-#endif // QT_GUI_LIB
- request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
- appName = QCoreApplication::instance()->applicationName();
- appVersion = QCoreApplication::instance()->applicationVersion();
- request.setHeader(QNetworkRequest::UserAgentHeader, getUserAgent());
- connect(this, SIGNAL(postNextMessage()), this, SLOT(postMessage()));
- timer.start(30000);
- connect(&timer, SIGNAL(timeout()), this, SLOT(postMessage()));
-}
-
-/**
- * Destructor
- * Delete an object of class Private.
- */
-GAnalytics::Private::~Private()
-{
-}
-
-void GAnalytics::Private::logMessage(LogLevel level, const QString &message)
-{
- if (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 GAnalytics::Private::buildStandardPostQuery(const QString &type)
-{
- QUrlQuery query;
- query.addQueryItem("v", "1");
- query.addQueryItem("tid", trackingID);
- query.addQueryItem("cid", clientID);
- if(!userID.isEmpty())
- {
- query.addQueryItem("uid", userID);
- }
- query.addQueryItem("t", type);
- query.addQueryItem("ul", language);
-
-#ifdef QT_GUI_LIB
- query.addQueryItem("vp", viewportSize);
- query.addQueryItem("sr", screenResolution);
-#endif // QT_GUI_LIB
-
- return query;
-}
-
-#ifdef QT_GUI_LIB
-/**
- * Get devicese screen resolution.
- * @return A QString like "800x600".
- */
-QString GAnalytics::Private::getScreenResolution()
-{
- QScreen *screen = QGuiApplication::primaryScreen();
- QSize size = screen->size();
-
- return QString("%1x%2").arg(size.width()).arg(size.height());
-}
-#endif // QT_GUI_LIB
-
-
-/**
- * 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 GAnalytics::Private::getUserAgent()
-{
- QString locale = QLocale::system().name();
- QString system = getSystemInfo();
-
- return QString("%1/%2 (%3; %4) GAnalytics/1.0 (Qt/%5)").arg(appName).arg(appVersion).arg(system).arg(locale).arg(QT_VERSION_STR);
-}
-
-
-#ifdef Q_OS_MAC
-/**
- * Only on Mac OS X
- * Get the Operating system name and version.
- * @return os The operating system name and version in a string.
- */
-QString GAnalytics::Private::getSystemInfo()
-{
- QSysInfo::MacVersion version = QSysInfo::macVersion();
- QString os;
- switch (version)
- {
- case QSysInfo::MV_9:
- os = "Macintosh; Mac OS 9";
- break;
- case QSysInfo::MV_10_0:
- os = "Macintosh; Mac OS 10.0";
- break;
- case QSysInfo::MV_10_1:
- os = "Macintosh; Mac OS 10.1";
- break;
- case QSysInfo::MV_10_2:
- os = "Macintosh; Mac OS 10.2";
- break;
- case QSysInfo::MV_10_3:
- os = "Macintosh; Mac OS 10.3";
- break;
- case QSysInfo::MV_10_4:
- os = "Macintosh; Mac OS 10.4";
- break;
- case QSysInfo::MV_10_5:
- os = "Macintosh; Mac OS 10.5";
- break;
- case QSysInfo::MV_10_6:
- os = "Macintosh; Mac OS 10.6";
- break;
- case QSysInfo::MV_10_7:
- os = "Macintosh; Mac OS 10.7";
- break;
- case QSysInfo::MV_10_8:
- os = "Macintosh; Mac OS 10.8";
- break;
- case QSysInfo::MV_10_9:
- os = "Macintosh; Mac OS 10.9";
- break;
- case QSysInfo::MV_10_10:
- os = "Macintosh; Mac OS 10.10";
- break;
- case QSysInfo::MV_10_11:
- os = "Macintosh; Mac OS 10.11";
- break;
-#if (QT_VERSION >= QT_VERSION_CHECK(5, 8, 0))
- case QSysInfo::MV_10_12:
- os = "Macintosh; Mac OS 10.12";
- break;
-#endif
- case QSysInfo::MV_Unknown:
- os = "Macintosh; Mac OS unknown";
- break;
- case QSysInfo::MV_IOS_5_0:
- os = "iPhone; iOS 5.0";
- break;
- case QSysInfo::MV_IOS_5_1:
- os = "iPhone; iOS 5.1";
- break;
- case QSysInfo::MV_IOS_6_0:
- os = "iPhone; iOS 6.0";
- break;
- case QSysInfo::MV_IOS_6_1:
- os = "iPhone; iOS 6.1";
- break;
- case QSysInfo::MV_IOS_7_0:
- os = "iPhone; iOS 7.0";
- break;
- case QSysInfo::MV_IOS_7_1:
- os = "iPhone; iOS 7.1";
- break;
- case QSysInfo::MV_IOS_8_0:
- os = "iPhone; iOS 8.0";
- break;
- case QSysInfo::MV_IOS_8_1:
- os = "iPhone; iOS 8.1";
- break;
- case QSysInfo::MV_IOS_8_2:
- os = "iPhone; iOS 8.2";
- break;
- case QSysInfo::MV_IOS_8_3:
- os = "iPhone; iOS 8.3";
- break;
- case QSysInfo::MV_IOS_8_4:
- os = "iPhone; iOS 8.4";
- break;
- case QSysInfo::MV_IOS_9_0:
- os = "iPhone; iOS 9.0";
- break;
-#if (QT_VERSION >= QT_VERSION_CHECK(5, 8, 0))
- case QSysInfo::MV_IOS_9_1:
- os = "iPhone; iOS 9.1";
- break;
- case QSysInfo::MV_IOS_9_2:
- os = "iPhone; iOS 9.2";
- break;
- case QSysInfo::MV_IOS_9_3:
- os = "iPhone; iOS 9.3";
- break;
- case QSysInfo::MV_IOS_10_0:
- os = "iPhone; iOS 10.0";
- break;
-#endif
- case QSysInfo::MV_IOS:
- os = "iPhone; iOS unknown";
- break;
- default:
- os = "Macintosh";
- break;
- }
- return os;
-}
-#endif
-
-#ifdef Q_OS_WIN
-/**
- * Only on Windows
- * Get operating system and its version.
- * @return os A QString containing the oprating systems name and version.
- */
-QString GAnalytics::Private::getSystemInfo()
-{
- QSysInfo::WinVersion version = QSysInfo::windowsVersion();
- QString os("Windows; ");
- switch (version)
- {
- case QSysInfo::WV_95:
- os += "Win 95";
- break;
- case QSysInfo::WV_98:
- os += "Win 98";
- break;
- case QSysInfo::WV_Me:
- os += "Win ME";
- break;
- case QSysInfo::WV_NT:
- os += "Win NT";
- break;
- case QSysInfo::WV_2000:
- os += "Win 2000";
- break;
- case QSysInfo::WV_2003:
- os += "Win Server 2003";
- break;
- case QSysInfo::WV_VISTA:
- os += "Win Vista";
- break;
- case QSysInfo::WV_WINDOWS7:
- os += "Win 7";
- break;
- case QSysInfo::WV_WINDOWS8:
- os += "Win 8";
- break;
- case QSysInfo::WV_WINDOWS8_1:
- os += "Win 8.1";
- break;
- case QSysInfo::WV_WINDOWS10:
- os += "Win 10";
- break;
- default:
- os = "Windows; unknown";
- break;
- }
- return os;
-}
-#endif
-
-#if defined(Q_OS_ANDROID)
-#include <QAndroidJniObject>
-
-QString GAnalytics::Private::getSystemInfo()
-{
- return QString("Linux; U; Android %1; %2 %3 Build/%4; %5")
- .arg(QAndroidJniObject::getStaticObjectField<jstring>("android/os/Build$VERSION", "RELEASE").toString())
- .arg(QAndroidJniObject::getStaticObjectField<jstring>("android/os/Build", "MANUFACTURER").toString())
- .arg(QAndroidJniObject::getStaticObjectField<jstring>("android/os/Build", "MODEL").toString())
- .arg(QAndroidJniObject::getStaticObjectField<jstring>("android/os/Build", "ID").toString())
- .arg(QAndroidJniObject::getStaticObjectField<jstring>("android/os/Build", "BRAND").toString());
-}
-#elif defined(Q_OS_LINUX)
-#include <sys/utsname.h>
-
-/**
- * Only on Unix systems.
- * Get operation system name and version.
- * @return os A QString with the name and version of the operating system.
- */
-QString GAnalytics::Private::getSystemInfo()
-{
- struct utsname buf;
- uname(&buf);
- QString system(buf.sysname);
- QString release(buf.release);
-
- return system + "; " + release;
-}
-#endif
-
-
-/**
- * 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> GAnalytics::Private::persistMessageQueue()
-{
- QList<QString> dataList;
- foreach (QueryBuffer buffer, 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 GAnalytics::Private::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;
- messageQueue.enqueue(buffer);
- }
-}
-
-/**
- * Change the user id.
- * @param userID A string with the user id.
- */
-void GAnalytics::Private::setUserID(const QString &userID)
-{
- this->userID = userID;
- QSettings settings;
- settings.setValue("GAnalytics-uid", userID);
-}
-
-/**
- * Get the user id.
- * User id once created is stored in application settings.
- * @return userID A string with the user id.
- */
-QString GAnalytics::Private::getUserID()
-{
- QSettings settings;
- QString userID = settings.value("GAnalytics-uid", QString("")).toString();
-
- return userID;
-}
-
-/**
- * Get the client id.
- * Client id once created is stored in application settings.
- * @return clientID A string with the client id.
- */
-QString GAnalytics::Private::getClientID()
-{
- QSettings settings;
- QString clientID;
- if (!settings.contains("GAnalytics-cid"))
- {
- clientID = QUuid::createUuid().toString();
- settings.setValue("GAnalytics-cid", clientID);
- }
- else
- {
- clientID = settings.value("GAnalytics-cid").toString();
- }
-
- return clientID;
-}
-
-/**
- * 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.
- * @param query
- */
-void GAnalytics::Private::enqueQueryWithCurrentTime(const QUrlQuery &query)
-{
- QueryBuffer buffer;
- buffer.postQuery = query;
- buffer.time = QDateTime::currentDateTime();
-
- messageQueue.enqueue(buffer);
-}
-
-/**
- * Change status of class. Emit signal that status was changed.
- * @param doSend
- */
-void GAnalytics::Private::setIsSending(bool doSend)
-{
- if (doSend)
- {
- timer.stop();
- }
- else
- {
- timer.start();
- }
-
- bool changed = (isSending != doSend);
-
- isSending = doSend;
-
- if (changed)
- {
- emit q->isSendingChanged(isSending);
- }
-}
-
-
-/**
- * CONSTRUCTOR GAnalytics
- * ------------------------------------------------------------------------------------------------------------
- * Constructs the GAnalytics Object.
- * @param parent The application which uses this object.
- * @param trackingID
- * @param clientID
- * @param withGet Determines wheather the messages are send with GET or POST.
- */
-GAnalytics::GAnalytics(QObject *parent)
-: QObject(parent)
-, d(new Private(this))
-{
-}
-
-GAnalytics::GAnalytics(const QString &trackingID, QObject *parent)
-: QObject(parent)
-, d(new Private(this))
-{
- setTrackingID(trackingID);
-}
-
-/**
- * Destructor of class GAnalytics.
- */
-GAnalytics::~GAnalytics()
-{
- delete d;
-}
-
-void GAnalytics::setLogLevel(GAnalytics::LogLevel logLevel)
-{
- if (d->logLevel != logLevel)
- {
- d->logLevel = logLevel;
- emit logLevelChanged();
- }
-}
-
-GAnalytics::LogLevel GAnalytics::logLevel() const
-{
- return d->logLevel;
-}
-
-// SETTER and GETTER
-void GAnalytics::setViewportSize(const QString &viewportSize)
-{
- if (d->viewportSize != viewportSize)
- {
- d->viewportSize = viewportSize;
- emit viewportSizeChanged();
- }
-}
-
-QString GAnalytics::viewportSize() const
-{
- return d->viewportSize;
-}
-
-void GAnalytics::setLanguage(const QString &language)
-{
- if (d->language != language)
- {
- d->language = language;
- emit languageChanged();
- }
-}
-
-QString GAnalytics::language() const
-{
- return d->language;
-}
-
-void GAnalytics::setTrackingID(const QString &trackingID)
-{
- if (d->trackingID != trackingID)
- {
- d->trackingID = trackingID;
- emit trackingIDChanged();
- }
-}
-
-QString GAnalytics::trackingID() const
-{
- return d->trackingID;
-}
-
-void GAnalytics::setSendInterval(int milliseconds)
-{
- if (d->timer.interval() != milliseconds)
- {
- d->timer.setInterval(milliseconds);
- emit sendIntervalChanged();
- }
-}
-
-int GAnalytics::sendInterval() const
-{
- return (d->timer.interval());
-}
-
-void GAnalytics::startSending()
-{
- if (!isSending())
- emit d->postNextMessage();
-}
-
-bool GAnalytics::isSending() const
-{
- return d->isSending;
-}
-
-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());
- }
-}
-
-
-/**
-* SentAppview is called when the user changed the applications view.
-* Deprecated because after SDK Version 3.08 and up no more "appview" event:
-* Use sendScreenView() instead
-* @param appName
-* @param appVersion
-* @param screenName
-*/
-void GAnalytics::sendAppView(const QString &screenName,
- const QVariantMap &customValues)
-{
- sendScreenView(screenName, customValues);
-}
-
-/**
- * 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.
- * @param appName
- * @param appVersion
- * @param screenName
- */
-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->appName);
- query.addQueryItem("av", d->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.
- * @param eventCategory
- * @param eventAction
- * @param eventLabel
- * @param eventValue
- */
-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->appName);
- query.addQueryItem("av", d->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.
- * @param exceptionDescription
- * @param exceptionFatal
- */
-void GAnalytics::sendException(const QString &exceptionDescription,
- bool exceptionFatal,
- const QVariantMap &customValues)
-{
- QUrlQuery query = d->buildStandardPostQuery("exception");
- query.addQueryItem("an", d->appName);
- query.addQueryItem("av", d->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);
-}
-
-/**
- * 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 GAnalytics::Private::postMessage()
-{
- if (messageQueue.isEmpty())
- {
- setIsSending(false);
- return;
- }
- else
- {
- setIsSending(true);
- }
-
- QString connection = "close";
- if (messageQueue.count() > 1)
- {
- connection = "keep-alive";
- }
-
- QueryBuffer buffer = messageQueue.head();
- QDateTime sendTime = QDateTime::currentDateTime();
- qint64 timeDiff = buffer.time.msecsTo(sendTime);
-
- if(timeDiff > fourHours)
- {
- // too old.
- messageQueue.dequeue();
- emit postNextMessage();
- return;
- }
-
- buffer.postQuery.addQueryItem("qt", QString::number(timeDiff));
- request.setRawHeader("Connection", connection.toUtf8());
- request.setHeader(QNetworkRequest::ContentLengthHeader, buffer.postQuery.toString().length());
-
- // Create a new network access manager if we don't have one yet
- if (networkManager == NULL)
- {
- networkManager = new QNetworkAccessManager(this);
- }
-
- QNetworkReply *reply = networkManager->post(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 GAnalytics::Private::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: %s").arg(reply->errorString()));
-
- // An error ocurred.
- setIsSending(false);
- return;
- }
- else
- {
- logMessage(GAnalytics::Debug, "Message sent");
- }
-
- messageQueue.dequeue();
- emit postNextMessage();
- reply->deleteLater();
-}
-
-
-/**
- * Qut stream to persist class GAnalytics.
- * @param outStream
- * @param analytics
- * @return
- */
-QDataStream &operator<<(QDataStream &outStream, const GAnalytics &analytics)
-{
- outStream << analytics.d->persistMessageQueue();
-
- return outStream;
-}
-
-
-/**
- * In stream to read GAnalytics from file.
- * @param inStream
- * @param analytics
- * @return
- */
-QDataStream &operator >>(QDataStream &inStream, GAnalytics &analytics)
-{
- QList<QString> dataList;
- inStream >> dataList;
- analytics.d->readMessagesFromFile(dataList);
-
- return inStream;
-}
-
-#ifdef QT_QML_LIB
-void GAnalytics::classBegin()
-{
- // Get the network access manager from the QmlEngine
- QQmlContext *context = QQmlEngine::contextForObject(this);
- if (context)
- {
- QQmlEngine *engine = context->engine();
- setNetworkAccessManager(engine->networkAccessManager());
- }
-}
-
-void GAnalytics::componentComplete()
-{
-}
-#endif // QT_QML_LIB
-
-#include "ganalytics.moc"
-
diff --git a/libraries/ganalytics/ganalytics.h b/libraries/ganalytics/ganalytics.h
deleted file mode 100644
index f67e737b..00000000
--- a/libraries/ganalytics/ganalytics.h
+++ /dev/null
@@ -1,108 +0,0 @@
-#ifndef GANALYTICS_H
-#define GANALYTICS_H
-
-#include <QObject>
-#include <QVariantMap>
-
-#ifdef QT_QML_LIB
-#include <QQmlParserStatus>
-#endif // QT_QML_LIB
-
-class QNetworkAccessManager;
-
-class GAnalytics : public QObject
-#ifdef QT_QML_LIB
- , public QQmlParserStatus
-#endif // QT_QML_LIB
-{
- Q_OBJECT
-#ifdef QT_QML_LIB
- Q_INTERFACES(QQmlParserStatus)
-#endif // QT_QML_LIB
- Q_ENUMS(LogLevel)
- Q_PROPERTY(LogLevel logLevel READ logLevel WRITE setLogLevel NOTIFY logLevelChanged)
- Q_PROPERTY(QString viewportSize READ viewportSize WRITE setViewportSize NOTIFY viewportSizeChanged)
- Q_PROPERTY(QString language READ language WRITE setLanguage NOTIFY languageChanged)
- Q_PROPERTY(QString trackingID READ trackingID WRITE setTrackingID NOTIFY trackingIDChanged)
- Q_PROPERTY(int sendInterval READ sendInterval WRITE setSendInterval NOTIFY sendIntervalChanged)
- Q_PROPERTY(bool isSending READ isSending NOTIFY isSendingChanged)
-
-public:
- explicit GAnalytics(QObject *parent = 0);
- explicit GAnalytics(const QString &trackingID, QObject *parent = 0);
- ~GAnalytics();
-
-public:
- enum LogLevel
- {
- Debug,
- Info,
- Error
- };
-
- 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 setTrackingID(const QString &trackingID);
- QString trackingID() const;
-
- void setSendInterval(int milliseconds);
- int sendInterval() const;
-
- void startSending();
- bool isSending() const;
-
- /// 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;
-
-#ifdef QT_QML_LIB
- // QQmlParserStatus interface
- void classBegin();
- void componentComplete();
-#endif // QT_QML_LIB
-
-public slots:
- void sendScreenView(const QString &screenName,
- const QVariantMap &customValues = QVariantMap());
- void sendAppView(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();
-
-
-signals:
- void logLevelChanged();
- void viewportSizeChanged();
- void languageChanged();
- void trackingIDChanged();
- void sendIntervalChanged();
- void isSendingChanged(bool isSending);
-
-private:
- class Private;
- Private *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);
-
-#endif // GANALYTICS_H
diff --git a/libraries/ganalytics/include/ganalytics.h b/libraries/ganalytics/include/ganalytics.h
new file mode 100644
index 00000000..7cbd2c85
--- /dev/null
+++ b/libraries/ganalytics/include/ganalytics.h
@@ -0,0 +1,65 @@
+#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, QObject *parent = 0);
+ ~GAnalytics();
+
+public:
+ enum LogLevel
+ {
+ Debug,
+ Info,
+ Error
+ };
+
+ 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 setSendInterval(int milliseconds);
+ int sendInterval() const;
+
+ void startSending();
+ bool isSending() const;
+
+ /// 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();
+
+signals:
+ void isSendingChanged(bool isSending);
+
+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);
diff --git a/libraries/ganalytics/src/ganalytics.cpp b/libraries/ganalytics/src/ganalytics.cpp
new file mode 100644
index 00000000..8d25efe9
--- /dev/null
+++ b/libraries/ganalytics/src/ganalytics.cpp
@@ -0,0 +1,222 @@
+#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, QObject *parent) : QObject(parent)
+{
+ d = new GAnalyticsWorker(this);
+ d->m_trackingID = trackingID;
+ d->m_clientID = clientID;
+}
+
+/**
+ * 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::setSendInterval(int milliseconds)
+{
+ d->m_timer.setInterval(milliseconds);
+}
+
+int GAnalytics::sendInterval() const
+{
+ return (d->m_timer.interval());
+}
+
+void GAnalytics::startSending()
+{
+ if (!isSending())
+ d->postMessage();
+}
+
+bool GAnalytics::isSending() const
+{
+ return d->m_isSending;
+}
+
+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;
+}
diff --git a/libraries/ganalytics/src/ganalytics_worker.cpp b/libraries/ganalytics/src/ganalytics_worker.cpp
new file mode 100644
index 00000000..3dbbb50a
--- /dev/null
+++ b/libraries/ganalytics/src/ganalytics_worker.cpp
@@ -0,0 +1,252 @@
+#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_isSending(false)
+{
+ 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.start(30000);
+ connect(&m_timer, &QTimer::timeout, this, &GAnalyticsWorker::postMessage);
+}
+
+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);
+ 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()
+{
+ QString locale = QLocale::system().name();
+ QString system = Sys::getSystemInfo();
+
+ return QString("%1/%2 (%3; %4) GAnalytics/1.0 (Qt/%5)").arg(m_appName).arg(m_appVersion).arg(system).arg(locale).arg(QT_VERSION_STR);
+}
+
+/**
+ * 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);
+}
+
+/**
+ * Change status of class. Emit signal that status was changed.
+ */
+void GAnalyticsWorker::setIsSending(bool doSend)
+{
+ if (doSend)
+ {
+ m_timer.stop();
+ }
+ else
+ {
+ m_timer.start();
+ }
+
+ bool changed = (m_isSending != doSend);
+
+ m_isSending = doSend;
+
+ if (changed)
+ {
+ emit q->isSendingChanged(m_isSending);
+ }
+}
+
+/**
+ * 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())
+ {
+ setIsSending(false);
+ return;
+ }
+ else
+ {
+ setIsSending(true);
+ }
+
+ 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());
+
+ // 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: %s").arg(reply->errorString()));
+
+ // An error ocurred.
+ setIsSending(false);
+ return;
+ }
+ else
+ {
+ logMessage(GAnalytics::Debug, "Message sent");
+ }
+
+ m_messageQueue.dequeue();
+ postMessage();
+ reply->deleteLater();
+}
diff --git a/libraries/ganalytics/src/ganalytics_worker.h b/libraries/ganalytics/src/ganalytics_worker.h
new file mode 100644
index 00000000..280f9ab2
--- /dev/null
+++ b/libraries/ganalytics/src/ganalytics_worker.h
@@ -0,0 +1,61 @@
+#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_isSending;
+
+ 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);
+
+public slots:
+ void postMessage();
+ void postMessageFinished();
+};
+
diff --git a/libraries/ganalytics/src/sys.h b/libraries/ganalytics/src/sys.h
new file mode 100644
index 00000000..ef37cbde
--- /dev/null
+++ b/libraries/ganalytics/src/sys.h
@@ -0,0 +1,11 @@
+#pragma once
+#include <QString>
+
+namespace Sys
+{
+/**
+ * Get operation system name and version.
+ * @return os A QString with the name and version of the operating system.
+ */
+QString getSystemInfo();
+}
diff --git a/libraries/ganalytics/src/sys_apple.cpp b/libraries/ganalytics/src/sys_apple.cpp
new file mode 100644
index 00000000..cfaea839
--- /dev/null
+++ b/libraries/ganalytics/src/sys_apple.cpp
@@ -0,0 +1,114 @@
+#include "sys.h"
+
+QString Sys::getSystemInfo()
+{
+ QSysInfo::MacVersion version = QSysInfo::macVersion();
+ QString os;
+ switch (version)
+ {
+ case QSysInfo::MV_9:
+ os = "Macintosh; Mac OS 9";
+ break;
+ case QSysInfo::MV_10_0:
+ os = "Macintosh; Mac OS 10.0";
+ break;
+ case QSysInfo::MV_10_1:
+ os = "Macintosh; Mac OS 10.1";
+ break;
+ case QSysInfo::MV_10_2:
+ os = "Macintosh; Mac OS 10.2";
+ break;
+ case QSysInfo::MV_10_3:
+ os = "Macintosh; Mac OS 10.3";
+ break;
+ case QSysInfo::MV_10_4:
+ os = "Macintosh; Mac OS 10.4";
+ break;
+ case QSysInfo::MV_10_5:
+ os = "Macintosh; Mac OS 10.5";
+ break;
+ case QSysInfo::MV_10_6:
+ os = "Macintosh; Mac OS 10.6";
+ break;
+ case QSysInfo::MV_10_7:
+ os = "Macintosh; Mac OS 10.7";
+ break;
+ case QSysInfo::MV_10_8:
+ os = "Macintosh; Mac OS 10.8";
+ break;
+ case QSysInfo::MV_10_9:
+ os = "Macintosh; Mac OS 10.9";
+ break;
+ case QSysInfo::MV_10_10:
+ os = "Macintosh; Mac OS 10.10";
+ break;
+ case QSysInfo::MV_10_11:
+ os = "Macintosh; Mac OS 10.11";
+ break;
+#if (QT_VERSION >= QT_VERSION_CHECK(5, 8, 0))
+ case QSysInfo::MV_10_12:
+ os = "Macintosh; Mac OS 10.12";
+ break;
+#endif
+ case QSysInfo::MV_Unknown:
+ os = "Macintosh; Mac OS unknown";
+ break;
+ case QSysInfo::MV_IOS_5_0:
+ os = "iPhone; iOS 5.0";
+ break;
+ case QSysInfo::MV_IOS_5_1:
+ os = "iPhone; iOS 5.1";
+ break;
+ case QSysInfo::MV_IOS_6_0:
+ os = "iPhone; iOS 6.0";
+ break;
+ case QSysInfo::MV_IOS_6_1:
+ os = "iPhone; iOS 6.1";
+ break;
+ case QSysInfo::MV_IOS_7_0:
+ os = "iPhone; iOS 7.0";
+ break;
+ case QSysInfo::MV_IOS_7_1:
+ os = "iPhone; iOS 7.1";
+ break;
+ case QSysInfo::MV_IOS_8_0:
+ os = "iPhone; iOS 8.0";
+ break;
+ case QSysInfo::MV_IOS_8_1:
+ os = "iPhone; iOS 8.1";
+ break;
+ case QSysInfo::MV_IOS_8_2:
+ os = "iPhone; iOS 8.2";
+ break;
+ case QSysInfo::MV_IOS_8_3:
+ os = "iPhone; iOS 8.3";
+ break;
+ case QSysInfo::MV_IOS_8_4:
+ os = "iPhone; iOS 8.4";
+ break;
+ case QSysInfo::MV_IOS_9_0:
+ os = "iPhone; iOS 9.0";
+ break;
+#if (QT_VERSION >= QT_VERSION_CHECK(5, 8, 0))
+ case QSysInfo::MV_IOS_9_1:
+ os = "iPhone; iOS 9.1";
+ break;
+ case QSysInfo::MV_IOS_9_2:
+ os = "iPhone; iOS 9.2";
+ break;
+ case QSysInfo::MV_IOS_9_3:
+ os = "iPhone; iOS 9.3";
+ break;
+ case QSysInfo::MV_IOS_10_0:
+ os = "iPhone; iOS 10.0";
+ break;
+#endif
+ case QSysInfo::MV_IOS:
+ os = "iPhone; iOS unknown";
+ break;
+ default:
+ os = "Macintosh";
+ break;
+ }
+ return os;
+}
diff --git a/libraries/ganalytics/src/sys_unix.cpp b/libraries/ganalytics/src/sys_unix.cpp
new file mode 100644
index 00000000..b4dd9b2b
--- /dev/null
+++ b/libraries/ganalytics/src/sys_unix.cpp
@@ -0,0 +1,13 @@
+#include "sys.h"
+
+#include <sys/utsname.h>
+
+QString Sys::getSystemInfo()
+{
+ struct utsname buf;
+ uname(&buf);
+ QString system(buf.sysname);
+ QString release(buf.release);
+
+ return system + "; " + release;
+}
diff --git a/libraries/ganalytics/src/sys_win32.cpp b/libraries/ganalytics/src/sys_win32.cpp
new file mode 100644
index 00000000..42c0f280
--- /dev/null
+++ b/libraries/ganalytics/src/sys_win32.cpp
@@ -0,0 +1,50 @@
+#include "sys.h"
+
+QString Sys::getSystemInfo()
+{
+ QSysInfo::WinVersion version = QSysInfo::windowsVersion();
+ QString os("Windows; ");
+ switch (version)
+ {
+ case QSysInfo::WV_95:
+ os += "Win 95";
+ break;
+ case QSysInfo::WV_98:
+ os += "Win 98";
+ break;
+ case QSysInfo::WV_Me:
+ os += "Win ME";
+ break;
+ case QSysInfo::WV_NT:
+ os += "Win NT";
+ break;
+ case QSysInfo::WV_2000:
+ os += "Win 2000";
+ break;
+ case QSysInfo::WV_2003:
+ os += "Win Server 2003";
+ break;
+ case QSysInfo::WV_VISTA:
+ os += "Win Vista";
+ break;
+ case QSysInfo::WV_WINDOWS7:
+ os += "Win 7";
+ break;
+ case QSysInfo::WV_WINDOWS8:
+ os += "Win 8";
+ break;
+ case QSysInfo::WV_WINDOWS8_1:
+ os += "Win 8.1";
+ break;
+#if (QT_VERSION >= QT_VERSION_CHECK(5, 5, 0))
+ case QSysInfo::WV_WINDOWS10:
+ os += "Win 10";
+ break;
+#endif
+ default:
+ os = "Windows; unknown";
+ break;
+ }
+ return os;
+}
+