1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-29 02:40:05 +00:00

Added a popup to the Projucer informing the user about the collection of analytics data

This commit is contained in:
hogliux 2017-05-04 15:24:28 +01:00
parent 5d491ee9c7
commit 9b7e944a54
13 changed files with 322 additions and 40 deletions

View file

@ -53,8 +53,8 @@
// BEGIN SECTION A
#define JUCE_DISPLAY_SPLASH_SCREEN 1
#define JUCE_REPORT_APP_USAGE 1
#define JUCE_DISPLAY_SPLASH_SCREEN 0
#define JUCE_REPORT_APP_USAGE 0
// END SECTION A

View file

@ -2,8 +2,8 @@
<JUCERPROJECT id="M70qfTRRk" name="Projucer" projectType="guiapp" juceFolder="../../juce"
jucerVersion="5.0.0" version="5.0.0" bundleIdentifier="com.juce.theprojucer"
defines="" includeBinaryInAppConfig="1" displaySplashScreen="1"
reportAppUsage="1" useDarkSplashScreen="1" splashScreenColour="Dark">
defines="" includeBinaryInAppConfig="1" splashScreenColour="Dark"
displaySplashScreen="0" reportAppUsage="0">
<EXPORTFORMATS>
<XCODE_MAC targetFolder="Builds/MacOSX" vstFolder="~/SDKs/vstsdk2.4" rtasFolder="~/SDKs/PT_80_SDK"
documentExtensions=".jucer" objCExtraSuffix="zkVtji" bigIcon="OCyr5F"

View file

@ -82,9 +82,13 @@ void ProjucerApplication::initialise (const String& commandLine)
initialiseBasics();
if (commandLine.isNotEmpty())
isRunningCommandLine = commandLine.isNotEmpty();
licenseController = new LicenseController;
licenseController->addLicenseStatusChangedCallback (this);
if (isRunningCommandLine)
{
isRunningCommandLine = true;
const int appReturnCode = performCommandLine (commandLine);
if (appReturnCode != commandLineNotPerformed)
@ -149,9 +153,8 @@ bool ProjucerApplication::initialiseLogger (const char* filePrefix)
void ProjucerApplication::handleAsyncUpdate()
{
licenseController = new LicenseController;
licenseController->addLicenseStatusChangedCallback (this);
licenseStateChanged (licenseController->getState());
if (licenseController != nullptr)
licenseController->startWebviewIfNeeded();
#if JUCE_MAC
PopupMenu extraAppleMenuItems;
@ -175,6 +178,9 @@ void ProjucerApplication::initialiseWindows (const String& commandLine)
mainWindowList.reopenLastProjects();
mainWindowList.createWindowIfNoneAreOpen();
if (licenseController->getState().applicationUsageDataState == LicenseState::ApplicationUsageData::notChosenYet)
showApplicationUsageDataAgreementPopup();
}
void ProjucerApplication::shutdown()
@ -255,8 +261,12 @@ void ProjucerApplication::systemRequestedQuit()
//==============================================================================
void ProjucerApplication::licenseStateChanged (const LicenseState& state)
{
#if ! JUCER_ENABLE_GPL_MODE
if (state.type != LicenseState::Type::notLoggedIn
&& state.type != LicenseState::Type::noLicenseChosenYet)
#else
ignoreUnused (state);
#endif
{
initialiseWindows (getCommandLineParameters());
}
@ -370,6 +380,7 @@ void ProjucerApplication::createFileMenu (PopupMenu& menu)
#if ! JUCE_MAC
menu.addCommandItem (commandManager, CommandIDs::showAboutWindow);
menu.addCommandItem (commandManager, CommandIDs::showAppUsageWindow);
menu.addCommandItem (commandManager, CommandIDs::showGlobalPreferences);
menu.addSeparator();
menu.addCommandItem (commandManager, StandardApplicationCommandIDs::quit);
@ -467,6 +478,7 @@ void ProjucerApplication::createToolsMenu (PopupMenu& menu)
void ProjucerApplication::createExtraAppleMenuItems (PopupMenu& menu)
{
menu.addCommandItem (commandManager, CommandIDs::showAboutWindow);
menu.addCommandItem (commandManager, CommandIDs::showAppUsageWindow);
menu.addSeparator();
menu.addCommandItem (commandManager, CommandIDs::showGlobalPreferences);
}
@ -508,10 +520,11 @@ void ProjucerApplication::handleMainMenuCommand (int menuItemID)
lookAndFeel.setupColours();
mainWindowList.sendLookAndFeelChange();
if (utf8Window != nullptr) utf8Window->sendLookAndFeelChange();
if (svgPathWindow != nullptr) svgPathWindow->sendLookAndFeelChange();
if (globalPreferencesWindow != nullptr) globalPreferencesWindow->sendLookAndFeelChange();
if (aboutWindow != nullptr) aboutWindow->sendLookAndFeelChange();
if (utf8Window != nullptr) utf8Window->sendLookAndFeelChange();
if (svgPathWindow != nullptr) svgPathWindow->sendLookAndFeelChange();
if (globalPreferencesWindow != nullptr) globalPreferencesWindow->sendLookAndFeelChange();
if (aboutWindow != nullptr) aboutWindow->sendLookAndFeelChange();
if (applicationUsageDataWindow != nullptr) applicationUsageDataWindow->sendLookAndFeelChange();
}
else
{
@ -532,6 +545,7 @@ void ProjucerApplication::getAllCommands (Array <CommandID>& commands)
CommandIDs::showUTF8Tool,
CommandIDs::showSVGPathTool,
CommandIDs::showAboutWindow,
CommandIDs::showAppUsageWindow,
CommandIDs::loginLogout };
commands.addArray (ids, numElementsInArray (ids));
@ -578,6 +592,10 @@ void ProjucerApplication::getCommandInfo (CommandID commandID, ApplicationComman
result.setInfo ("About Projucer", "Shows the Projucer's 'About' page.", CommandCategories::general, 0);
break;
case CommandIDs::showAppUsageWindow:
result.setInfo ("Application Usage Data", "Shows the application usage data agreement window", CommandCategories::general, 0);
break;
case CommandIDs::loginLogout:
{
bool isLoggedIn = false;
@ -615,6 +633,7 @@ bool ProjucerApplication::perform (const InvocationInfo& info)
case CommandIDs::showSVGPathTool: showSVGPathDataToolWindow(); break;
case CommandIDs::showGlobalPreferences: AppearanceSettings::showGlobalPreferences (globalPreferencesWindow); break;
case CommandIDs::showAboutWindow: showAboutWindow(); break;
case CommandIDs::showAppUsageWindow: showApplicationUsageDataAgreementPopup(); break;
case CommandIDs::loginLogout: doLogout(); break;
default: return JUCEApplication::perform (info);
}
@ -686,12 +705,28 @@ void ProjucerApplication::showAboutWindow()
if (aboutWindow != nullptr)
aboutWindow->toFront (true);
else
new FloatingToolWindow ("",
"aboutWindowPos",
new AboutWindowComponent(), aboutWindow, false,
new FloatingToolWindow ({}, {}, new AboutWindowComponent(),
aboutWindow, false,
500, 300, 500, 300, 500, 300);
}
void ProjucerApplication::showApplicationUsageDataAgreementPopup()
{
if (applicationUsageDataWindow != nullptr)
applicationUsageDataWindow->toFront (true);
else
new FloatingToolWindow ("Application Usage Analytics",
{}, new ApplicationUsageDataWindowComponent (isPaidOrGPL()),
applicationUsageDataWindow, false,
400, 300, 400, 300, 400, 300);
}
void ProjucerApplication::dismissApplicationUsageDataAgreementPopup()
{
if (applicationUsageDataWindow != nullptr)
applicationUsageDataWindow = nullptr;
}
//==============================================================================
struct FileWithTime
{

View file

@ -99,8 +99,8 @@ public:
void showSVGPathDataToolWindow();
void showAboutWindow();
void showLoginWindow();
void showApplicationUsageDataAgreementPopup();
void dismissApplicationUsageDataAgreementPopup();
void updateAllBuildTabs();
LatestVersionChecker* createVersionChecker() const;
@ -124,7 +124,9 @@ public:
OpenDocumentManager openDocumentManager;
ScopedPointer<ApplicationCommandManager> commandManager;
ScopedPointer<Component> appearanceEditorWindow, globalPreferencesWindow, utf8Window, svgPathWindow, aboutWindow;
ScopedPointer<Component> appearanceEditorWindow, globalPreferencesWindow, utf8Window,
svgPathWindow, aboutWindow, applicationUsageDataWindow;
ScopedPointer<FileLogger> logger;
bool isRunningCommandLine;

View file

@ -49,6 +49,7 @@ namespace CommandIDs
showTranslationTool = 0x300022,
showSVGPathTool = 0x300023,
showAboutWindow = 0x300024,
showAppUsageWindow = 0x300025,
showProjectSettings = 0x300030,
showProjectTab = 0x300031,

View file

@ -34,6 +34,7 @@
#include "../Utility/jucer_UTF8Component.h"
#include "../Utility/jucer_SVGPathDataComponent.h"
#include "../Utility/jucer_AboutWindowComponent.h"
#include "../Utility/jucer_ApplicationUsageDataWindowComponent.h"
#include "../Utility/jucer_FloatingToolWindow.h"
#include "../LiveBuildEngine/projucer_MessageIDs.h"

View file

@ -71,6 +71,23 @@ static LicenseState::Type getLicenseTypeFromValue (const String& d)
return LicenseState::Type::noLicenseChosenYet;
}
static const char* getApplicationUsageDataStateValue (LicenseState::ApplicationUsageData type)
{
switch (type)
{
case LicenseState::ApplicationUsageData::enabled: return "enabled";
case LicenseState::ApplicationUsageData::disabled: return "disabled";
default: return "notChosen";
}
}
static LicenseState::ApplicationUsageData getApplicationUsageDataTypeFromValue (const String& value)
{
if (value == getApplicationUsageDataStateValue (LicenseState::ApplicationUsageData::enabled)) return LicenseState::ApplicationUsageData::enabled;
if (value == getApplicationUsageDataStateValue (LicenseState::ApplicationUsageData::disabled)) return LicenseState::ApplicationUsageData::disabled;
return LicenseState::ApplicationUsageData::notChosenYet;
}
//==============================================================================
struct LicenseController::ModalCompletionCallback : ModalComponentManager::Callback
{
@ -83,15 +100,11 @@ struct LicenseController::ModalCompletionCallback : ModalComponentManager::Callb
//==============================================================================
LicenseController::LicenseController()
#if (! JUCER_ENABLE_GPL_MODE)
: state (licenseStateFromSettings (ProjucerApplication::getApp().settings->getGlobalProperties()))
#endif
{
#if JUCER_ENABLE_GPL_MODE
state.type = LicenseState::Type::GPL;
state.username = "GPL mode";
#else
thread = new LicenseThread (*this, false);
#endif
}
@ -101,6 +114,37 @@ LicenseController::~LicenseController()
closeWebview (-1);
}
LicenseState LicenseController::getState() const noexcept
{
LicenseState projucerState = state;
// if the user has never logged in before and the user is running from command line
// then we have no way to ask the user to log in, so fallback to GPL mode
if (guiNotInitialisedYet
&& (state.type == LicenseState::Type::notLoggedIn
|| state.type == LicenseState::Type::noLicenseChosenYet))
{
projucerState.type = LicenseState::Type::GPL;
projucerState.username = "GPL mode";
}
return projucerState;
}
void LicenseController::startWebviewIfNeeded()
{
if (guiNotInitialisedYet)
{
guiNotInitialisedYet = false;
listeners.call (&StateChangedCallback::licenseStateChanged, getState());
}
#if ! JUCER_ENABLE_GPL_MODE
if (thread == nullptr)
thread = new LicenseThread (*this, false);
#endif
}
void LicenseController::logout()
{
jassert (MessageManager::getInstance()->isThisTheMessageThread());
@ -127,6 +171,15 @@ void LicenseController::chooseNewLicense()
#endif
}
void LicenseController::setApplicationUsageDataState (LicenseState::ApplicationUsageData newState)
{
if (state.applicationUsageDataState != newState)
{
state.applicationUsageDataState = newState;
updateState (state);
}
}
//==============================================================================
void LicenseController::closeWebview (int result)
{
@ -198,7 +251,7 @@ void LicenseController::updateState (const LicenseState& newState)
state = newState;
licenseStateToSettings (state, props);
listeners.call (&StateChangedCallback::licenseStateChanged, state);
listeners.call (&StateChangedCallback::licenseStateChanged, getState());
}
LicenseState LicenseController::licenseStateFromSettings (PropertiesFile& props)
@ -208,10 +261,11 @@ LicenseState LicenseController::licenseStateFromSettings (PropertiesFile& props)
if (licenseXml != nullptr)
{
LicenseState result;
result.type = getLicenseTypeFromValue (licenseXml->getChildElementAllSubText ("type", {}));
result.username = licenseXml->getChildElementAllSubText ("username", {});
result.email = licenseXml->getChildElementAllSubText ("email", {});
result.authToken = licenseXml->getChildElementAllSubText ("authToken", {});
result.type = getLicenseTypeFromValue (licenseXml->getChildElementAllSubText ("type", {}));
result.applicationUsageDataState = getApplicationUsageDataTypeFromValue (licenseXml->getChildElementAllSubText ("applicationUsageData", {}));
result.username = licenseXml->getChildElementAllSubText ("username", {});
result.email = licenseXml->getChildElementAllSubText ("email", {});
result.authToken = licenseXml->getChildElementAllSubText ("authToken", {});
MemoryOutputStream imageData;
Base64::convertFromBase64 (imageData, licenseXml->getChildElementAllSubText ("avatar", {}));
@ -227,14 +281,15 @@ void LicenseController::licenseStateToSettings (const LicenseState& state, Prope
{
props.removeValue ("license");
if (state.type != LicenseState::Type::notLoggedIn
&& state.username.isNotEmpty() && state.authToken.isNotEmpty())
if (state.type != LicenseState::Type::notLoggedIn && state.username.isNotEmpty())
{
XmlElement licenseXml ("license");
if (auto* typeString = getLicenseStateValue (state.type))
licenseXml.createNewChildElement ("type")->addTextElement (typeString);
licenseXml.createNewChildElement ("applicationUsageData")->addTextElement (getApplicationUsageDataStateValue (state.applicationUsageDataState));
licenseXml.createNewChildElement ("username")->addTextElement (state.username);
licenseXml.createNewChildElement ("email") ->addTextElement (state.email);

View file

@ -44,7 +44,16 @@ struct LicenseState
pro
};
enum class ApplicationUsageData
{
notChosenYet,
enabled,
disabled
};
Type type = Type::notLoggedIn;
ApplicationUsageData applicationUsageDataState = ApplicationUsageData::notChosenYet;
String username;
String email;
String authToken;
@ -71,10 +80,13 @@ public:
LicenseController();
~LicenseController();
void startWebviewIfNeeded();
//==============================================================================
const LicenseState& getState() const noexcept { return state; }
LicenseState getState() const noexcept;
void logout();
void chooseNewLicense();
void setApplicationUsageDataState (LicenseState::ApplicationUsageData newState);
//==============================================================================
void addLicenseStatusChangedCallback (StateChangedCallback* callback) { listeners.add (callback); }
@ -103,6 +115,7 @@ private:
ScopedPointer<LicenseThread> thread;
LicenseWebview* licenseWebview = nullptr;
ListenerList<LicenseController::StateChangedCallback> listeners;
bool guiNotInitialisedYet = true;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LicenseController)
};

View file

@ -374,7 +374,6 @@ private:
<< "// [END_USER_CODE_SECTION]" << newLine;
out << newLine
<< "//==============================================================================" << newLine
<< "/*" << newLine
<< " ==============================================================================" << newLine
<< newLine

View file

@ -491,7 +491,7 @@ private:
{
if (LicenseController* controller = ProjucerApplication::getApp().licenseController)
{
auto& state = controller->getState();
auto state = controller->getState();
userSettingsButton->iconImage = state.avatar;
userSettingsButton->repaint();

View file

@ -113,13 +113,19 @@ void Project::setMissingDefaultValues()
setTitle ("JUCE Project");
{
auto defaultSplashScreenAndReporting = ! ProjucerApplication::getApp().isPaidOrGPL();
auto defaultSplashScreen = ! ProjucerApplication::getApp().isPaidOrGPL();
if (shouldDisplaySplashScreen() == var() || defaultSplashScreenAndReporting)
shouldDisplaySplashScreen() = defaultSplashScreenAndReporting;
if (shouldDisplaySplashScreen() == var() || defaultSplashScreen)
shouldDisplaySplashScreen() = defaultSplashScreen;
if (shouldReportAppUsage() == var() || defaultSplashScreenAndReporting)
shouldReportAppUsage() = defaultSplashScreenAndReporting;
if (ProjucerApplication::getApp().isPaidOrGPL())
{
if (shouldReportAppUsage() == var())
shouldReportAppUsage() = ProjucerApplication::getApp().licenseController->getState().applicationUsageDataState
== LicenseState::ApplicationUsageData::enabled;
}
else
shouldReportAppUsage() = true;
}
if (splashScreenColour() == var())

View file

@ -0,0 +1,167 @@
/*
==============================================================================
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.
==============================================================================
*/
#pragma once
class ApplicationUsageDataWindowComponent : public Component,
private Button::Listener
{
public:
ApplicationUsageDataWindowComponent (bool showCheckbox)
{
addAndMakeVisible (headerLabel);
headerLabel.setText ("Application Usage Analytics", dontSendNotification);
headerLabel.setFont (Font (20.0f, Font::FontStyleFlags::bold));
headerLabel.setJustificationType (Justification::centred);
auto textToShow = String ("We use analytics services to understand how developers use our software in order for JUCE to improve its software and services. ");
if (! showCheckbox)
textToShow += String (" Analytics can be disabled with an Indie or Pro license. ");
textToShow += String ("For more information, please read the JUCE EULA and Privacy policy:");
addAndMakeVisible (bodyLabel);
bodyLabel.setText (textToShow, dontSendNotification);
bodyLabel.setFont (Font (14.0f));
bodyLabel.setJustificationType (Justification::centredLeft);
addAndMakeVisible (juceEULALink);
juceEULALink.setButtonText ("JUCE EULA");
juceEULALink.setFont (Font (14.0f), false);
juceEULALink.setURL (URL ("https://juce.com/juce-5-license"));
addAndMakeVisible (privacyPolicyLink);
privacyPolicyLink.setButtonText ("Privacy Policy");
privacyPolicyLink.setFont (Font (14.0f), false);
privacyPolicyLink.setURL (URL ("https://juce.com/privacy-policy"));
addAndMakeVisible (okButton);
okButton.setButtonText ("OK");
okButton.addListener (this);
if (showCheckbox)
{
addAndMakeVisible (shareApplicationUsageDataToggle = new ToggleButton());
shareApplicationUsageDataToggle->setToggleState (true, dontSendNotification);
addAndMakeVisible(shareApplicationUsageDataLabel = new Label ({}, "Help JUCE to improve its software and services by sharing my application usage data"));
shareApplicationUsageDataLabel->setFont (Font (14.0f));
shareApplicationUsageDataLabel->setMinimumHorizontalScale (1.0f);
}
else
{
addAndMakeVisible (upgradeLicenseButton = new TextButton ("Upgrade License"));
upgradeLicenseButton->addListener (this);
upgradeLicenseButton->setColour (TextButton::buttonColourId, findColour (secondaryButtonBackgroundColourId));
}
}
~ApplicationUsageDataWindowComponent()
{
if (LicenseController* controller = ProjucerApplication::getApp().licenseController)
{
auto newApplicationUsageDataState = LicenseState::ApplicationUsageData::enabled;
if (shareApplicationUsageDataToggle != nullptr && ! shareApplicationUsageDataToggle->getToggleState())
newApplicationUsageDataState = LicenseState::ApplicationUsageData::disabled;
controller->setApplicationUsageDataState (newApplicationUsageDataState);
}
}
void resized() override
{
auto bounds = getLocalBounds().reduced (20);
headerLabel.setBounds (bounds.removeFromTop (40));
bodyLabel.setBounds (bounds.removeFromTop (75));
bounds.removeFromTop (10);
auto linkBounds = bounds.removeFromTop (20);
juceEULALink.setBounds (linkBounds.removeFromLeft (linkBounds.getWidth() / 2).reduced (2));
privacyPolicyLink.setBounds (linkBounds.reduced (2));
if (shareApplicationUsageDataToggle != nullptr)
{
bounds.removeFromTop (10);
auto toggleBounds = bounds.removeFromTop (40);
shareApplicationUsageDataToggle->setBounds (toggleBounds.removeFromLeft (40).reduced (5));
shareApplicationUsageDataLabel->setBounds (toggleBounds);
}
bounds.removeFromTop (10);
auto buttonW = 125;
auto buttonH = 40;
if (upgradeLicenseButton != nullptr)
{
auto left = bounds.removeFromLeft (bounds.getWidth() / 2);
upgradeLicenseButton->setSize (buttonW, buttonH);
upgradeLicenseButton->setCentrePosition (left.getCentreX(), left.getCentreY());
}
okButton.setSize (buttonW, buttonH);
okButton.setCentrePosition (bounds.getCentreX(), bounds.getCentreY());
}
void paint (Graphics& g) override
{
g.fillAll (findColour (backgroundColourId));
}
private:
Label headerLabel, bodyLabel;
HyperlinkButton juceEULALink, privacyPolicyLink;
ScopedPointer<Label> shareApplicationUsageDataLabel;
ScopedPointer<ToggleButton> shareApplicationUsageDataToggle;
TextButton okButton;
ScopedPointer<TextButton> upgradeLicenseButton;
void buttonClicked (Button* b) override
{
if (b == &okButton)
{
ProjucerApplication::getApp().dismissApplicationUsageDataAgreementPopup();
}
else if (b == upgradeLicenseButton)
{
if (LicenseController* controller = ProjucerApplication::getApp().licenseController)
controller->chooseNewLicense();
}
}
void lookAndFeelChanged() override
{
if (upgradeLicenseButton != nullptr)
upgradeLicenseButton->setColour (TextButton::buttonColourId, findColour (secondaryButtonBackgroundColourId));
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ApplicationUsageDataWindowComponent)
};

View file

@ -47,7 +47,9 @@ struct FloatingToolWindow : public DialogWindow
setResizeLimits (minW, minH, maxW, maxH);
setContentOwned (content, false);
const String windowState (getGlobalProperties().getValue (windowPosProperty));
String windowState;
if (windowPosProperty.isNotEmpty())
windowState = getGlobalProperties().getValue (windowPosProperty);
if (windowState.isNotEmpty())
restoreWindowStateFromString (windowState);
@ -60,7 +62,8 @@ struct FloatingToolWindow : public DialogWindow
~FloatingToolWindow()
{
getGlobalProperties().setValue (windowPosProperty, getWindowStateAsString());
if (windowPosProperty.isNotEmpty())
getGlobalProperties().setValue (windowPosProperty, getWindowStateAsString());
}
void closeButtonPressed() override