/* ============================================================================== 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. ============================================================================== */ //============================================================================== class VoicePurchases : private InAppPurchases::Listener { public: //============================================================================== struct VoiceProduct { const char* identifier; const char* humanReadable; bool isPurchased, priceIsKnown, purchaseInProgress; String purchasePrice; }; //============================================================================== VoicePurchases (AsyncUpdater& asyncUpdater) : guiUpdater (asyncUpdater) { voiceProducts = Array( {VoiceProduct {"robot", "Robot", true, true, false, "Free" }, VoiceProduct {"jules", "Jules", false, false, false, "Retrieving price..." }, VoiceProduct {"fabian", "Fabian", false, false, false, "Retrieving price..." }, VoiceProduct {"ed", "Ed", false, false, false, "Retrieving price..." }, VoiceProduct {"lukasz", "Lukasz", false, false, false, "Retrieving price..." }, VoiceProduct {"jb", "JB", false, false, false, "Retrieving price..." } }); } ~VoicePurchases() { inAppPurchases.removeListener (this); } //============================================================================== VoiceProduct getPurchase (int voiceIndex) { if (! havePurchasesBeenRestored) { havePurchasesBeenRestored = true; inAppPurchases.addListener (this); inAppPurchases.restoreProductsBoughtList (true); } return voiceProducts[voiceIndex]; } void purchaseVoice (int voiceIndex) { if (havePricesBeenFetched && isPositiveAndBelow (voiceIndex, voiceProducts.size())) { auto& product = voiceProducts.getReference (voiceIndex); if (! product.isPurchased) { product.purchaseInProgress = true; inAppPurchases.purchaseProduct (product.identifier, false); } } } StringArray getVoiceNames() const { StringArray names; for (auto& voiceProduct : voiceProducts) names.add (voiceProduct.humanReadable); return names; } private: //============================================================================== void productsInfoReturned (const Array& products) override { if (! inAppPurchases.isInAppPurchasesSupported()) { for (auto idx = 1; idx < voiceProducts.size(); ++idx) { auto& voiceProduct = voiceProducts.getReference (idx); voiceProduct.isPurchased = false; voiceProduct.priceIsKnown = false; voiceProduct.purchasePrice = "In-App purcahses unavailable"; } AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon, "In-app purchase is unavailable!", "In-App purchases are not available. This either means you are trying " "to use IAP on a platform that does not support IAP or you haven't setup " "your app correctly to work with IAP.", "OK"); } else { for (auto product : products) { auto idx = findVoiceIndexFromIdentifier (product.identifier); if (isPositiveAndBelow (idx, voiceProducts.size())) { auto& voiceProduct = voiceProducts.getReference (idx); voiceProduct.priceIsKnown = true; voiceProduct.purchasePrice = product.price; } } AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon, "Your credit card will be charged!", "You are running the sample code for JUCE In-App purchases. " "Although this is only sample code, it will still CHARGE YOUR CREDIT CARD!", "Understood!"); } guiUpdater.triggerAsyncUpdate(); } void productPurchaseFinished (const PurchaseInfo& info, bool success, const String&) override { auto idx = findVoiceIndexFromIdentifier (info.purchase.productId); if (isPositiveAndBelow (idx, voiceProducts.size())) { auto& voiceProduct = voiceProducts.getReference (idx); voiceProduct.isPurchased = success; voiceProduct.purchaseInProgress = false; guiUpdater.triggerAsyncUpdate(); } } void purchasesListRestored (const Array& infos, bool success, const String&) override { if (success) { for (auto& info : infos) { auto idx = findVoiceIndexFromIdentifier (info.purchase.productId); if (isPositiveAndBelow (idx, voiceProducts.size())) { auto& voiceProduct = voiceProducts.getReference (idx); voiceProduct.isPurchased = true; } } guiUpdater.triggerAsyncUpdate(); } if (! havePricesBeenFetched) { havePricesBeenFetched = true; StringArray identifiers; for (auto& voiceProduct : voiceProducts) identifiers.add (voiceProduct.identifier); inAppPurchases.getProductsInformation(identifiers); } } //============================================================================== int findVoiceIndexFromIdentifier (String identifier) const { identifier = identifier.toLowerCase(); for (auto i = 0; i < voiceProducts.size(); ++i) if (String (voiceProducts.getReference (i).identifier) == identifier) return i; return -1; } //============================================================================== AsyncUpdater& guiUpdater; bool havePurchasesBeenRestored = false, havePricesBeenFetched = false; InAppPurchases inAppPurchases; Array voiceProducts; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (VoicePurchases) };