1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-10 23:44:24 +00:00
JUCE/extras/Projucer/Source/Application/jucer_ProjucerAnalytics.cpp

254 lines
7.6 KiB
C++

/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 5 End-User License
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
27th April 2017).
End User License Agreement: www.juce.com/juce-5-licence
Privacy Policy: www.juce.com/juce-5-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
#include "jucer_ProjucerAnalytics.h"
//==============================================================================
ProjucerAnalyticsDestination::ProjucerAnalyticsDestination()
: ThreadedAnalyticsDestination ("ProjucerAnalyticsThread")
{
{
MemoryOutputStream mo;
if (Base64::convertFromBase64 (mo, BinaryData::nothingtoseehere_txt))
apiKey = mo.toString();
}
auto dataDir = File::getSpecialLocation (File::userApplicationDataDirectory)
#if JUCE_MAC
.getChildFile ("Application Support")
#endif
.getChildFile ("Projucer")
.getChildFile ("Analytics");
if (! dataDir.exists())
dataDir.createDirectory();
savedEventsFile = dataDir.getChildFile ("analytics_events.xml");
startAnalyticsThread (initialPeriodMs);
}
ProjucerAnalyticsDestination::~ProjucerAnalyticsDestination()
{
Thread::sleep (initialPeriodMs);
stopAnalyticsThread (1000);
}
//==============================================================================
static void setData (const AnalyticsDestination::AnalyticsEvent& event, StringPairArray& data)
{
data.set ("ea", event.name);
if (event.parameters.getAllKeys().contains ("label"))
data.set ("el", event.parameters.getValue ("label", {}));
data.addArray (event.userProperties);
}
bool ProjucerAnalyticsDestination::logBatchedEvents (const Array<AnalyticsEvent>& events)
{
String appData ("v=1&aip=1&tid=" + apiKey);
StringArray postData;
for (auto& event : events)
{
StringPairArray data;
data.set ("t", "event");
data.set ("cid", event.userID);
switch (event.eventType)
{
case ProjucerAnalyticsEvent::appEvent:
{
data.set ("ec", "App");
setData (event, data);
break;
}
case ProjucerAnalyticsEvent::projectEvent:
{
data.set ("ec", "Project");
setData (event, data);
break;
}
case ProjucerAnalyticsEvent::userEvent:
{
data.set ("ec", "User");
setData (event, data);
break;
}
case ProjucerAnalyticsEvent::exampleEvent:
{
data.set ("ec", "Example");
setData (event, data);
break;
}
case ProjucerAnalyticsEvent::startPageEvent:
{
data.set ("ec", "Start Page");
setData (event, data);
break;
}
default:
{
// unknown event type!
jassertfalse;
break;
}
}
StringArray eventData;
for (auto& key : data.getAllKeys())
eventData.add (key + "=" + URL::addEscapeChars (data[key], true));
postData.add (appData + "&" + eventData.joinIntoString ("&"));
}
auto url = URL ("https://www.google-analytics.com/batch")
.withPOSTData (postData.joinIntoString ("\n"));
{
const ScopedLock lock (webStreamCreation);
if (shouldExit)
return false;
webStream.reset (new WebInputStream (url, true));
}
auto success = webStream->connect (nullptr);
// Do an exponential backoff if we failed to connect.
if (success)
periodMs = initialPeriodMs;
else
periodMs *= 2;
setBatchPeriod (periodMs);
return success;
}
void ProjucerAnalyticsDestination::stopLoggingEvents()
{
const ScopedLock lock (webStreamCreation);
shouldExit = true;
if (webStream.get() != nullptr)
webStream->cancel();
}
//==============================================================================
void ProjucerAnalyticsDestination::saveUnloggedEvents (const std::deque<AnalyticsEvent>& eventsToSave)
{
auto xml = parseXMLIfTagMatches (savedEventsFile, "events");
if (xml == nullptr)
xml = std::make_unique<XmlElement> ("events");
for (auto& event : eventsToSave)
{
auto* xmlEvent = new XmlElement ("google_analytics_event");
xmlEvent->setAttribute ("name", event.name);
xmlEvent->setAttribute ("type", event.eventType);
xmlEvent->setAttribute ("timestamp", (int) event.timestamp);
xmlEvent->setAttribute ("user_id", event.userID);
auto* parameters = new XmlElement ("parameters");
for (auto& key : event.parameters.getAllKeys())
parameters->setAttribute (key, event.parameters[key]);
xmlEvent->addChildElement (parameters);
auto* userProperties = new XmlElement ("user_properties");
for (auto& key : event.userProperties.getAllKeys())
userProperties->setAttribute (key, event.userProperties[key]);
xmlEvent->addChildElement (userProperties);
xml->addChildElement (xmlEvent);
}
xml->writeTo (savedEventsFile, {});
}
void ProjucerAnalyticsDestination::restoreUnloggedEvents (std::deque<AnalyticsEvent>& restoredEventQueue)
{
auto xml = parseXMLIfTagMatches (savedEventsFile, "events");
if (xml == nullptr)
return;
auto numEvents = xml->getNumChildElements();
for (int iEvent = 0; iEvent < numEvents; ++iEvent)
{
auto* xmlEvent = xml->getChildElement (iEvent);
StringPairArray parameters;
auto* xmlParameters = xmlEvent->getChildByName ("parameters");
auto numParameters = xmlParameters->getNumAttributes();
for (int iParam = 0; iParam < numParameters; ++iParam)
parameters.set (xmlParameters->getAttributeName (iParam),
xmlParameters->getAttributeValue (iParam));
StringPairArray userProperties;
auto* xmlUserProperties = xmlEvent->getChildByName ("user_properties");
auto numUserProperties = xmlUserProperties->getNumAttributes();
for (int iProp = 0; iProp < numUserProperties; ++iProp)
userProperties.set (xmlUserProperties->getAttributeName (iProp),
xmlUserProperties->getAttributeValue (iProp));
restoredEventQueue.push_back ({
xmlEvent->getStringAttribute ("name"),
xmlEvent->getIntAttribute ("type"),
static_cast<uint32> (xmlEvent->getIntAttribute ("timestamp")),
parameters,
xmlEvent->getStringAttribute ("user_id"),
userProperties
});
}
savedEventsFile.deleteFile();
}