The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

224 lines
8.1KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2017 - ROLI Ltd.
  5. JUCE is an open source library subject to commercial or open-source
  6. licensing.
  7. By using JUCE, you agree to the terms of both the JUCE 5 End-User License
  8. Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
  9. 27th April 2017).
  10. End User License Agreement: www.juce.com/juce-5-licence
  11. Privacy Policy: www.juce.com/juce-5-privacy-policy
  12. Or: You may also use this code under the terms of the GPL v3 (see
  13. www.gnu.org/licenses).
  14. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  15. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  16. DISCLAIMED.
  17. ==============================================================================
  18. */
  19. //==============================================================================
  20. class VoicePurchases : private InAppPurchases::Listener
  21. {
  22. public:
  23. //==============================================================================
  24. struct VoiceProduct
  25. {
  26. const char* identifier;
  27. const char* humanReadable;
  28. bool isPurchased, priceIsKnown, purchaseInProgress;
  29. String purchasePrice;
  30. };
  31. //==============================================================================
  32. VoicePurchases (AsyncUpdater& asyncUpdater)
  33. : guiUpdater (asyncUpdater)
  34. {
  35. voiceProducts = Array<VoiceProduct>(
  36. {VoiceProduct {"robot", "Robot", true, true, false, "Free" },
  37. VoiceProduct {"jules", "Jules", false, false, false, "Retrieving price..." },
  38. VoiceProduct {"fabian", "Fabian", false, false, false, "Retrieving price..." },
  39. VoiceProduct {"ed", "Ed", false, false, false, "Retrieving price..." },
  40. VoiceProduct {"lukasz", "Lukasz", false, false, false, "Retrieving price..." },
  41. VoiceProduct {"jb", "JB", false, false, false, "Retrieving price..." } });
  42. }
  43. ~VoicePurchases()
  44. {
  45. InAppPurchases::getInstance()->removeListener (this);
  46. }
  47. //==============================================================================
  48. VoiceProduct getPurchase (int voiceIndex)
  49. {
  50. if (! havePurchasesBeenRestored)
  51. {
  52. havePurchasesBeenRestored = true;
  53. InAppPurchases::getInstance()->addListener (this);
  54. InAppPurchases::getInstance()->restoreProductsBoughtList (true);
  55. }
  56. return voiceProducts[voiceIndex];
  57. }
  58. void purchaseVoice (int voiceIndex)
  59. {
  60. if (havePricesBeenFetched && isPositiveAndBelow (voiceIndex, voiceProducts.size()))
  61. {
  62. auto& product = voiceProducts.getReference (voiceIndex);
  63. if (! product.isPurchased)
  64. {
  65. purchaseInProgress = true;
  66. product.purchaseInProgress = true;
  67. InAppPurchases::getInstance()->purchaseProduct (product.identifier, false);
  68. guiUpdater.triggerAsyncUpdate();
  69. }
  70. }
  71. }
  72. StringArray getVoiceNames() const
  73. {
  74. StringArray names;
  75. for (auto& voiceProduct : voiceProducts)
  76. names.add (voiceProduct.humanReadable);
  77. return names;
  78. }
  79. bool isPurchaseInProgress() const noexcept { return purchaseInProgress; }
  80. private:
  81. //==============================================================================
  82. void productsInfoReturned (const Array<InAppPurchases::Product>& products) override
  83. {
  84. if (! InAppPurchases::getInstance()->isInAppPurchasesSupported())
  85. {
  86. for (auto idx = 1; idx < voiceProducts.size(); ++idx)
  87. {
  88. auto& voiceProduct = voiceProducts.getReference (idx);
  89. voiceProduct.isPurchased = false;
  90. voiceProduct.priceIsKnown = false;
  91. voiceProduct.purchasePrice = "In-App purcahses unavailable";
  92. }
  93. AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon,
  94. "In-app purchase is unavailable!",
  95. "In-App purchases are not available. This either means you are trying "
  96. "to use IAP on a platform that does not support IAP or you haven't setup "
  97. "your app correctly to work with IAP.",
  98. "OK");
  99. }
  100. else
  101. {
  102. for (auto product : products)
  103. {
  104. auto idx = findVoiceIndexFromIdentifier (product.identifier);
  105. if (isPositiveAndBelow (idx, voiceProducts.size()))
  106. {
  107. auto& voiceProduct = voiceProducts.getReference (idx);
  108. voiceProduct.priceIsKnown = true;
  109. voiceProduct.purchasePrice = product.price;
  110. }
  111. }
  112. AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon,
  113. "Your credit card will be charged!",
  114. "You are running the sample code for JUCE In-App purchases. "
  115. "Although this is only sample code, it will still CHARGE YOUR CREDIT CARD!",
  116. "Understood!");
  117. }
  118. guiUpdater.triggerAsyncUpdate();
  119. }
  120. void productPurchaseFinished (const PurchaseInfo& info, bool success, const String&) override
  121. {
  122. purchaseInProgress = false;
  123. auto idx = findVoiceIndexFromIdentifier (info.purchase.productId);
  124. if (isPositiveAndBelow (idx, voiceProducts.size()))
  125. {
  126. auto& voiceProduct = voiceProducts.getReference (idx);
  127. voiceProduct.isPurchased = success;
  128. voiceProduct.purchaseInProgress = false;
  129. guiUpdater.triggerAsyncUpdate();
  130. }
  131. else
  132. {
  133. // On failure Play Store will not tell us which purchase failed
  134. for (auto& voiceProduct : voiceProducts)
  135. voiceProduct.purchaseInProgress = false;
  136. guiUpdater.triggerAsyncUpdate();
  137. }
  138. }
  139. void purchasesListRestored (const Array<PurchaseInfo>& infos, bool success, const String&) override
  140. {
  141. if (success)
  142. {
  143. for (auto& info : infos)
  144. {
  145. auto idx = findVoiceIndexFromIdentifier (info.purchase.productId);
  146. if (isPositiveAndBelow (idx, voiceProducts.size()))
  147. {
  148. auto& voiceProduct = voiceProducts.getReference (idx);
  149. voiceProduct.isPurchased = true;
  150. }
  151. }
  152. guiUpdater.triggerAsyncUpdate();
  153. }
  154. if (! havePricesBeenFetched)
  155. {
  156. havePricesBeenFetched = true;
  157. StringArray identifiers;
  158. for (auto& voiceProduct : voiceProducts)
  159. identifiers.add (voiceProduct.identifier);
  160. InAppPurchases::getInstance()->getProductsInformation (identifiers);
  161. }
  162. }
  163. //==============================================================================
  164. int findVoiceIndexFromIdentifier (String identifier) const
  165. {
  166. identifier = identifier.toLowerCase();
  167. for (auto i = 0; i < voiceProducts.size(); ++i)
  168. if (String (voiceProducts.getReference (i).identifier) == identifier)
  169. return i;
  170. return -1;
  171. }
  172. //==============================================================================
  173. AsyncUpdater& guiUpdater;
  174. bool havePurchasesBeenRestored = false, havePricesBeenFetched = false, purchaseInProgress = false;
  175. Array<VoiceProduct> voiceProducts;
  176. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (VoicePurchases)
  177. };