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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef QT_GUI_LIB -#include -#include -#endif // QT_GUI_LIB - -#ifdef QT_QML_LIB -#include -#include -#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 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 persistMessageQueue(); - void readMessagesFromFile(const QList &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 - -QString GAnalytics::Private::getSystemInfo() -{ - return QString("Linux; U; Android %1; %2 %3 Build/%4; %5") - .arg(QAndroidJniObject::getStaticObjectField("android/os/Build$VERSION", "RELEASE").toString()) - .arg(QAndroidJniObject::getStaticObjectField("android/os/Build", "MANUFACTURER").toString()) - .arg(QAndroidJniObject::getStaticObjectField("android/os/Build", "MODEL").toString()) - .arg(QAndroidJniObject::getStaticObjectField("android/os/Build", "ID").toString()) - .arg(QAndroidJniObject::getStaticObjectField("android/os/Build", "BRAND").toString()); -} -#elif defined(Q_OS_LINUX) -#include - -/** - * 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 GAnalytics::Private::persistMessageQueue() -{ - QList 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. - * Two lines in the list build a QueryBuffer object. - */ -void GAnalytics::Private::readMessagesFromFile(const QList &dataList) -{ - QListIterator 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(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 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 -#include - -#ifdef QT_QML_LIB -#include -#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 +#include + +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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 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 +#include +#include + +#include +#include + +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 GAnalyticsWorker::persistMessageQueue() +{ + QList 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. + * Two lines in the list build a QueryBuffer object. + */ +void GAnalyticsWorker::readMessagesFromFile(const QList &dataList) +{ + QListIterator 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(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 +#include +#include +#include +#include + +struct QueryBuffer +{ + QUrlQuery postQuery; + QDateTime time; +}; + +class GAnalyticsWorker : public QObject +{ + Q_OBJECT + +public: + explicit GAnalyticsWorker(GAnalytics *parent = 0); + + GAnalytics *q; + + QNetworkAccessManager *networkManager = nullptr; + + QQueue 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 persistMessageQueue(); + void readMessagesFromFile(const QList &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 + +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 + +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; +} +