summaryrefslogtreecommitdiffstats
path: root/libraries/ganalytics/src/ganalytics_worker.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'libraries/ganalytics/src/ganalytics_worker.cpp')
-rw-r--r--libraries/ganalytics/src/ganalytics_worker.cpp252
1 files changed, 252 insertions, 0 deletions
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();
+}