mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-10 23:44:24 +00:00
Projucer: Updated licence code to JUCE 6
This commit is contained in:
parent
aa200ffeac
commit
d7f44a9cf5
11 changed files with 349 additions and 194 deletions
|
|
@ -59,7 +59,7 @@ static void doBasicProjectSetup (Project& project, const NewProjectTemplates::Pr
|
|||
project.getProjectValue (Ids::useAppConfig) = false;
|
||||
project.getProjectValue (Ids::addUsingNamespaceToJuceHeader) = false;
|
||||
|
||||
if (! ProjucerApplication::getApp().getLicenseController().getCurrentState().isPaidOrGPL())
|
||||
if (! ProjucerApplication::getApp().getLicenseController().getCurrentState().canUnlockFullFeatures())
|
||||
project.getProjectValue (Ids::displaySplashScreen) = true;
|
||||
|
||||
if (NewProjectTemplates::isPlugin (projectTemplate))
|
||||
|
|
|
|||
|
|
@ -19,14 +19,23 @@
|
|||
#pragma once
|
||||
|
||||
#include "jucer_LicenseState.h"
|
||||
#include "jucer_LicenseQueryThread.h"
|
||||
|
||||
//==============================================================================
|
||||
class LicenseController
|
||||
class LicenseController : private Timer
|
||||
{
|
||||
public:
|
||||
LicenseController() = default;
|
||||
LicenseController()
|
||||
{
|
||||
checkLicense();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
static LicenseState getGPLState()
|
||||
{
|
||||
return { LicenseState::Type::gpl, projucerMajorVersion, {}, {} };
|
||||
}
|
||||
|
||||
LicenseState getCurrentState() const noexcept
|
||||
{
|
||||
return state;
|
||||
|
|
@ -34,10 +43,13 @@ public:
|
|||
|
||||
void setState (const LicenseState& newState)
|
||||
{
|
||||
state = newState;
|
||||
licenseStateToSettings (state, getGlobalProperties());
|
||||
if (state != newState)
|
||||
{
|
||||
state = newState;
|
||||
licenseStateToSettings (state, getGlobalProperties());
|
||||
|
||||
stateListeners.call ([] (LicenseStateListener& l) { l.licenseStateChanged(); });
|
||||
stateListeners.call ([] (LicenseStateListener& l) { l.licenseStateChanged(); });
|
||||
}
|
||||
}
|
||||
|
||||
void resetState()
|
||||
|
|
@ -45,26 +57,21 @@ public:
|
|||
setState ({});
|
||||
}
|
||||
|
||||
static LicenseState getGPLState()
|
||||
void signIn (const String& email, const String& password,
|
||||
std::function<void (const String&)> completionCallback)
|
||||
{
|
||||
static auto logoImage = []() -> Image
|
||||
{
|
||||
if (auto logo = Drawable::createFromImageData (BinaryData::gpl_logo_svg, BinaryData::gpl_logo_svgSize))
|
||||
{
|
||||
auto bounds = logo->getDrawableBounds();
|
||||
licenseQueryThread.doSignIn (email, password,
|
||||
[this, completionCallback] (LicenseQueryThread::ErrorMessageAndType error,
|
||||
LicenseState newState)
|
||||
{
|
||||
completionCallback (error.first);
|
||||
setState (newState);
|
||||
});
|
||||
}
|
||||
|
||||
Image image (Image::ARGB, roundToInt (bounds.getWidth()), roundToInt (bounds.getHeight()), true);
|
||||
Graphics g (image);
|
||||
logo->draw (g, 1.0f);
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
jassertfalse;
|
||||
return {};
|
||||
}();
|
||||
|
||||
return { LicenseState::Type::gpl, {}, {}, logoImage };
|
||||
void cancelSignIn()
|
||||
{
|
||||
licenseQueryThread.cancelRunningJobs();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
|
|
@ -112,24 +119,6 @@ private:
|
|||
return LicenseState::Type::none;
|
||||
}
|
||||
|
||||
static Image avatarFromLicenseState (const String& licenseState)
|
||||
{
|
||||
MemoryOutputStream imageData;
|
||||
Base64::convertFromBase64 (imageData, licenseState);
|
||||
|
||||
return ImageFileFormat::loadFrom (imageData.getData(), imageData.getDataSize());
|
||||
}
|
||||
|
||||
static String avatarToLicenseState (Image avatarImage)
|
||||
{
|
||||
MemoryOutputStream imageData;
|
||||
|
||||
if (avatarImage.isValid() && PNGImageFormat().writeImageToStream (avatarImage, imageData))
|
||||
return Base64::toBase64 (imageData.getData(), imageData.getDataSize());
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
static LicenseState licenseStateFromSettings (PropertiesFile& props)
|
||||
{
|
||||
if (auto licenseXml = props.getXmlValue ("license"))
|
||||
|
|
@ -140,9 +129,9 @@ private:
|
|||
auto stateFromOldSettings = [&licenseXml]() -> LicenseState
|
||||
{
|
||||
return { getLicenseTypeFromValue (licenseXml->getChildElementAllSubText ("type", {})),
|
||||
licenseXml->getChildElementAllSubText ("authToken", {}),
|
||||
licenseXml->getChildElementAllSubText ("version", "-1").getIntValue(),
|
||||
licenseXml->getChildElementAllSubText ("username", {}),
|
||||
avatarFromLicenseState (licenseXml->getStringAttribute ("avatar", {})) };
|
||||
licenseXml->getChildElementAllSubText ("authToken", {}) };
|
||||
}();
|
||||
|
||||
licenseStateToSettings (stateFromOldSettings, props);
|
||||
|
|
@ -151,9 +140,9 @@ private:
|
|||
}
|
||||
|
||||
return { getLicenseTypeFromValue (licenseXml->getStringAttribute ("type", {})),
|
||||
licenseXml->getStringAttribute ("authToken", {}),
|
||||
licenseXml->getIntAttribute ("version", -1),
|
||||
licenseXml->getStringAttribute ("username", {}),
|
||||
avatarFromLicenseState (licenseXml->getStringAttribute ("avatar", {})) };
|
||||
licenseXml->getStringAttribute ("authToken", {}) };
|
||||
}
|
||||
|
||||
return {};
|
||||
|
|
@ -163,16 +152,16 @@ private:
|
|||
{
|
||||
props.removeValue ("license");
|
||||
|
||||
if (state.isValid())
|
||||
if (state.isSignedIn())
|
||||
{
|
||||
XmlElement licenseXml ("license");
|
||||
|
||||
if (auto* typeString = getLicenseStateValue (state.type))
|
||||
licenseXml.setAttribute ("type", typeString);
|
||||
|
||||
licenseXml.setAttribute ("authToken", state.authToken);
|
||||
licenseXml.setAttribute ("version", state.version);
|
||||
licenseXml.setAttribute ("username", state.username);
|
||||
licenseXml.setAttribute ("avatar", avatarToLicenseState (state.avatar));
|
||||
licenseXml.setAttribute ("authToken", state.authToken);
|
||||
|
||||
props.setValue ("license", &licenseXml);
|
||||
}
|
||||
|
|
@ -180,6 +169,38 @@ private:
|
|||
props.saveIfNeeded();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void checkLicense()
|
||||
{
|
||||
if (state.isSignedIn() && ! state.isGPL())
|
||||
{
|
||||
auto completionCallback = [this] (LicenseQueryThread::ErrorMessageAndType error,
|
||||
LicenseState updatedState)
|
||||
{
|
||||
if (error == LicenseQueryThread::ErrorMessageAndType())
|
||||
{
|
||||
setState (updatedState);
|
||||
}
|
||||
else if ((error.second == LicenseQueryThread::ErrorType::busy
|
||||
|| error.second == LicenseQueryThread::ErrorType::cancelled
|
||||
|| error.second == LicenseQueryThread::ErrorType::connectionError)
|
||||
&& ! hasRetriedLicenseCheck)
|
||||
{
|
||||
hasRetriedLicenseCheck = true;
|
||||
startTimer (10000);
|
||||
}
|
||||
};
|
||||
|
||||
licenseQueryThread.checkLicenseValidity (state, std::move (completionCallback));
|
||||
}
|
||||
}
|
||||
|
||||
void timerCallback() override
|
||||
{
|
||||
stopTimer();
|
||||
checkLicense();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
#if JUCER_ENABLE_GPL_MODE
|
||||
LicenseState state = getGPLState();
|
||||
|
|
@ -188,6 +209,8 @@ private:
|
|||
#endif
|
||||
|
||||
ListenerList<LicenseStateListener> stateListeners;
|
||||
LicenseQueryThread licenseQueryThread;
|
||||
bool hasRetriedLicenseCheck = false;
|
||||
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LicenseController)
|
||||
|
|
|
|||
|
|
@ -18,47 +18,147 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
//==============================================================================
|
||||
namespace LicenseHelpers
|
||||
{
|
||||
inline LicenseState::Type licenseTypeForString (const String& licenseString)
|
||||
{
|
||||
if (licenseString == "juce-pro") return LicenseState::Type::pro;
|
||||
if (licenseString == "juce-indie") return LicenseState::Type::indie;
|
||||
if (licenseString == "juce-edu") return LicenseState::Type::educational;
|
||||
if (licenseString == "juce-personal") return LicenseState::Type::personal;
|
||||
|
||||
jassertfalse; // unknown type
|
||||
return LicenseState::Type::none;
|
||||
}
|
||||
|
||||
using LicenseVersionAndType = std::pair<int, LicenseState::Type>;
|
||||
|
||||
inline LicenseVersionAndType findBestLicense (std::vector<LicenseVersionAndType>&& licenses)
|
||||
{
|
||||
if (licenses.size() == 1)
|
||||
return licenses[0];
|
||||
|
||||
auto getValueForLicenceType = [] (LicenseState::Type type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case LicenseState::Type::pro: return 4;
|
||||
case LicenseState::Type::indie: return 3;
|
||||
case LicenseState::Type::educational: return 2;
|
||||
case LicenseState::Type::personal: return 1;
|
||||
case LicenseState::Type::gpl:
|
||||
case LicenseState::Type::none:
|
||||
default: return -1;
|
||||
}
|
||||
};
|
||||
|
||||
std::sort (licenses.begin(), licenses.end(),
|
||||
[getValueForLicenceType] (const LicenseVersionAndType& l1, const LicenseVersionAndType& l2)
|
||||
{
|
||||
if (l1.first > l2.first)
|
||||
return true;
|
||||
|
||||
if (l1.first == l2.first)
|
||||
return getValueForLicenceType (l1.second) > getValueForLicenceType (l2.second);
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
auto findFirstLicense = [&licenses] (bool isPaid)
|
||||
{
|
||||
auto iter = std::find_if (licenses.begin(), licenses.end(),
|
||||
[isPaid] (const LicenseVersionAndType& l)
|
||||
{
|
||||
auto proOrIndie = (l.second == LicenseState::Type::pro || l.second == LicenseState::Type::indie);
|
||||
return isPaid ? proOrIndie : ! proOrIndie;
|
||||
});
|
||||
|
||||
return iter != licenses.end() ? *iter
|
||||
: LicenseVersionAndType();
|
||||
};
|
||||
|
||||
auto newestPaid = findFirstLicense (true);
|
||||
auto newestFree = findFirstLicense (false);
|
||||
|
||||
if (newestPaid.first >= projucerMajorVersion || newestPaid.first >= newestFree.first)
|
||||
return newestPaid;
|
||||
|
||||
return newestFree;
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
class LicenseQueryThread : public Thread
|
||||
class LicenseQueryThread
|
||||
{
|
||||
public:
|
||||
LicenseQueryThread (const String& userEmail, const String& userPassword,
|
||||
std::function<void (LicenseState, String)>&& cb)
|
||||
: Thread ("LicenseQueryThread"),
|
||||
email (userEmail),
|
||||
password (userPassword),
|
||||
completionCallback (std::move (cb))
|
||||
enum class ErrorType
|
||||
{
|
||||
startThread();
|
||||
}
|
||||
busy,
|
||||
cancelled,
|
||||
connectionError,
|
||||
webResponseError
|
||||
};
|
||||
|
||||
~LicenseQueryThread() override
|
||||
using ErrorMessageAndType = std::pair<String, ErrorType>;
|
||||
using LicenseQueryCallback = std::function<void (ErrorMessageAndType, LicenseState)>;
|
||||
|
||||
//==============================================================================
|
||||
LicenseQueryThread() = default;
|
||||
|
||||
void checkLicenseValidity (const LicenseState& state, LicenseQueryCallback completionCallback)
|
||||
{
|
||||
signalThreadShouldExit();
|
||||
waitForThreadToExit (6000);
|
||||
}
|
||||
|
||||
void run() override
|
||||
{
|
||||
LicenseState state;
|
||||
|
||||
auto errorMessage = runJob (std::make_unique<UserLogin> (email, password), state);
|
||||
|
||||
if (errorMessage.isEmpty())
|
||||
errorMessage = runJob (std::make_unique<UserLicenseQuery> (state.authToken), state);
|
||||
|
||||
if (errorMessage.isNotEmpty())
|
||||
state = {};
|
||||
|
||||
WeakReference<LicenseQueryThread> weakThis (this);
|
||||
MessageManager::callAsync ([this, weakThis, state, errorMessage]
|
||||
if (jobPool.getNumJobs() > 0)
|
||||
{
|
||||
if (weakThis != nullptr)
|
||||
completionCallback (state, errorMessage);
|
||||
completionCallback ({ {}, ErrorType::busy }, {});
|
||||
return;
|
||||
}
|
||||
|
||||
jobPool.addJob ([this, state, completionCallback]
|
||||
{
|
||||
auto updatedState = state;
|
||||
|
||||
auto result = runTask (std::make_unique<UserLicenseQuery> (state.authToken), updatedState);
|
||||
|
||||
WeakReference<LicenseQueryThread> weakThis (this);
|
||||
MessageManager::callAsync ([weakThis, result, updatedState, completionCallback]
|
||||
{
|
||||
if (weakThis != nullptr)
|
||||
completionCallback (result, updatedState);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void doSignIn (const String& email, const String& password, LicenseQueryCallback completionCallback)
|
||||
{
|
||||
cancelRunningJobs();
|
||||
|
||||
jobPool.addJob ([this, email, password, completionCallback]
|
||||
{
|
||||
LicenseState state;
|
||||
|
||||
auto result = runTask (std::make_unique<UserLogin> (email, password), state);
|
||||
|
||||
if (result == ErrorMessageAndType())
|
||||
result = runTask (std::make_unique<UserLicenseQuery> (state.authToken), state);
|
||||
|
||||
if (result != ErrorMessageAndType())
|
||||
state = {};
|
||||
|
||||
WeakReference<LicenseQueryThread> weakThis (this);
|
||||
MessageManager::callAsync ([weakThis, result, state, completionCallback]
|
||||
{
|
||||
if (weakThis != nullptr)
|
||||
completionCallback (result, state);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void cancelRunningJobs()
|
||||
{
|
||||
jobPool.removeAllJobs (true, 500);
|
||||
}
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
struct AccountEnquiryBase
|
||||
|
|
@ -115,22 +215,7 @@ private:
|
|||
auto json = JSON::parse (serverResponse);
|
||||
|
||||
licenseState.authToken = json.getProperty ("token", {}).toString();
|
||||
licenseState.username = json.getProperty ("user", {}).getProperty ("username", {}).toString();
|
||||
|
||||
auto avatarURL = json.getProperty ("user", {}).getProperty ("avatar_url", {}).toString();
|
||||
|
||||
if (avatarURL.isNotEmpty())
|
||||
{
|
||||
URL url (avatarURL);
|
||||
|
||||
if (auto stream = url.createInputStream (false, nullptr, nullptr, {}, 5000))
|
||||
{
|
||||
MemoryBlock mb;
|
||||
stream->readIntoMemoryBlock (mb);
|
||||
|
||||
licenseState.avatar = ImageFileFormat::loadFrom (mb.getData(), mb.getSize());
|
||||
}
|
||||
}
|
||||
licenseState.username = json.getProperty ("user", {}).getProperty ("username", {}).toString();
|
||||
|
||||
return (licenseState.authToken.isNotEmpty() && licenseState.username.isNotEmpty());
|
||||
}
|
||||
|
|
@ -174,30 +259,27 @@ private:
|
|||
|
||||
if (auto* licensesJson = json.getArray())
|
||||
{
|
||||
StringArray licenseTypes;
|
||||
std::vector<LicenseHelpers::LicenseVersionAndType> licenses;
|
||||
|
||||
for (auto& license : *licensesJson)
|
||||
{
|
||||
auto status = license.getProperty ("status", {}).toString();
|
||||
auto version = license.getProperty ("product_version", {}).toString().trim();
|
||||
auto type = license.getProperty ("licence_type", {}).toString();
|
||||
auto status = license.getProperty ("status", {}).toString();
|
||||
|
||||
if (status == "active")
|
||||
licenseTypes.add (license.getProperty ("licence_type", {}).toString());
|
||||
if (status == "active" && type.isNotEmpty() && version.isNotEmpty())
|
||||
licenses.push_back ({ version.getIntValue(), LicenseHelpers::licenseTypeForString (type) });
|
||||
}
|
||||
|
||||
licenseTypes.removeEmptyStrings();
|
||||
licenseTypes.removeDuplicates (false);
|
||||
|
||||
licenseState.type = [licenseTypes]()
|
||||
if (! licenses.empty())
|
||||
{
|
||||
if (licenseTypes.contains ("juce-pro")) return LicenseState::Type::pro;
|
||||
else if (licenseTypes.contains ("juce-indie")) return LicenseState::Type::indie;
|
||||
else if (licenseTypes.contains ("juce-personal")) return LicenseState::Type::personal;
|
||||
else if (licenseTypes.contains ("juce-edu")) return LicenseState::Type::educational;
|
||||
auto bestLicense = LicenseHelpers::findBestLicense (std::move (licenses));
|
||||
|
||||
return LicenseState::Type::none;
|
||||
}();
|
||||
licenseState.version = bestLicense.first;
|
||||
licenseState.type = bestLicense.second;
|
||||
}
|
||||
|
||||
return (licenseState.type != LicenseState::Type::none);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
|
@ -217,33 +299,34 @@ private:
|
|||
return JSON::toString (var (d.get()));
|
||||
}
|
||||
|
||||
String runJob (std::unique_ptr<AccountEnquiryBase> accountEnquiryJob, LicenseState& state)
|
||||
static ErrorMessageAndType runTask (std::unique_ptr<AccountEnquiryBase> accountEnquiryTask, LicenseState& state)
|
||||
{
|
||||
const ErrorMessageAndType cancelledError ("Cancelled.", ErrorType::cancelled);
|
||||
const String endpointURL = "https://api.juce.com/api/v1";
|
||||
|
||||
auto url = URL (endpointURL + accountEnquiryJob->getEndpointURLSuffix());
|
||||
auto url = URL (endpointURL + accountEnquiryTask->getEndpointURLSuffix());
|
||||
|
||||
auto isPOST = accountEnquiryJob->isPOSTLikeRequest();
|
||||
auto isPOST = accountEnquiryTask->isPOSTLikeRequest();
|
||||
|
||||
if (isPOST)
|
||||
url = url.withPOSTData (postDataStringAsJSON (accountEnquiryJob->getParameterNamesAndValues()));
|
||||
url = url.withPOSTData (postDataStringAsJSON (accountEnquiryTask->getParameterNamesAndValues()));
|
||||
|
||||
if (threadShouldExit())
|
||||
return "Cancelled.";
|
||||
if (ThreadPoolJob::getCurrentThreadPoolJob()->shouldExit())
|
||||
return cancelledError;
|
||||
|
||||
int statusCode = 0;
|
||||
auto urlStream = url.createInputStream (isPOST, nullptr, nullptr,
|
||||
accountEnquiryJob->getExtraHeaders(),
|
||||
accountEnquiryTask->getExtraHeaders(),
|
||||
5000, nullptr, &statusCode);
|
||||
|
||||
if (urlStream == nullptr)
|
||||
return "Failed to connect to the web server.";
|
||||
return { "Failed to connect to the web server.", ErrorType::connectionError };
|
||||
|
||||
if (statusCode != accountEnquiryJob->getSuccessCode())
|
||||
return accountEnquiryJob->errorCodeToString (statusCode);
|
||||
if (statusCode != accountEnquiryTask->getSuccessCode())
|
||||
return { accountEnquiryTask->errorCodeToString (statusCode), ErrorType::webResponseError };
|
||||
|
||||
if (threadShouldExit())
|
||||
return "Cancelled.";
|
||||
if (ThreadPoolJob::getCurrentThreadPoolJob()->shouldExit())
|
||||
return cancelledError;
|
||||
|
||||
String response;
|
||||
|
||||
|
|
@ -252,8 +335,8 @@ private:
|
|||
char buffer [8192];
|
||||
auto num = urlStream->read (buffer, sizeof (buffer));
|
||||
|
||||
if (threadShouldExit())
|
||||
return "Cancelled.";
|
||||
if (ThreadPoolJob::getCurrentThreadPoolJob()->shouldExit())
|
||||
return cancelledError;
|
||||
|
||||
if (num <= 0)
|
||||
break;
|
||||
|
|
@ -261,18 +344,17 @@ private:
|
|||
response += buffer;
|
||||
}
|
||||
|
||||
if (threadShouldExit())
|
||||
return "Cancelled.";
|
||||
if (ThreadPoolJob::getCurrentThreadPoolJob()->shouldExit())
|
||||
return cancelledError;
|
||||
|
||||
if (! accountEnquiryJob->parseServerResponse (response, state))
|
||||
return "Failed to parse server response.";
|
||||
if (! accountEnquiryTask->parseServerResponse (response, state))
|
||||
return { "Failed to parse server response.", ErrorType::webResponseError };
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
const String email, password;
|
||||
const std::function<void (LicenseState, String)> completionCallback;
|
||||
ThreadPool jobPool { 1 };
|
||||
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_WEAK_REFERENCEABLE (LicenseQueryThread)
|
||||
|
|
|
|||
|
|
@ -34,16 +34,32 @@ struct LicenseState
|
|||
|
||||
LicenseState() = default;
|
||||
|
||||
LicenseState (Type t, String token, String user, Image avatarImage)
|
||||
: type (t), authToken (token), username (user), avatar (avatarImage)
|
||||
LicenseState (Type t, int v, String user, String token)
|
||||
: type (t), version (v), username (user), authToken (token)
|
||||
{
|
||||
}
|
||||
|
||||
bool isValid() const noexcept { return isGPL() || (type != Type::none && authToken.isNotEmpty() && username.isNotEmpty()); }
|
||||
bool operator== (const LicenseState& other) const noexcept
|
||||
{
|
||||
return type == other.type
|
||||
&& version == other.version
|
||||
&& username == other.username
|
||||
&& authToken == other.authToken;
|
||||
}
|
||||
|
||||
bool isPaid() const noexcept { return type == Type::indie || type == Type::pro; }
|
||||
bool isGPL() const noexcept { return type == Type::gpl; }
|
||||
bool isPaidOrGPL() const noexcept { return isPaid() || isGPL(); }
|
||||
bool operator != (const LicenseState& other) const noexcept
|
||||
{
|
||||
return ! operator== (other);
|
||||
}
|
||||
|
||||
bool isSignedIn() const noexcept { return isGPL() || (version > 0 && username.isNotEmpty()); }
|
||||
bool isOldLicense() const noexcept { return isSignedIn() && version < projucerMajorVersion; }
|
||||
bool isGPL() const noexcept { return type == Type::gpl; }
|
||||
|
||||
bool canUnlockFullFeatures() const noexcept
|
||||
{
|
||||
return isGPL() || (isSignedIn() && ! isOldLicense() && (type == Type::indie || type == Type::pro));
|
||||
}
|
||||
|
||||
String getLicenseTypeString() const
|
||||
{
|
||||
|
|
@ -63,6 +79,6 @@ struct LicenseState
|
|||
}
|
||||
|
||||
Type type = Type::none;
|
||||
String authToken, username;
|
||||
Image avatar;
|
||||
int version = -1;
|
||||
String username, authToken;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "jucer_LicenseQueryThread.h"
|
||||
|
||||
#include "../../Project/UI/jucer_UserAvatarComponent.h"
|
||||
|
||||
//==============================================================================
|
||||
|
|
@ -74,6 +74,11 @@ public:
|
|||
setSize (300, 350);
|
||||
}
|
||||
|
||||
~LoginFormComponent() override
|
||||
{
|
||||
ProjucerApplication::getApp().getLicenseController().cancelSignIn();
|
||||
}
|
||||
|
||||
void resized() override
|
||||
{
|
||||
auto bounds = getLocalBounds().reduced (20);
|
||||
|
|
@ -185,9 +190,6 @@ private:
|
|||
|
||||
void submitDetails()
|
||||
{
|
||||
if ((licenseQueryThread != nullptr && licenseQueryThread->isThreadRunning()))
|
||||
return;
|
||||
|
||||
auto loginFormError = checkLoginFormsAreValid();
|
||||
|
||||
if (loginFormError.isNotEmpty())
|
||||
|
|
@ -199,29 +201,27 @@ private:
|
|||
updateLoginButtonStates (true);
|
||||
|
||||
WeakReference<Component> weakThis (this);
|
||||
licenseQueryThread.reset (new LicenseQueryThread (emailBox.getText(), passwordBox.getText(),
|
||||
[this, weakThis] (LicenseState newState, String errorMessage)
|
||||
{
|
||||
if (weakThis == nullptr)
|
||||
return;
|
||||
auto completionCallback = [this, weakThis] (const String& errorMessage)
|
||||
{
|
||||
if (weakThis == nullptr)
|
||||
return;
|
||||
|
||||
updateLoginButtonStates (false);
|
||||
updateLoginButtonStates (false);
|
||||
|
||||
if (errorMessage.isNotEmpty())
|
||||
{
|
||||
showErrorMessage (errorMessage);
|
||||
}
|
||||
else
|
||||
{
|
||||
hideErrorMessage();
|
||||
if (errorMessage.isNotEmpty())
|
||||
{
|
||||
showErrorMessage (errorMessage);
|
||||
}
|
||||
else
|
||||
{
|
||||
hideErrorMessage();
|
||||
mainWindow.hideLoginFormOverlay();
|
||||
ProjucerApplication::getApp().getCommandManager().commandStatusChanged();
|
||||
}
|
||||
};
|
||||
|
||||
auto& licenseController = ProjucerApplication::getApp().getLicenseController();
|
||||
licenseController.setState (newState);
|
||||
mainWindow.hideLoginFormOverlay();
|
||||
|
||||
ProjucerApplication::getApp().getCommandManager().commandStatusChanged();
|
||||
}
|
||||
}));
|
||||
ProjucerApplication::getApp().getLicenseController().signIn (emailBox.getText(), passwordBox.getText(),
|
||||
std::move (completionCallback));
|
||||
}
|
||||
|
||||
String checkLoginFormsAreValid() const
|
||||
|
|
@ -263,11 +263,9 @@ private:
|
|||
findColour (treeIconColourId),
|
||||
findColour (treeIconColourId).overlaidWith (findColour (defaultHighlightedTextColourId).withAlpha (0.2f)),
|
||||
findColour (treeIconColourId).overlaidWith (findColour (defaultHighlightedTextColourId).withAlpha (0.4f)) };
|
||||
UserAvatarComponent userAvatar { false, false };
|
||||
UserAvatarComponent userAvatar { false };
|
||||
Label createAccountLabel { {}, "Create an account" },
|
||||
errorMessageLabel { {}, {} };
|
||||
|
||||
std::unique_ptr<LicenseQueryThread> licenseQueryThread;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LoginFormComponent)
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1087,7 +1087,7 @@ void ProjucerApplication::getCommandInfo (CommandID commandID, ApplicationComman
|
|||
if (licenseState.isGPL())
|
||||
result.setInfo ("Disable GPL mode", "Disables GPL mode", CommandCategories::general, 0);
|
||||
else
|
||||
result.setInfo (licenseState.isValid() ? String ("Sign out ") + licenseState.username + "..." : String ("Sign in..."),
|
||||
result.setInfo (licenseState.isSignedIn() ? String ("Sign out ") + licenseState.username + "..." : String ("Sign in..."),
|
||||
"Sign out of your JUCE account",
|
||||
CommandCategories::general, 0);
|
||||
break;
|
||||
|
|
@ -1349,7 +1349,11 @@ void ProjucerApplication::launchTutorialsBrowser()
|
|||
|
||||
void ProjucerApplication::doLoginOrLogout()
|
||||
{
|
||||
if (licenseController->getCurrentState().type == LicenseState::Type::none)
|
||||
if (licenseController->getCurrentState().isSignedIn())
|
||||
{
|
||||
licenseController->resetState();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (auto* window = mainWindowList.getMainWindowWithLoginFormOpen())
|
||||
{
|
||||
|
|
@ -1361,10 +1365,6 @@ void ProjucerApplication::doLoginOrLogout()
|
|||
mainWindowList.getFrontmostWindow()->showLoginFormOverlay();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
licenseController->resetState();
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
|
|
|
|||
|
|
@ -87,3 +87,6 @@ enum ColourIds
|
|||
widgetBackgroundColourId = 0x2340010,
|
||||
secondaryWidgetBackgroundColourId = 0x2340011,
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
static constexpr int projucerMajorVersion = 5;
|
||||
|
|
|
|||
|
|
@ -94,7 +94,7 @@ private:
|
|||
Label configLabel { "Config Label", "Selected exporter" }, projectNameLabel;
|
||||
|
||||
ImageComponent juceIcon;
|
||||
UserAvatarComponent userAvatar { true, true };
|
||||
UserAvatarComponent userAvatar { true };
|
||||
|
||||
IconButton projectSettingsButton { "Project Settings", getIcons().settings },
|
||||
saveAndOpenInIDEButton { "Save and Open in IDE", Image() },
|
||||
|
|
|
|||
|
|
@ -245,12 +245,12 @@ private:
|
|||
};
|
||||
|
||||
//==============================================================================
|
||||
void valueTreePropertyChanged (ValueTree&, const Identifier&) override { triggerAsyncUpdate(); }
|
||||
void valueTreeChildAdded (ValueTree&, ValueTree&) override { triggerAsyncUpdate(); }
|
||||
void valueTreeChildRemoved (ValueTree&, ValueTree&, int) override { triggerAsyncUpdate(); }
|
||||
void valueTreeChildOrderChanged (ValueTree&, int, int) override { triggerAsyncUpdate(); }
|
||||
void valueTreeParentChanged (ValueTree&) override { triggerAsyncUpdate(); }
|
||||
void valueTreeRedirected (ValueTree&) override { triggerAsyncUpdate(); }
|
||||
void valueTreePropertyChanged (ValueTree&, const Identifier&) override { messagesChanged(); }
|
||||
void valueTreeChildAdded (ValueTree&, ValueTree&) override { messagesChanged(); }
|
||||
void valueTreeChildRemoved (ValueTree&, ValueTree&, int) override { messagesChanged(); }
|
||||
void valueTreeChildOrderChanged (ValueTree&, int, int) override { messagesChanged(); }
|
||||
void valueTreeParentChanged (ValueTree&) override { messagesChanged(); }
|
||||
void valueTreeRedirected (ValueTree&) override { messagesChanged(); }
|
||||
|
||||
void handleAsyncUpdate() override
|
||||
{
|
||||
|
|
|
|||
|
|
@ -20,18 +20,18 @@
|
|||
|
||||
#include "../../Application/jucer_Application.h"
|
||||
|
||||
//==============================================================================
|
||||
class UserAvatarComponent : public Component,
|
||||
public SettableTooltipClient,
|
||||
public ChangeBroadcaster,
|
||||
private LicenseController::LicenseStateListener
|
||||
{
|
||||
public:
|
||||
UserAvatarComponent (bool tooltip, bool signIn)
|
||||
: displayTooltip (tooltip),
|
||||
signInOnClick (signIn)
|
||||
UserAvatarComponent (bool isInteractive)
|
||||
: interactive (isInteractive)
|
||||
{
|
||||
ProjucerApplication::getApp().getLicenseController().addListener (this);
|
||||
licenseStateChanged();
|
||||
lookAndFeelChanged();
|
||||
}
|
||||
|
||||
~UserAvatarComponent() override
|
||||
|
|
@ -53,12 +53,12 @@ public:
|
|||
g.reduceClipRegion (ellipse);
|
||||
}
|
||||
|
||||
g.drawImage (userAvatarImage, bounds.toFloat(), RectanglePlacement::fillDestination);
|
||||
g.drawImage (currentAvatar, bounds.toFloat(), RectanglePlacement::fillDestination);
|
||||
}
|
||||
|
||||
void mouseUp (const MouseEvent&) override
|
||||
{
|
||||
if (signInOnClick)
|
||||
if (interactive)
|
||||
{
|
||||
PopupMenu menu;
|
||||
menu.addCommandItem (ProjucerApplication::getApp().commandManager.get(), CommandIDs::loginLogout);
|
||||
|
|
@ -70,7 +70,25 @@ public:
|
|||
bool isDisplaingGPLLogo() const noexcept { return isGPL; }
|
||||
|
||||
private:
|
||||
Image createDefaultAvatarImage()
|
||||
//==============================================================================
|
||||
static Image createGPLAvatarImage()
|
||||
{
|
||||
if (auto logo = Drawable::createFromImageData (BinaryData::gpl_logo_svg, BinaryData::gpl_logo_svgSize))
|
||||
{
|
||||
auto bounds = logo->getDrawableBounds();
|
||||
|
||||
Image image (Image::ARGB, roundToInt (bounds.getWidth()), roundToInt (bounds.getHeight()), true);
|
||||
Graphics g (image);
|
||||
logo->draw (g, 1.0f);
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
jassertfalse;
|
||||
return {};
|
||||
}
|
||||
|
||||
Image createStandardAvatarImage()
|
||||
{
|
||||
Image image (Image::ARGB, 250, 250, true);
|
||||
Graphics g (image);
|
||||
|
|
@ -87,17 +105,18 @@ private:
|
|||
return image;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
void licenseStateChanged() override
|
||||
{
|
||||
auto state = ProjucerApplication::getApp().getLicenseController().getCurrentState();
|
||||
|
||||
isGPL = ProjucerApplication::getApp().getLicenseController().getCurrentState().isGPL();
|
||||
|
||||
if (displayTooltip)
|
||||
if (interactive)
|
||||
{
|
||||
auto formattedUserString = [state]() -> String
|
||||
{
|
||||
if (state.isValid())
|
||||
if (state.isSignedIn())
|
||||
return (state.isGPL() ? "" : (state.username + " - ")) + state.getLicenseTypeString();
|
||||
|
||||
return "Not logged in";
|
||||
|
|
@ -106,18 +125,26 @@ private:
|
|||
setTooltip (formattedUserString);
|
||||
}
|
||||
|
||||
userAvatarImage = state.isValid() && state.avatar.isValid() ? state.avatar : defaultAvatarImage;
|
||||
currentAvatar = isGPL ? gplAvatarImage
|
||||
: state.isSignedIn() ? standardAvatarImage : signedOutAvatarImage;
|
||||
|
||||
repaint();
|
||||
sendChangeMessage();
|
||||
}
|
||||
|
||||
void lookAndFeelChanged() override
|
||||
{
|
||||
defaultAvatarImage = createDefaultAvatarImage();
|
||||
standardAvatarImage = createStandardAvatarImage();
|
||||
signedOutAvatarImage = createStandardAvatarImage();
|
||||
|
||||
if (interactive)
|
||||
signedOutAvatarImage.multiplyAllAlphas (0.4f);
|
||||
|
||||
licenseStateChanged();
|
||||
repaint();
|
||||
}
|
||||
|
||||
Image userAvatarImage, defaultAvatarImage { createDefaultAvatarImage() };
|
||||
bool isGPL = false, displayTooltip = false, signInOnClick = false;
|
||||
//==============================================================================
|
||||
Image standardAvatarImage, signedOutAvatarImage, gplAvatarImage { createGPLAvatarImage() }, currentAvatar;
|
||||
bool isGPL = false, interactive = false;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -735,7 +735,7 @@ bool Project::hasIncompatibleLicenseTypeAndSplashScreenSetting() const
|
|||
|| companyName == "ROLI Ltd.");
|
||||
|
||||
return ! ProjucerApplication::getApp().isRunningCommandLine && ! isJUCEProject && ! shouldDisplaySplashScreen()
|
||||
&& ! ProjucerApplication::getApp().getLicenseController().getCurrentState().isPaidOrGPL();
|
||||
&& ! ProjucerApplication::getApp().getLicenseController().getCurrentState().canUnlockFullFeatures();
|
||||
}
|
||||
|
||||
bool Project::isSaveAndExportDisabled() const
|
||||
|
|
@ -747,9 +747,15 @@ void Project::updateLicenseWarning()
|
|||
{
|
||||
if (hasIncompatibleLicenseTypeAndSplashScreenSetting())
|
||||
{
|
||||
ProjectMessages::MessageAction action;
|
||||
|
||||
if (ProjucerApplication::getApp().getLicenseController().getCurrentState().isOldLicense())
|
||||
action = { "Upgrade", [] { URL ("https://juce.com/get-juce").launchInDefaultBrowser(); } };
|
||||
else
|
||||
action = { "Sign in", [this] { ProjucerApplication::getApp().mainWindowList.getMainWindowForFile (getFile())->showLoginFormOverlay(); } };
|
||||
|
||||
addProjectMessage (ProjectMessages::Ids::incompatibleLicense,
|
||||
{ { "Sign in", [this] { ProjucerApplication::getApp().mainWindowList.getMainWindowForFile (getFile())->showLoginFormOverlay(); } },
|
||||
{ "Enable splash screen", [this] { displaySplashScreenValue = true; } } });
|
||||
{ std::move (action), { "Enable splash screen", [this] { displaySplashScreenValue = true; } } });
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue