NOISSUE import google analytics from third party

See: https://github.com/HSAnet/qt-google-analytics

Sadly, the API and its internals are not acceptable and it needs changes
upstream likely wouldn't allow.
This commit is contained in:
Petr Mrázek 2016-11-20 12:04:29 +01:00
parent 69be23c5f6
commit 2ec15c32e4
6 changed files with 1100 additions and 0 deletions

View File

@ -91,6 +91,7 @@ option(NBT_USE_ZLIB "Build NBT library with zlib support" OFF)
option(NBT_BUILD_TESTS "Build NBT library tests" OFF) #FIXME: fix unit tests.
add_subdirectory(libraries/libnbtplusplus)
add_subdirectory(libraries/ganalytics) # google analytics library
add_subdirectory(libraries/hoedown) # markdown parser
add_subdirectory(libraries/launcher) # java based launcher part for Minecraft
add_subdirectory(libraries/javacheck) # java compatibility checker

View File

@ -0,0 +1,10 @@
project(ganalytics)
find_package(Qt5Core)
find_package(Qt5Gui)
find_package(Qt5Network)
add_library(ganalytics STATIC ganalytics.cpp ganalytics.h)
qt5_use_modules(ganalytics Core Gui Network)
target_include_directories(ganalytics PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
target_compile_definitions(ganalytics PRIVATE -DQT_GUI_LIB)

View File

@ -0,0 +1,24 @@
Copyright (c) 2014-2015, University of Applied Sciences Augsburg
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the University of Applied Sciences Augsburg nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
OODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
UT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@ -0,0 +1,34 @@
qt-google-analytics
================
Qt5 classes for providing google analytics usage in a Qt/QML application.
## Building
Include ```qt-google-analytics.pri``` in your .pro file.
## Using
Please make sure you have set your application information using ```QApplication::setApplicationName``` and ```QApplication::setApplicationVersion```.
### In C++:
```
GAnalytics tracker("UA-my-id");
tracker.sendScreenView("Main Screen");
```
### In QtQuick:
Register the class on the C++ side using ```qmlRegisterType<GAnalytics>("analytics", 0, 1, "Tracker");```
```
Tracker {
id: tracker
trackingID: "UA-my-id"
}
[...]
tracker.sendScreenView("Main Screen")
```
There is also an example application in the examples folder.
## License
Copyright (c) 2014-2016, University of Applied Sciences Augsburg.
All rights reserved. Distributed under the terms and conditions of the BSD License. See separate LICENSE.txt.

View File

@ -0,0 +1,923 @@
#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"

View File

@ -0,0 +1,108 @@
#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